-
Notifications
You must be signed in to change notification settings - Fork 84
Fix typed proxy access to generic skeleton event storage #394
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
4bede76
4630e3e
c683d5b
5c90167
0acf5d3
780572e
2c1ab48
554bb12
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,7 +14,11 @@ | |
| #define SCORE_MW_COM_IMPL_BINDINGS_LOLA_PROXY_EVENT_H | ||
|
|
||
| #include "score/mw/com/impl/bindings/lola/event_data_storage.h" | ||
| #include "score/mw/com/impl/bindings/lola/event_meta_info.h" | ||
| #include "score/mw/com/impl/bindings/lola/proxy_event_common.h" | ||
|
|
||
| #include "score/language/safecpp/safe_math/safe_math.h" | ||
| #include "score/memory/shared/pointer_arithmetic_util.h" | ||
| #include "score/mw/com/impl/proxy_event_binding.h" | ||
| #include "score/mw/com/impl/sample_reference_tracker.h" | ||
| #include "score/mw/com/impl/subscription_state.h" | ||
|
|
@@ -26,6 +30,7 @@ | |
| #include <score/assert.hpp> | ||
| #include <score/optional.hpp> | ||
|
|
||
| #include <cstdint> | ||
| #include <exception> | ||
| #include <iostream> | ||
| #include <limits> | ||
|
|
@@ -64,7 +69,9 @@ class ProxyEvent final : public ProxyEventBinding<SampleType> | |
| ProxyEvent(Proxy& parent, const ElementFqId element_fq_id, const std::string_view event_name) | ||
| : ProxyEventBinding<SampleType>{}, | ||
| proxy_event_common_{parent, element_fq_id, event_name}, | ||
| samples_{parent.GetEventDataStorage<SampleType>(element_fq_id)} | ||
| meta_info_{parent.GetEventMetaInfo(element_fq_id)}, | ||
| aligned_sample_size_{memory::shared::CalculateAlignedSize(sizeof(SampleType), alignof(SampleType))}, | ||
| event_slots_raw_array_{InitialiseEventSlotsRawArray()} | ||
| { | ||
| } | ||
|
|
||
|
|
@@ -130,13 +137,36 @@ class ProxyEvent final : public ProxyEventBinding<SampleType> | |
| }; | ||
|
|
||
| private: | ||
| const std::uint8_t* InitialiseEventSlotsRawArray(); | ||
|
|
||
| Result<std::size_t> GetNewSamplesImpl(Callback&& receiver, TrackerGuardFactory& tracker) noexcept; | ||
| Result<std::size_t> GetNumNewSamplesAvailableImpl() const noexcept; | ||
|
|
||
| ProxyEventCommon proxy_event_common_; | ||
| const EventDataStorage<SampleType>& samples_; | ||
| const EventMetaInfo& meta_info_; | ||
| const std::size_t aligned_sample_size_; | ||
| const std::uint8_t* event_slots_raw_array_; | ||
| }; | ||
|
|
||
| template <typename SampleType> | ||
| inline const std::uint8_t* ProxyEvent<SampleType>::InitialiseEventSlotsRawArray() | ||
| { | ||
| auto& event_data_control_local = proxy_event_common_.GetConsumerEventDataControlLocal(); | ||
|
|
||
| const auto event_slots_raw_array_size = safe_math::Multiply<safe_math::ReturnMode::kAbortOnError>( | ||
| aligned_sample_size_, event_data_control_local.GetMaxSampleSlots()); | ||
|
|
||
| const void* const event_slots_raw_array = meta_info_.event_slots_raw_array_.get(event_slots_raw_array_size); | ||
|
|
||
| SCORE_LANGUAGE_FUTURECPP_PRECONDITION_PRD_MESSAGE(nullptr != event_slots_raw_array, "Null event slot array"); | ||
| SCORE_LANGUAGE_FUTURECPP_PRECONDITION_PRD_MESSAGE(meta_info_.data_type_info_.size == sizeof(SampleType), | ||
| "Event sample size mismatch"); | ||
| SCORE_LANGUAGE_FUTURECPP_PRECONDITION_PRD_MESSAGE(meta_info_.data_type_info_.alignment == alignof(SampleType), | ||
| "Event sample alignment mismatch"); | ||
|
|
||
| return static_cast<const std::uint8_t*>(event_slots_raw_array); | ||
| } | ||
|
|
||
| template <typename SampleType> | ||
| inline Result<std::size_t> ProxyEvent<SampleType>::GetNumNewSamplesAvailable() const noexcept | ||
| { | ||
|
|
@@ -191,10 +221,26 @@ inline Result<std::size_t> ProxyEvent<SampleType>::GetNewSamplesImpl(Callback&& | |
| const auto slot_indices = proxy_event_common_.GetNewSamplesSlotIndices(max_sample_count); | ||
|
|
||
| auto& event_data_control_local = proxy_event_common_.GetConsumerEventDataControlLocal(); | ||
| SCORE_LANGUAGE_FUTURECPP_PRECONDITION_PRD_MESSAGE(nullptr != event_slots_raw_array_, "Null event slot array"); | ||
|
|
||
| for (auto slot_index_it = slot_indices.begin; slot_index_it != slot_indices.end; ++slot_index_it) | ||
| { | ||
| const SampleType& sample_data{samples_.at(static_cast<std::size_t>(*slot_index_it))}; | ||
| // TODO: Replace this temporary raw-slot access when the LoLa binding layer is type-erased. | ||
| // The current fix avoids interpreting GenericSkeleton-created storage as EventDataStorage<SampleType>, since | ||
| // the DynamicArray element count may not match the typed proxy sample type. | ||
| // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) The pointer event_slots_raw_array_ points to | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add a todo here stating that this is just a temporary fix since the size of EventDataStorage may incorrect and the long term solution will be to type erase the binding layer.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. I added a TODO near the raw-slot access in ProxyEvent. The comment now states that this is a temporary compatibility fix and that the long-term solution is to type-erase the LoLa binding layer, because the EventDataStorage DynamicArray element count may not match the typed proxy sample type. |
||
| // the first byte of the type-erased event sample storage in shared memory. Samples may originate from either a | ||
| // typed SkeletonEvent or a GenericSkeletonEvent, therefore slot lookup must use the stable EventMetaInfo raw | ||
| // storage address and SampleType stride instead of interpreting the shared-memory DynamicArray object type. | ||
| const auto* const object_start_address = &event_slots_raw_array_[aligned_sample_size_ * (*slot_index_it)]; | ||
| // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) | ||
|
|
||
| // Suppress "AUTOSAR C++14 M5-2-8" rule finding: "An object with integer type or pointer to void type shall | ||
| // not be converted to an object with pointer type.". | ||
| // The raw storage address is provided through EventMetaInfo. The regular typed proxy validates the expected | ||
| // type at construction time and calculates the slot offset with sizeof(SampleType)/alignof(SampleType). | ||
| // coverity[autosar_cpp14_m5_2_8_violation] | ||
| const SampleType& sample_data{*reinterpret_cast<const SampleType*>(object_start_address)}; | ||
| const EventSlotStatus event_slot_status{event_data_control_local[*slot_index_it]}; | ||
| const EventSlotStatus::EventTimeStamp sample_timestamp{event_slot_status.GetTimeStamp()}; | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -703,18 +703,53 @@ TYPED_TEST(LolaProxyEventDeathTest, FailOnEventNotFound) | |
| } | ||
|
|
||
| using LoLaTypedProxyEventTestFixture = LolaProxyEventFixture<ProxyEventStruct>; | ||
|
|
||
| TEST_F(LoLaTypedProxyEventTestFixture, GetNewSamplesReturnsTypedSampleFromProviderStorage) | ||
| { | ||
| // Given a typed LoLa ProxyEvent that is subscribed to a provider event containing one sample | ||
| const std::size_t max_sample_count_subscription{5U}; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All our tests should follow the given, when, then structure (e.g. https://martinfowler.com/bliki/GivenWhenThen.html)
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. I reworked the added regression test to follow the Given / When / Then structure explicitly. The test now separates:
|
||
| this->GivenAProxyEvent(this->element_fq_id_, this->event_name_) | ||
| .ThatIsSubscribedWithMaxSamples(max_sample_count_subscription) | ||
| .WithSkeletonEventData({{kDummySampleValue, kDummyInputTimestamp}}); | ||
|
|
||
| // When GetNewSamples is called | ||
| const std::size_t max_samples{1U}; | ||
| TestSampleType received_sample{0U}; | ||
| const auto receiver = [&received_sample](impl::SamplePtr<TestSampleType> sample, | ||
| const tracing::ITracingRuntime::TracePointDataId) { | ||
| received_sample = *sample; | ||
| }; | ||
|
|
||
| const auto num_callbacks_result = this->GetNewSamples(receiver, max_samples); | ||
|
|
||
| // Then one sample is returned to the typed proxy with the data written by the provider | ||
| ASSERT_TRUE(num_callbacks_result.has_value()); | ||
| EXPECT_EQ(num_callbacks_result.value(), 1U); | ||
| EXPECT_EQ(received_sample, kDummySampleValue); | ||
| } | ||
|
|
||
| TEST_F(LoLaTypedProxyEventTestFixture, SampleConstness) | ||
| { | ||
| RecordProperty("Verifies", "SCR-6340729"); | ||
| RecordProperty("Description", "Proxy shall interpret slot data as const"); | ||
| RecordProperty("TestType", "Requirements-based test"); | ||
| RecordProperty("DerivationTechnique", "Analysis of requirements"); | ||
|
|
||
| using SamplePtrDataType = std::remove_pointer_t<decltype(std::declval<impl::SamplePtr<TestSampleType>>().get())>; | ||
| static_assert(std::is_const<SamplePtrDataType>::value, "Proxy should expose const sample data."); | ||
| } | ||
|
|
||
| TEST_F(LoLaTypedProxyEventTestFixture, HoldsEventMetaInfoAsConstReference) | ||
| { | ||
| // Given a typed LoLa ProxyEvent | ||
| this->GivenAProxyEvent(this->element_fq_id_, this->event_name_); | ||
|
|
||
| // When accessing the proxy event through the test attorney | ||
| ProxyEventAttorney<TestSampleType> proxy_event_attorney{*test_proxy_event_}; | ||
| using SamplesMemberType = typename std::remove_reference<decltype(proxy_event_attorney.GetSamplesMember())>::type; | ||
| static_assert(std::is_const<SamplesMemberType>::value, "Proxy should hold const slot data."); | ||
|
|
||
| // Then the proxy stores EventMetaInfo as const data | ||
| using MetaInfoMemberType = typename std::remove_reference<decltype(proxy_event_attorney.GetMetaInfoMember())>::type; | ||
| static_assert(std::is_const<MetaInfoMemberType>::value, "Proxy should hold const event meta info."); | ||
| } | ||
|
|
||
| } // namespace | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. I rebased the branch locally onto latest The final commit message is now descriptive and concise:
It includes a short summary explaining that typed |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,6 +13,7 @@ | |
| #ifndef SCORE_MW_COM_IMPL_BINDINGS_LOLA_TEST_PROXY_EVENT_TEST_RESOURCES_H | ||
| #define SCORE_MW_COM_IMPL_BINDINGS_LOLA_TEST_PROXY_EVENT_TEST_RESOURCES_H | ||
|
|
||
| #include "score/mw/com/impl/bindings/lola/event_data_storage.h" | ||
| #include "score/mw/com/impl/bindings/lola/event_subscription_control.h" | ||
| #include "score/mw/com/impl/bindings/lola/generic_proxy_event.h" | ||
| #include "score/mw/com/impl/bindings/lola/i_runtime.h" | ||
|
|
@@ -94,9 +95,9 @@ class ProxyEventAttorney | |
|
|
||
| ProxyEventAttorney(ProxyEvent<T>& proxy_event) noexcept : proxy_event_{proxy_event} {} | ||
|
|
||
| auto& GetSamplesMember() | ||
| auto& GetMetaInfoMember() | ||
| { | ||
| return proxy_event_.samples_; | ||
| return proxy_event_.meta_info_; | ||
| } | ||
|
|
||
| private: | ||
|
|
@@ -215,6 +216,7 @@ class ProxyMockedMemoryFixture : public ::testing::Test | |
| std::optional<ProviderEventDataControlLocalView<>> provider_event_data_control_local_{}; | ||
| std::optional<ConsumerEventDataControlLocalView<>> consumer_event_data_control_local_{}; | ||
| EventDataStorage<SampleType>* event_data_storage_{nullptr}; | ||
| void* event_slots_raw_array_{nullptr}; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We shouldn't remove event_data_storage_. We should rather emulate the production code in which event_slots_raw_array_ is a type erased view into the event_data_storage_
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. I restored The fixture now emulates the production layout more closely: So the provider/test setup still behaves like typed skeleton storage, while the updated |
||
| RollbackSynchronization rollback_synchronization_{}; | ||
|
|
||
| std::shared_ptr<MessagePassingServiceMock> mock_service_{std::make_shared<MessagePassingServiceMock>()}; | ||
|
|
||

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These should also be added as deps in the build file if they're not already there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Checked. No BUILD file change was required.
The existing LoLa proxy target already has the relevant dependencies available for safe_math and pointer_arithmetic_util, so the PR builds successfully without adding new BUILD deps.