From c743b6f4437d294aab09c8be4d98115ce618969f Mon Sep 17 00:00:00 2001 From: manuelfehlhammer Date: Sun, 16 Aug 2026 01:10:08 +0200 Subject: [PATCH] impl/lola: type-erased EventDataStorage Changed the previously typed EventDataStorage now to a type-erased storage. --- score/mw/com/impl/BUILD | 1 + score/mw/com/impl/bindings/lola/BUILD | 11 +- .../impl/bindings/lola/event_data_storage.cpp | 76 ++++++++ .../impl/bindings/lola/event_data_storage.h | 42 ++++- .../bindings/lola/event_data_storage_test.cpp | 154 ++++++++++++++-- .../com/impl/bindings/lola/event_meta_info.h | 13 +- .../bindings/lola/generic_proxy_event.cpp | 47 +---- .../impl/bindings/lola/generic_proxy_event.h | 1 + .../lola/generic_proxy_event_test.cpp | 29 --- .../bindings/lola/generic_skeleton_event.cpp | 14 +- .../bindings/lola/generic_skeleton_event.h | 17 +- .../lola/generic_skeleton_event_test.cpp | 28 +-- score/mw/com/impl/bindings/lola/proxy.cpp | 23 +++ score/mw/com/impl/bindings/lola/proxy.h | 6 + score/mw/com/impl/bindings/lola/proxy_event.h | 46 +---- .../mw/com/impl/bindings/lola/proxy_test.cpp | 6 +- .../bindings/lola/service_data_storage.cpp | 11 +- .../impl/bindings/lola/service_data_storage.h | 14 +- .../lola/service_data_storage_test.cpp | 92 +++++----- score/mw/com/impl/bindings/lola/skeleton.cpp | 16 +- score/mw/com/impl/bindings/lola/skeleton.h | 82 +-------- .../com/impl/bindings/lola/skeleton_event.h | 23 +-- .../bindings/lola/skeleton_memory_manager.cpp | 147 ++++------------ .../bindings/lola/skeleton_memory_manager.h | 123 ++----------- .../com/impl/bindings/lola/skeleton_test.cpp | 166 +++++------------- .../lola/test/proxy_event_test_resources.cpp | 11 +- .../lola/test/proxy_event_test_resources.h | 3 +- .../lola/test/skeleton_component_test.cpp | 21 +-- .../test/skeleton_event_component_test.cpp | 7 +- .../lola/test/skeleton_test_resources.h | 27 ++- .../test_doubles/fake_mocked_service_data.cpp | 6 +- .../test_doubles/fake_mocked_service_data.h | 27 ++- .../lola/test_doubles/fake_service_data.cpp | 2 +- .../lola/test_doubles/fake_service_data.h | 28 ++- .../mock_binding/generic_skeleton_event.h | 4 +- .../bindings/mock_binding/skeleton_event.h | 9 +- score/mw/com/impl/generic_skeleton_event.cpp | 4 +- .../com/impl/generic_skeleton_event_binding.h | 4 +- .../com/impl/generic_skeleton_event_test.cpp | 6 +- score/mw/com/impl/skeleton_event_binding.h | 18 +- .../com/impl/skeleton_event_binding_test.cpp | 5 +- 41 files changed, 572 insertions(+), 798 deletions(-) diff --git a/score/mw/com/impl/BUILD b/score/mw/com/impl/BUILD index cec1a9177..122652422 100644 --- a/score/mw/com/impl/BUILD +++ b/score/mw/com/impl/BUILD @@ -225,6 +225,7 @@ cc_library( deps = [ ":receive_handler_registration_changed_handler", ":skeleton_event_binding", + "@score_baselibs//score/memory:data_type_size_info", "@score_baselibs//score/result", ], ) diff --git a/score/mw/com/impl/bindings/lola/BUILD b/score/mw/com/impl/bindings/lola/BUILD index 66ed7c89f..de4d67959 100644 --- a/score/mw/com/impl/bindings/lola/BUILD +++ b/score/mw/com/impl/bindings/lola/BUILD @@ -328,6 +328,9 @@ cc_library( "generic_skeleton_event.h", ], features = COMPILER_WARNING_FEATURES, + implementation_deps = [ + "@score_baselibs//score/memory:data_type_size_info", + ], tags = ["FFI"], visibility = [ "//score/mw/com/impl:__subpackages__", @@ -747,8 +750,11 @@ cc_library( "//score/mw/com/impl:__subpackages__", ], deps = [ + ":control_slot_types", + "//score/memory/shared:managed_memory_resource", + "//score/memory/shared:offset_ptr", "//score/memory/shared:types", - "@score_baselibs//score/containers:dynamic_array", + "@score_baselibs//score/memory:data_type_size_info", ], ) @@ -1271,7 +1277,8 @@ cc_unit_test( features = COMPILER_WARNING_FEATURES, deps = [ ":event_data_storage", - "@score_baselibs//score/containers:dynamic_array", + "//score/memory/shared:new_delete_delegate_resource", + "@score_baselibs//score/memory:data_type_size_info", "@score_baselibs//score/mw/log", ], ) diff --git a/score/mw/com/impl/bindings/lola/event_data_storage.cpp b/score/mw/com/impl/bindings/lola/event_data_storage.cpp index 03c186b3e..147c8647e 100644 --- a/score/mw/com/impl/bindings/lola/event_data_storage.cpp +++ b/score/mw/com/impl/bindings/lola/event_data_storage.cpp @@ -11,3 +11,79 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ #include "score/mw/com/impl/bindings/lola/event_data_storage.h" + +#include +#include + +#include + +namespace score::mw::com::impl::lola +{ + +EventDataStorage::EventDataStorage(memory::shared::ManagedMemoryResource& resource, + SlotIndexType number_of_slots, + memory::DataTypeSizeInfo event_sample_size_info) + : number_of_slots_(number_of_slots), + sample_size_info_(event_sample_size_info), + memory_resource_(resource), + type_erased_data_slots_(nullptr), + type_erased_data_slots_storage_size_(0) +{ + // Guard against an overflow when calculating the total number of bytes needed for the raw slot-array. Without + // this check, an overflowing multiplication would silently wrap around to a much smaller value than what is + // actually required, leading to an undersized allocation and, subsequently, out-of-bounds accesses when the + // slots are used (see GetTypeErasedDataSlot()). + SCORE_LANGUAGE_FUTURECPP_PRECONDITION_PRD_MESSAGE( + (event_sample_size_info.Size() == 0U) || + (number_of_slots <= (std::numeric_limits::max() / event_sample_size_info.Size())), + "Overflow while calculating the total size of the raw event-data slot-array."); + const auto storage_bytes_needed = number_of_slots * event_sample_size_info.Size(); + + // The alignment used here must match exactly the alignment CalculateServiceDataStorageShmSize() assumes for this + // allocation (see service_data_storage.cpp), i.e. event_sample_size_info.Alignment(). Using a different (e.g. + // hardcoded, stricter) alignment here would make the analytically calculated shm-size wrong (too small). + void* const type_erased_data_slots_start = + memory_resource_.allocate(storage_bytes_needed, event_sample_size_info.Alignment()); + SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD(nullptr != type_erased_data_slots_start); + type_erased_data_slots_ = static_cast(type_erased_data_slots_start); + type_erased_data_slots_storage_size_ = storage_bytes_needed; +} + +EventDataStorage::~EventDataStorage() +{ + if (type_erased_data_slots_ != nullptr) + { + memory_resource_.deallocate(type_erased_data_slots_.get(), type_erased_data_slots_storage_size_); + } +} + +void* EventDataStorage::GetTypeErasedDataSlot(SlotIndexType index, size_t data_size) const +{ + SCORE_LANGUAGE_FUTURECPP_PRECONDITION_PRD(index < number_of_slots_); + + SCORE_LANGUAGE_FUTURECPP_PRECONDITION_PRD(data_size == sample_size_info_.Size()); + const auto element_offset = data_size * index; + + // we apply the required bounds-checking: + // Verify, that the start of the type-erased storage is still within bounds + auto* const slots_start_address = type_erased_data_slots_.get(); + // Verify, that the complete slot to be accessed in the type-erased storage is still within bounds. + const auto slot_last_byte_offset_ptr = + type_erased_data_slots_ + decltype(type_erased_data_slots_)::difference_type(element_offset + data_size - 1U); + score::cpp::ignore = slot_last_byte_offset_ptr.get(); + + // In our architecture we have a one-to-one mapping between pointers and integral values. + // The preconditions above guarantee that element_address will always point inside type_erased_data_slots_. + // Therefore, casting between integers and pointers is well-defined in this case. + // NOLINTNEXTLINE(score-banned-function) see above + auto* const element_address = memory::shared::AddOffsetToPointer(slots_start_address, element_offset); + + return element_address; +} + +SlotIndexType EventDataStorage::GetNumberOfSlots() const +{ + return number_of_slots_; +} + +} // namespace score::mw::com::impl::lola diff --git a/score/mw/com/impl/bindings/lola/event_data_storage.h b/score/mw/com/impl/bindings/lola/event_data_storage.h index 146f49613..02aa0d9c5 100644 --- a/score/mw/com/impl/bindings/lola/event_data_storage.h +++ b/score/mw/com/impl/bindings/lola/event_data_storage.h @@ -13,24 +13,52 @@ #ifndef SCORE_MW_COM_IMPL_BINDINGS_LOLA_EVENT_DATA_STORAGE_H #define SCORE_MW_COM_IMPL_BINDINGS_LOLA_EVENT_DATA_STORAGE_H -#include "score/containers/dynamic_array.h" -#include "score/memory/shared/polymorphic_offset_ptr_allocator.h" +#include "score/memory/data_type_size_info.h" +#include "score/memory/shared/managed_memory_resource.h" +#include "score/memory/shared/offset_ptr.h" +#include "score/mw/com/impl/bindings/lola/control_slot_types.h" -#include +#include namespace score::mw::com::impl::lola { -/// \brief Container for storing the actual data of a LoLa Event +/// \brief Container for storing the actual data of a LoLa event (resp. field) within shared-memory. /// /// \details This container will be accessed in parallel by multiple threads. The access must be synchronized via the /// EventDataControl block. The idea is that a producer first needs to claim an event slot, then change the data within /// the storage and then mark the slot as ready (similar for a consumer). This enables us cache optimized access of /// these data structures. The overall contract will be abstracted for the end-user anyhow, so the separation into two /// classes should be no problem. -template -using EventDataStorage = score::containers:: - DynamicArray>>; +class EventDataStorage final +{ + public: + EventDataStorage(memory::shared::ManagedMemoryResource& resource, + SlotIndexType number_of_slots, + memory::DataTypeSizeInfo event_sample_size_info); + + ~EventDataStorage(); + + /// \brief Returns a pointer to the type-erased data slot at the given index. + /// \details This access also does a complete bounds-check to verify that the returned raw-pointer is within the + /// bounds as well as the end-address (returned pointer plus data_size). + /// \param data_size The size of the data slot. This is used to verify, that the callers size expectation matches + /// the size of the event data type, the EventDataStorage was constructed with. + /// @return A pointer to the type-erased data slot. + void* GetTypeErasedDataSlot(SlotIndexType index, size_t data_size) const; + + SlotIndexType GetNumberOfSlots() const; + + private: + SlotIndexType number_of_slots_; + memory::DataTypeSizeInfo sample_size_info_; + memory::shared::ManagedMemoryResource& memory_resource_; + + memory::shared::OffsetPtr type_erased_data_slots_; + + /// size of type_erased_data_slots_ storage in bytes. This is equal to number_of_slots_ * sample_size_info_.Size() + std::size_t type_erased_data_slots_storage_size_; +}; } // namespace score::mw::com::impl::lola diff --git a/score/mw/com/impl/bindings/lola/event_data_storage_test.cpp b/score/mw/com/impl/bindings/lola/event_data_storage_test.cpp index 75bf19103..10fa67043 100644 --- a/score/mw/com/impl/bindings/lola/event_data_storage_test.cpp +++ b/score/mw/com/impl/bindings/lola/event_data_storage_test.cpp @@ -12,28 +12,156 @@ ********************************************************************************/ #include "score/mw/com/impl/bindings/lola/event_data_storage.h" -#include "score/containers/dynamic_array.h" +#include "score/memory/data_type_size_info.h" +#include "score/memory/shared/new_delete_delegate_resource.h" + +#include #include -#include + +#include +#include +#include +#include +#include namespace score::mw::com::impl::lola { namespace { -TEST(EventDataStorageTest, EventDataStorageUsesADynamicArrayToRepresentSlots) +const std::uint64_t kMemoryResourceId{42U}; +constexpr SlotIndexType kNumberOfSlots{4U}; + +/// \brief A trivial dummy type that requires std::max_align_t alignment. +/// \details Used (in addition to plain integral types) as one of the sample types EventDataStorage is typed-tested +/// with, to make sure EventDataStorage also correctly handles the "worst case" alignment requirement. +struct MaxAlignedDummyStruct +{ + std::byte byte_member; + std::max_align_t max_align_member; +}; + +/// \brief Fills every byte of value with pattern, so that two values created with a differing pattern are guaranteed +/// to compare unequal (see BytesEqual()), regardless of TypeParam's actual member layout. +template +T MakeValue(const std::uint8_t pattern) +{ + T value{}; + std::memset(&value, pattern, sizeof(T)); + return value; +} + +/// \brief Compares lhs and rhs byte-by-byte. +/// \details We cannot rely on operator== being defined for every TypeParam (e.g. MaxAlignedDummyStruct doesn't define +/// one), so we compare the raw bytes instead. +template +bool BytesEqual(const T& lhs, const T& rhs) +{ + return std::memcmp(&lhs, &rhs, sizeof(T)) == 0; +} + +/// \brief Templated test fixture that constructs a real EventDataStorage for TypeParam, sized/aligned according to +/// TypeParam's actual memory::DataTypeSizeInfo. +template +class EventDataStorageTypedTest : public ::testing::Test +{ + protected: + memory::shared::NewDeleteDelegateMemoryResource memory_resource_{kMemoryResourceId}; + memory::DataTypeSizeInfo sample_size_info_{sizeof(T), alignof(T)}; + EventDataStorage unit_{memory_resource_, kNumberOfSlots, sample_size_info_}; +}; + +using SampleTypes = ::testing::Types; +TYPED_TEST_SUITE(EventDataStorageTypedTest, SampleTypes, ); + +TYPED_TEST(EventDataStorageTypedTest, GetTypeErasedDataSlotReturnsCorrectlyAlignedAndWritableSlotForEveryIndex) +{ + // Given an EventDataStorage constructed for TypeParam (see fixture) + + for (SlotIndexType slot_index = 0U; slot_index < kNumberOfSlots; ++slot_index) + { + // When retrieving the type-erased pointer to the data slot at slot_index + void* const type_erased_slot = this->unit_.GetTypeErasedDataSlot(slot_index, sizeof(TypeParam)); + + // Then the returned pointer is non-null and correctly aligned for TypeParam + ASSERT_NE(type_erased_slot, nullptr); + EXPECT_EQ(reinterpret_cast(type_erased_slot) % alignof(TypeParam), 0U); + + // and casting it to a TypeParam* and writing/reading a value through it works without crashing (e.g. due to + // an alignment violation) and yields back the very same value that was written. + auto* const typed_slot = static_cast(type_erased_slot); + const TypeParam value_to_write = MakeValue(static_cast(slot_index + 1U)); + *typed_slot = value_to_write; + + EXPECT_TRUE(BytesEqual(*typed_slot, value_to_write)); + } +} + +TYPED_TEST(EventDataStorageTypedTest, DataSlotsOfDifferentIndicesDoNotOverlap) +{ + // Given an EventDataStorage constructed for TypeParam (see fixture) + + // When writing a distinct typed-value into every one of its data slots + std::vector written_values{}; + for (SlotIndexType slot_index = 0U; slot_index < kNumberOfSlots; ++slot_index) + { + const TypeParam value = MakeValue(static_cast(slot_index + 1U)); + written_values.push_back(value); + + auto* const typed_slot = + static_cast(this->unit_.GetTypeErasedDataSlot(slot_index, sizeof(TypeParam))); + *typed_slot = value; + } + + // Then every data slot still contains the very same distinct value that was written to it, i.e. writing to one + // slot did not corrupt/overlap the contents of another slot. + for (SlotIndexType slot_index = 0U; slot_index < kNumberOfSlots; ++slot_index) + { + auto* const typed_slot = + static_cast(this->unit_.GetTypeErasedDataSlot(slot_index, sizeof(TypeParam))); + EXPECT_TRUE(BytesEqual(*typed_slot, written_values[slot_index])); + } +} + +TEST(EventDataStorageDeathTest, GetTypeErasedDataSlotTerminatesOnDataSizeMismatch) +{ + // Given an EventDataStorage constructed for a std::uint32_t sample type + memory::shared::NewDeleteDelegateMemoryResource memory_resource{kMemoryResourceId}; + const memory::DataTypeSizeInfo sample_size_info{sizeof(std::uint32_t), alignof(std::uint32_t)}; + EventDataStorage unit{memory_resource, kNumberOfSlots, sample_size_info}; + + // When requesting a data slot with a data_size that does not match the sample type's actual size + // Then the program terminates, since the caller's size expectation doesn't match the storage's sample size. + EXPECT_DEATH(unit.GetTypeErasedDataSlot(0U, sizeof(std::uint32_t) + 1U), ".*"); +} + +TEST(EventDataStorageDeathTest, GetTypeErasedDataSlotTerminatesOnOutOfBoundsIndex) { - // Our detailed design (aas/lib/memory/design/shared_memory/OffsetPtrDesign.md#dynamic-array-considerations) - // requires that we use a DynamicArray to represent our slots so that bounds checking is done. - using DummySampleType = int; - static_assert( - std::is_same_v< - score::containers::DynamicArray< - DummySampleType, - std::scoped_allocator_adaptor>>, - EventDataStorage>, - "EventDataControl should use a dynamic array to represent slots."); + // Given an EventDataStorage constructed for a std::uint32_t sample type with kNumberOfSlots slots + memory::shared::NewDeleteDelegateMemoryResource memory_resource{kMemoryResourceId}; + const memory::DataTypeSizeInfo sample_size_info{sizeof(std::uint32_t), alignof(std::uint32_t)}; + EventDataStorage unit{memory_resource, kNumberOfSlots, sample_size_info}; + + // When requesting a data slot with an index that is out of bounds + // Then the program terminates. + EXPECT_DEATH(unit.GetTypeErasedDataSlot(kNumberOfSlots, sizeof(std::uint32_t)), ".*"); +} + +TEST(EventDataStorageDeathTest, ConstructionTerminatesOnRawSlotArraySizeOverflow) +{ + // Given a number_of_slots and a per-sample memory::DataTypeSizeInfo whose sizes, when multiplied to calculate + // the total size of the raw event-data slot-array, overflow std::size_t + memory::shared::NewDeleteDelegateMemoryResource memory_resource{kMemoryResourceId}; + constexpr std::size_t alignment{alignof(std::max_align_t)}; + const memory::DataTypeSizeInfo overflowing_sample_size_info{ + (std::numeric_limits::max() / alignment) * alignment, alignment}; + constexpr SlotIndexType number_of_slots{2U}; + + // When constructing an EventDataStorage from this sizing information + // Then the program terminates, since calculating the total raw slot-array size would silently overflow. + EXPECT_DEATH( + score::cpp::ignore = (EventDataStorage{memory_resource, number_of_slots, overflowing_sample_size_info}), ".*"); } } // namespace diff --git a/score/mw/com/impl/bindings/lola/event_meta_info.h b/score/mw/com/impl/bindings/lola/event_meta_info.h index 479daaa8d..d5dbce3df 100644 --- a/score/mw/com/impl/bindings/lola/event_meta_info.h +++ b/score/mw/com/impl/bindings/lola/event_meta_info.h @@ -14,7 +14,6 @@ #define SCORE_MW_COM_IMPL_BINDINGS_LOLA_EVENT_META_INFO_H #include "score/memory/data_type_size_info.h" -#include "score/memory/shared/offset_ptr.h" namespace score::mw::com::impl::lola { @@ -23,22 +22,18 @@ namespace score::mw::com::impl::lola /// \details Normally proxies/skeletons or "user code" dealing with an event, know its properties. This info is /// provided and placed into shared-memory for the GenericProxy use-case, where a proxy connects to a provided /// service based on only deployment info, NOT having any knowledge about the exact data type of the event. +/// Currently, the only "meta-info" needed is DataTypeSizeInfo. However, we wrap it into EventMetaInfo to be +/// prepared for future extensions. class EventMetaInfo { public: - EventMetaInfo(const memory::DataTypeSizeInfo data_type_info, - const memory::shared::OffsetPtr event_slots_raw_array) - : data_type_info_(data_type_info), event_slots_raw_array_(event_slots_raw_array) - { - } + EventMetaInfo(const memory::DataTypeSizeInfo data_type_info) : data_type_info_(data_type_info) {} // Suppress "AUTOSAR C++14 M11-0-1" rule findings. This rule states: "Member data in non-POD class types shall - // be private.". There are no class invariants to maintain which could be violated by directly accessing member + // be private". There are no class invariants to maintain which could be violated by directly accessing member // variables. // coverity[autosar_cpp14_m11_0_1_violation] memory::DataTypeSizeInfo data_type_info_; - // coverity[autosar_cpp14_m11_0_1_violation] - memory::shared::OffsetPtr event_slots_raw_array_; }; } // namespace score::mw::com::impl::lola diff --git a/score/mw/com/impl/bindings/lola/generic_proxy_event.cpp b/score/mw/com/impl/bindings/lola/generic_proxy_event.cpp index 020357dfb..3b1ffb940 100644 --- a/score/mw/com/impl/bindings/lola/generic_proxy_event.cpp +++ b/score/mw/com/impl/bindings/lola/generic_proxy_event.cpp @@ -10,10 +10,6 @@ * * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -/// -/// @file -/// @copyright Copyright (C) 2023, Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -/// #include "score/mw/com/impl/bindings/lola/generic_proxy_event.h" @@ -29,7 +25,8 @@ namespace score::mw::com::impl::lola GenericProxyEvent::GenericProxyEvent(Proxy& parent, const ElementFqId element_fq_id, const std::string_view event_name) : GenericProxyEventBinding{}, proxy_event_common_{parent, element_fq_id, event_name}, - meta_info_{parent.GetEventMetaInfo(element_fq_id)} + meta_info_{parent.GetEventMetaInfo(element_fq_id)}, + event_data_storage_{parent.GetEventDataStorage(element_fq_id)} { parent.RegisterEvent(event_name, *this); } @@ -136,53 +133,17 @@ Result GenericProxyEvent::GetNewSamplesImpl(Callback&& receiver, Tr auto& event_data_control_local = proxy_event_common_.GetConsumerEventDataControlLocal(); const std::size_t sample_size = meta_info_.data_type_info_.Size(); - const std::size_t sample_alignment = meta_info_.data_type_info_.Alignment(); - const std::size_t aligned_size = - memory::shared::CalculateAlignedSize(sample_size, static_cast(sample_alignment)); - const std::size_t max_number_of_sample_slots = event_data_control_local.GetMaxSampleSlots(); - const auto event_slots_raw_array_size = safe_math::Multiply(aligned_size, max_number_of_sample_slots); - - if (!event_slots_raw_array_size.has_value()) - { - score::mw::log::LogFatal("lola") << "Could not calculate the event slots raw array size. Terminating."; - std::terminate(); - } - - const void* const event_slots_raw_array = meta_info_.event_slots_raw_array_.get(event_slots_raw_array_size.value()); - - // AMP assert that the event_slots_raw_array address is according to sample_alignment for (auto slot_it = slot_indices.begin; slot_it != slot_indices.end; ++slot_it) { const auto slot_index = *slot_it; - // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) The pointer event_slots_raw_array points - // to the memory managed by a DynamicArray which has been type erased. The DynamicArray wraps a regular pointer - // array so elements may be accessed by using offsets to regular pointers to elements. Therefore, the pointer - // arithmetic is being done on memory which can be treated as an array. - // Suppress "AUTOSAR C++14 M5-2-8" rule: "An object with integer type or pointer to void type shall not be - // converted to an object with pointer type.". - // Casting to uint8_t pointer is as minimum byte size for pointer arithmetic to address a certain chunk of - // memory. - // coverity[autosar_cpp14_m5_2_8_violation] - const auto* const event_slots_array = static_cast(event_slots_raw_array); - SCORE_LANGUAGE_FUTURECPP_PRECONDITION_PRD_MESSAGE(nullptr != event_slots_array, "Null event slot array"); - // Suppress "AUTOSAR C++14 A5-3-2" rule finding. This rule states: "Null pointers shall not be dereferenced.". - // Suppress "AUTOSAR C++14 M5-0-15" rule finding. This rule states: "Array indexing shall be the only form of - // pointer arithmetic.". - // Suppress "AUTOSAR C++14 A4-7-1" rule finding. This rule states: "An integer expression shall not lead to - // data loss.". The result of the integer operation will be stored in a std::size_t which is the biggest integer - // type. - // coverity[autosar_cpp14_a5_3_2_violation] The check above ensures that array is not NULL - // coverity[autosar_cpp14_m5_0_15_violation] False-positive, get access through array indexing - // coverity[autosar_cpp14_a4_7_1_violation] - const auto* const object_start_address = &event_slots_array[aligned_size * slot_index]; - /* NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) deviation ends here */ + const void* type_erased_sample_ptr = event_data_storage_.GetTypeErasedDataSlot(slot_index, sample_size); const EventSlotStatus event_slot_status{event_data_control_local[slot_index]}; const EventSlotStatus::EventTimeStamp sample_timestamp{event_slot_status.GetTimeStamp()}; - SamplePtr sample{object_start_address, event_data_control_local, slot_index}; + SamplePtr sample{type_erased_sample_ptr, event_data_control_local, slot_index}; auto guard = std::move(*tracker.TakeGuard()); auto sample_binding_independent = this->MakeSamplePtr(std::move(sample), std::move(guard)); diff --git a/score/mw/com/impl/bindings/lola/generic_proxy_event.h b/score/mw/com/impl/bindings/lola/generic_proxy_event.h index 5b5b74370..7af45c59d 100644 --- a/score/mw/com/impl/bindings/lola/generic_proxy_event.h +++ b/score/mw/com/impl/bindings/lola/generic_proxy_event.h @@ -85,6 +85,7 @@ class GenericProxyEvent final : public GenericProxyEventBinding ProxyEventCommon proxy_event_common_; const EventMetaInfo& meta_info_; + const EventDataStorage& event_data_storage_; }; } // namespace score::mw::com::impl::lola diff --git a/score/mw/com/impl/bindings/lola/generic_proxy_event_test.cpp b/score/mw/com/impl/bindings/lola/generic_proxy_event_test.cpp index e77865c6d..f4210abfb 100644 --- a/score/mw/com/impl/bindings/lola/generic_proxy_event_test.cpp +++ b/score/mw/com/impl/bindings/lola/generic_proxy_event_test.cpp @@ -19,7 +19,6 @@ #include #include -#include #include #include @@ -28,8 +27,6 @@ namespace score::mw::com::impl::lola namespace { -const std::size_t kMaxSampleCount{2U}; - class LolaGenericProxyEventFixture : public LolaProxyEventResources { public: @@ -120,31 +117,5 @@ TEST_F(LolaGenericProxyEventDeathTest, FailOnEventNotFound) EXPECT_DEATH(WithAGenericProxyEvent(bad_element_fq_id, bad_event_name), ".*"); } -TEST_F(LolaGenericProxyEventDeathTest, OverflowWhenCalculatingRawEventsSlotsArraySizeTerminates) -{ - SampleReferenceTracker sample_reference_tracker_{kMaxSampleCount}; - TrackerGuardFactory guard_factory{sample_reference_tracker_.Allocate(1U)}; - - // Given a mocked SkeletonEvent whose metainfo stores a size which will lead to an overflow when calculating the raw - // event slot array size - const auto align_of = fake_data_->data_storage->events_metainfo_.at(element_fq_id_).data_type_info_.Alignment(); - - // Subtract the align of from the max size to prevent an overflow when calculating the aligned size. - // Keep the size a multiple of the alignment to satisfy the DataTypeSizeInfo invariant. - fake_data_->data_storage->events_metainfo_.at(element_fq_id_).data_type_info_ = - score::memory::DataTypeSizeInfo{(std::numeric_limits::max() / align_of) * align_of, align_of}; - - // and given a GenericProxyEvent which has subscribed - WithAGenericProxyEvent(element_fq_id_, event_name_); - std::ignore = generic_proxy_event_->Subscribe(kMaxSampleCount); - - // When calling GetNewSamples - // Then the program terminates - EXPECT_DEATH( - score::cpp::ignore = generic_proxy_event_->GetNewSamples( - [](impl::SamplePtr, const tracing::ITracingRuntime::TracePointDataId) noexcept {}, guard_factory), - ".*"); -} - } // namespace } // namespace score::mw::com::impl::lola diff --git a/score/mw/com/impl/bindings/lola/generic_skeleton_event.cpp b/score/mw/com/impl/bindings/lola/generic_skeleton_event.cpp index f98ef8c48..1554d5544 100644 --- a/score/mw/com/impl/bindings/lola/generic_skeleton_event.cpp +++ b/score/mw/com/impl/bindings/lola/generic_skeleton_event.cpp @@ -35,13 +35,10 @@ GenericSkeletonEvent::GenericSkeletonEvent(Skeleton& parent, Result GenericSkeletonEvent::PrepareOffer() noexcept { - const auto registration_result = - skeleton_event_common_.GetParent().RegisterGeneric(skeleton_event_common_.GetElementFQId(), - skeleton_event_common_.GetEventProperties(), - size_info_.Size(), - size_info_.Alignment()); + const auto registration_result = skeleton_event_common_.GetParent().Register( + skeleton_event_common_.GetElementFQId(), skeleton_event_common_.GetEventProperties(), size_info_); - event_data_storage_ = static_cast(registration_result.type_erased_event_data_storage_ptr); + event_data_storage_ = &(registration_result.event_data_storage); skeleton_event_common_.PrepareOfferCommon(registration_result.event_control_qm, registration_result.event_control_asil_b); @@ -87,11 +84,6 @@ Result GenericSkeletonEvent::Notify() noexcept return skeleton_event_common_.NotifyConsumersIfHandlersRegistered(); } -std::pair GenericSkeletonEvent::GetSizeInfo() const noexcept -{ - return {size_info_.Size(), size_info_.Alignment()}; -} - void GenericSkeletonEvent::PrepareStopOffer() noexcept { skeleton_event_common_.PrepareStopOfferCommon(); diff --git a/score/mw/com/impl/bindings/lola/generic_skeleton_event.h b/score/mw/com/impl/bindings/lola/generic_skeleton_event.h index 2e422dd70..6bb832fdd 100644 --- a/score/mw/com/impl/bindings/lola/generic_skeleton_event.h +++ b/score/mw/com/impl/bindings/lola/generic_skeleton_event.h @@ -50,7 +50,10 @@ class GenericSkeletonEvent : public GenericSkeletonEventBinding Result Notify() noexcept override; - std::pair GetSizeInfo() const noexcept override; + memory::DataTypeSizeInfo GetSizeInfo() const noexcept override + { + return size_info_; + } Result PrepareOffer() noexcept override; void PrepareStopOffer() noexcept override; @@ -60,16 +63,6 @@ class GenericSkeletonEvent : public GenericSkeletonEventBinding skeleton_event_common_.SetSkeletonEventTracingData(tracing_data); } - std::size_t GetMaxSize() const noexcept override - { - return size_info_.Size(); - } - - std::size_t GetAlignment() const noexcept override - { - return size_info_.Alignment(); - } - /// \brief Set callback, to get notified, when either the 1st event-notification has been registered or the last /// event-notification has been unregistered. /// \detail This extension has been added to GenericSkeletonEvent only (not "typed" SkeletonEvent), @@ -89,7 +82,7 @@ class GenericSkeletonEvent : public GenericSkeletonEventBinding private: memory::DataTypeSizeInfo size_info_; - std::uint8_t* event_data_storage_; + EventDataStorage* event_data_storage_; SkeletonEventCommon skeleton_event_common_; }; diff --git a/score/mw/com/impl/bindings/lola/generic_skeleton_event_test.cpp b/score/mw/com/impl/bindings/lola/generic_skeleton_event_test.cpp index 2af43843d..af3b659aa 100644 --- a/score/mw/com/impl/bindings/lola/generic_skeleton_event_test.cpp +++ b/score/mw/com/impl/bindings/lola/generic_skeleton_event_test.cpp @@ -125,32 +125,8 @@ TEST_F(GenericSkeletonEventFixture, GetSizeInfo) auto size_info = generic_skeleton_event_->GetSizeInfo(); // Then we get the correct size and alignment - EXPECT_EQ(size_info.first, size_info_.Size()); - EXPECT_EQ(size_info.second, size_info_.Alignment()); -} - -// Test: GetMaxSize -TEST_F(GenericSkeletonEventFixture, GetMaxSize) -{ - RecordProperty("Verifies", "SCR-14035184"); - RecordProperty("Description", "Checks that GetMaxSize returns correct maximum size."); - RecordProperty("TestType", "Requirements-based test"); - RecordProperty("Priority", "1"); - RecordProperty("DerivationTechnique", "Analysis of requirements"); - - const bool enforce_max_samples{true}; - const std::size_t max_samples{5U}; - const std::uint8_t max_subscribers{3U}; - - // Given a GenericSkeletonEvent - CreateGenericSkeletonEvent( - fake_element_fq_id_, fake_event_name_, max_samples, max_subscribers, enforce_max_samples); - - // When requesting max size - auto max_size = generic_skeleton_event_->GetMaxSize(); - - // Then we get the correct size - EXPECT_EQ(max_size, size_info_.Size()); + EXPECT_EQ(size_info.Size(), size_info_.Size()); + EXPECT_EQ(size_info.Alignment(), size_info_.Alignment()); } // Test: PrepareOffer diff --git a/score/mw/com/impl/bindings/lola/proxy.cpp b/score/mw/com/impl/bindings/lola/proxy.cpp index 589126b39..010d8d5b0 100644 --- a/score/mw/com/impl/bindings/lola/proxy.cpp +++ b/score/mw/com/impl/bindings/lola/proxy.cpp @@ -624,6 +624,29 @@ TransactionLogSet& Proxy::GetTransactionLogSet(const ElementFqId element_fq_id) return event_entry->second.transaction_log_set_; } +const EventDataStorage& Proxy::GetEventDataStorage(const ElementFqId element_fq_id) const +{ + SCORE_LANGUAGE_FUTURECPP_PRECONDITION_PRD_MESSAGE( + data_ != nullptr, "Proxy::GetEventDataStorage: Managed memory data pointer is Null"); + auto& service_data_storage = detail_proxy::GetServiceDataStorage(*data_); + const auto event_entry = service_data_storage.events_.find(element_fq_id); + if (event_entry == service_data_storage.events_.end()) + { + score::mw::log::LogFatal("lola") << __func__ << __LINE__ + << "Unable to find data storage for given event instance. Terminating."; + std::terminate(); + } + // Suppress "AUTOSAR C++14 A5-3-2" rule finding. This rule declares: "Null pointers shall not be dereferenced.". + // The "event_entry" variable is an iterator of interprocess map returned by the "find" method. + // A check is made that the iterator is not equal to map.end(). Therefore, the call to "event_entry->" + // does not return nullptr. + // coverity[autosar_cpp14_a5_3_2_violation] + const auto* event_data_storage_ptr = event_entry->second.get(); + SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD_MESSAGE(event_data_storage_ptr != nullptr, + "Could not get EventDataStorage from OffsetPtr"); + return *event_data_storage_ptr; +} + // Suppress "AUTOSAR C++14 A15-5-3" rule findings. This rule states: "The std::terminate() function shall not be called // implicitly". This is a false positive, std::less which is used by std::map::find could throw an exception if the key // value is not comparable and in our case the key is comparable. so no way for 'event_controls_.find()' to throw an diff --git a/score/mw/com/impl/bindings/lola/proxy.h b/score/mw/com/impl/bindings/lola/proxy.h index a4171d22d..d9f04955d 100644 --- a/score/mw/com/impl/bindings/lola/proxy.h +++ b/score/mw/com/impl/bindings/lola/proxy.h @@ -143,6 +143,12 @@ class Proxy : public ProxyBinding /// Terminates if the event control structure cannot be found. TransactionLogSet& GetTransactionLogSet(const ElementFqId element_fq_id); + /// Retrieves a reference to the event data storage area for a given ElementFqId. + /// + /// \param element_fq_id The Event ID. + /// \return A reference to the EventDataStorage. + const EventDataStorage& GetEventDataStorage(const ElementFqId element_fq_id) const; + /// Retrieves an event data meta info. /// /// The event meta info can be used to iterate over events in the event data storage when the type is not known e.g. diff --git a/score/mw/com/impl/bindings/lola/proxy_event.h b/score/mw/com/impl/bindings/lola/proxy_event.h index c802a519b..fdc82d02b 100644 --- a/score/mw/com/impl/bindings/lola/proxy_event.h +++ b/score/mw/com/impl/bindings/lola/proxy_event.h @@ -70,8 +70,7 @@ class ProxyEvent final : public ProxyEventBinding : ProxyEventBinding{}, proxy_event_common_{parent, element_fq_id, event_name}, meta_info_{parent.GetEventMetaInfo(element_fq_id)}, - aligned_sample_size_{memory::shared::CalculateAlignedSize(sizeof(SampleType), alignof(SampleType))}, - event_slots_raw_array_{InitialiseEventSlotsRawArray()} + event_data_storage_{parent.GetEventDataStorage(element_fq_id)} { parent.RegisterEvent(event_name, *this); } @@ -134,36 +133,14 @@ class ProxyEvent final : public ProxyEventBinding }; private: - const std::uint8_t* InitialiseEventSlotsRawArray(); - Result GetNewSamplesImpl(Callback&& receiver, TrackerGuardFactory& tracker) noexcept; Result GetNumNewSamplesAvailableImpl() const noexcept; ProxyEventCommon proxy_event_common_; const EventMetaInfo& meta_info_; - const std::size_t aligned_sample_size_; - const std::uint8_t* event_slots_raw_array_; + const EventDataStorage& event_data_storage_; }; -template -inline const std::uint8_t* ProxyEvent::InitialiseEventSlotsRawArray() -{ - auto& event_data_control_local = proxy_event_common_.GetConsumerEventDataControlLocal(); - - const auto event_slots_raw_array_size = safe_math::Multiply( - 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(event_slots_raw_array); -} - template inline Result ProxyEvent::GetNumNewSamplesAvailable() const { @@ -218,26 +195,17 @@ inline Result ProxyEvent::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) { - // 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, 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 - // 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) + const void* type_erased_sample_ptr = + event_data_storage_.GetTypeErasedDataSlot(*slot_index_it, sizeof(SampleType)); // 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). + // not be converted to an object with pointer type". + // The event samples are stored type-erased within shared-memory. // coverity[autosar_cpp14_m5_2_8_violation] - const SampleType& sample_data{*reinterpret_cast(object_start_address)}; + const SampleType& sample_data{*static_cast(type_erased_sample_ptr)}; const EventSlotStatus event_slot_status{event_data_control_local[*slot_index_it]}; const EventSlotStatus::EventTimeStamp sample_timestamp{event_slot_status.GetTimeStamp()}; diff --git a/score/mw/com/impl/bindings/lola/proxy_test.cpp b/score/mw/com/impl/bindings/lola/proxy_test.cpp index 2c6eeefee..8cccb2987 100644 --- a/score/mw/com/impl/bindings/lola/proxy_test.cpp +++ b/score/mw/com/impl/bindings/lola/proxy_test.cpp @@ -122,7 +122,7 @@ TEST_F(ProxyCreationFixture, ProxyCreationOpensSharedMemoryWithoutProvidersIfNot .WillOnce(WithArg<2>( Invoke([this](const auto& provider_list) -> std::shared_ptr { EXPECT_FALSE(provider_list.has_value()); - return fake_data_->data_memory; + return fake_data_->data_memory_resource; }))); // When creating a proxy @@ -157,7 +157,7 @@ TEST_F(ProxyCreationFixture, ProxyCreationOpensSharedMemoryWithProvidersFromConf EXPECT_TRUE(provider_list.has_value()); EXPECT_THAT(provider_list.value(), Contains(allowed_qm_providers[0])); EXPECT_THAT(provider_list.value(), Contains(allowed_qm_providers[1])); - return fake_data_->data_memory; + return fake_data_->data_memory_resource; }))); // When creating a proxy @@ -672,7 +672,7 @@ TEST_F(ProxyGetEventMetaInfoDeathTest, CallingGetEventMetaInfoWhenGettingDataSec InitialiseProxyWithCreate(identifier_); // and that getting the usable base address (from which we read the EventMetaInfo) returns a nullptr - ON_CALL(*(fake_data_->data_memory), getUsableBaseAddress()).WillByDefault(Return(nullptr)); + ON_CALL(*(fake_data_->data_memory_resource), getUsableBaseAddress()).WillByDefault(Return(nullptr)); // When getting the EventMetaInfo for a random element fq id // Then the program terminates diff --git a/score/mw/com/impl/bindings/lola/service_data_storage.cpp b/score/mw/com/impl/bindings/lola/service_data_storage.cpp index 6ebe1a577..c8f63e3b2 100644 --- a/score/mw/com/impl/bindings/lola/service_data_storage.cpp +++ b/score/mw/com/impl/bindings/lola/service_data_storage.cpp @@ -51,14 +51,13 @@ std::size_t CalculateServiceDataStorageShmSize( alignof(ServiceDataStorage::EventMetaInfoMap::value_type)); // (3) For each event/field (in the exact order it gets registered/offered): the EventDataStorage object plus its - // raw slot-array. The exact size/alignment of the slot-array (which differs between typed and generic - // events/fields, see SkeletonMemoryManager::CreateEventDataInCreatedSharedMemory() resp. - // CreateGenericEventDataInCreatedSharedMemory()) is provided by the caller. + // data-slot-array (type_erased_data_slots_). The exact size/alignment of the slot-array (see + // SkeletonMemoryManager::CreateEventDataInCreatedSharedMemory()) is provided by the caller. - // The size/alignment of the EventDataStorage control structure (a DynamicArray) is independent of the concrete + // The size/alignment of the EventDataStorage control structure is independent of the concrete // sample-type (it only holds an offset-pointer, an allocator and two size_t members). - constexpr std::size_t event_data_storage_object_size = sizeof(EventDataStorage); - constexpr std::size_t event_data_storage_object_alignment = alignof(EventDataStorage); + constexpr std::size_t event_data_storage_object_size = sizeof(EventDataStorage); + constexpr std::size_t event_data_storage_object_alignment = alignof(EventDataStorage); for (const auto& service_element : event_and_fields_size_info) { allocation_sequence.emplace_back(event_data_storage_object_size, event_data_storage_object_alignment); diff --git a/score/mw/com/impl/bindings/lola/service_data_storage.h b/score/mw/com/impl/bindings/lola/service_data_storage.h index bb93fee2a..c547bc973 100644 --- a/score/mw/com/impl/bindings/lola/service_data_storage.h +++ b/score/mw/com/impl/bindings/lola/service_data_storage.h @@ -15,6 +15,7 @@ #include "score/mw/com/impl/binding_type.h" #include "score/mw/com/impl/bindings/lola/element_fq_id.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/i_runtime.h" #include "score/mw/com/impl/bindings/lola/linear_search_map.h" @@ -36,16 +37,15 @@ class ServiceDataStorage { public: /// \brief associative container mapping a service-element (event/field) to the raw storage of its event-data slots. - /// \details The value-type of the map is a type-erased pointer to the raw storage of the event-data slots. - /// The OffsetPtr points to a EventDataStorage, which gets created by events/fields, when - /// calling Skeleton::Register()! - /// - using EventDataStorageMap = LinearSearchMap>; + /// \details The value-type of the map is a pointer to the storage of the type-erased event-data slots. + /// The OffsetPtr points to a EventDataStorage, which gets created by events/fields, when calling + /// Skeleton::Register()! + using EventDataStorageMap = LinearSearchMap>; /// \brief associative container mapping a service-element (event/field) to its (type-erased) meta-information. using EventMetaInfoMap = LinearSearchMap; /// \brief Ctor for the ServiceDataStorage with a given memory resource to be used for internal storage allocation. - /// \details ServiceDataStorage no longer uses dynamically allocating map-types. Instead it uses fixed-capacity + /// \details ServiceDataStorage no longer uses dynamically allocating map-types. Instead, it uses fixed-capacity /// containers (LinearSearchMap) whose capacity has to be provided at construction time. The capacity /// equals the number of service-elements (events + fields) of the service-instance, which is known /// up-front. This makes the memory footprint of ServiceDataStorage deterministic and calculable without a @@ -92,7 +92,7 @@ class ServiceDataStorage /// (see SkeletonMemoryManager::CreateEventDataInCreatedSharedMemory()). /// - generic events allocate number_of_slots * sample_size bytes rounded up to a whole number of /// std::max_align_t elements, aligned to alignof(std::max_align_t) (see -/// SkeletonMemoryManager::CreateGenericEventDataInCreatedSharedMemory()). +/// SkeletonMemoryManager::CreateEventDataInCreatedSharedMemory()). /// The size of the span equals the number of service-elements (events + fields), which is the fixed capacity /// the ServiceDataStorage containers are constructed with. /// \return the exact number of bytes needed for the data shm-object. diff --git a/score/mw/com/impl/bindings/lola/service_data_storage_test.cpp b/score/mw/com/impl/bindings/lola/service_data_storage_test.cpp index 89a51c804..7a44b4b9b 100644 --- a/score/mw/com/impl/bindings/lola/service_data_storage_test.cpp +++ b/score/mw/com/impl/bindings/lola/service_data_storage_test.cpp @@ -146,75 +146,71 @@ TEST(ServiceDataStorageShmSizeTest, AddingAnAdditionalServiceElementIncreasesCal EXPECT_GT(size_for_two_service_elements, size_for_single_service_element); } -/// \brief A trivial type of exactly Alignment bytes size, aligned to Alignment bytes. -/// \details Used to construct a real EventDataStorage> whose raw slot-array has the very -/// same size/alignment characteristics as a score::memory::DataTypeSizeInfo entry (Size() / Alignment()), by -/// choosing the number of slots as Size() / Alignment. This lets the tests below verify -/// CalculateServiceDataStorageShmSize's exact-size claim without needing to know the real (production) sample-type -/// of each service-element. -template -struct alignas(Alignment) AlignedBlock +/// \brief Sizing information of a single service-element (event/field): the size/alignment of a single sample of +/// its datatype plus the number of slots in its raw slot-array. This mirrors exactly what +/// SkeletonMemoryManager::CreateEventDataInCreatedSharedMemory() passes to EventDataStorage's constructor at runtime. +struct EventOrFieldSizeInfo { - std::byte data[Alignment]; + score::memory::DataTypeSizeInfo per_sample_size_info; + std::size_t number_of_slots; }; -/// \brief Constructs a real EventDataStorage> (with Size() / Alignment slots) on the -/// given resource, mirroring the size/alignment of the given service_element's raw slot-array. -template -void ConstructEventDataStorage(const score::memory::DataTypeSizeInfo& service_element, - memory::shared::ManagedMemoryResource& resource) -{ - const auto number_of_slots = service_element.Size() / Alignment; - score::cpp::ignore = resource.construct>>( - number_of_slots, memory::shared::PolymorphicOffsetPtrAllocator>(resource)); -} +using EventsOrFieldsSizeInfo = std::vector; /// \brief Constructs a real ServiceDataStorage on the given resource and, for each entry of /// service_elements_size_info, a real EventDataStorage whose raw slot-array's size/alignment matches the entry /// (mirroring what SkeletonMemoryManager does at runtime for each event/field). /// \return the number of bytes the given resource reports as allocated after construction. -std::size_t ConstructServiceDataStorageAndGetAllocatedBytes( - const std::vector& service_elements_size_info, - memory::shared::ManagedMemoryResource& resource) +std::size_t ConstructServiceDataStorageAndGetAllocatedBytes(const EventsOrFieldsSizeInfo& service_elements_size_info, + memory::shared::ManagedMemoryResource& resource) { score::cpp::ignore = resource.construct(service_elements_size_info.size(), resource); for (const auto& service_element : service_elements_size_info) { - // Only a handful of alignments are exercised by these tests; dispatch to the matching instantiation of - // ConstructEventDataStorage() so a real DynamicArray with the required alignment gets allocated. - switch (service_element.Alignment()) - { - case 8U: - ConstructEventDataStorage<8U>(service_element, resource); - break; - case kMaxSupportedAlignment: - ConstructEventDataStorage(service_element, resource); - break; - default: - ADD_FAILURE() << "Unsupported alignment in test: " << service_element.Alignment(); - break; - } + // Construct a real EventDataStorage (with number_of_slots slots) on the resource, mirroring the + // size/alignment of the service_element's datatype. + score::cpp::ignore = + resource.construct(resource, + static_cast(service_element.number_of_slots), + service_element.per_sample_size_info); } return resource.GetUserAllocatedBytes(); } -using ServiceElementsSizeInfo = std::vector; +/// \brief Converts the given per-service-element sizing information (one sample's size/alignment plus the number of +/// slots) into the per-service-element TOTAL raw slot-array sizing information expected by +/// CalculateServiceDataStorageShmSize(). +std::vector ToSlotArraySizeInfos( + const EventsOrFieldsSizeInfo& service_elements_size_info) +{ + std::vector slot_array_size_infos{}; + slot_array_size_infos.reserve(service_elements_size_info.size()); + for (const auto& service_element : service_elements_size_info) + { + slot_array_size_infos.emplace_back( + service_element.number_of_slots * service_element.per_sample_size_info.Size(), + service_element.per_sample_size_info.Alignment()); + } + return slot_array_size_infos; +} class ServiceDataStorageShmSizeParameterizedTestFixture : public ServiceDataStorageFixture, - public ::testing::WithParamInterface + public ::testing::WithParamInterface { }; TEST_P(ServiceDataStorageShmSizeParameterizedTestFixture, CalculatedSizeMatchesActualAllocation) { - // Given the sizing information of some (possibly zero) service-elements (events/fields) + // Given the sizing information of some (possibly zero) service-elements (events/fields), each described by the + // size/alignment of a single sample of its datatype plus its number of slots const auto& service_elements_size_info = GetParam(); // When calculating the required shm-size for a ServiceDataStorage holding these service-elements + const auto slot_array_size_infos = ToSlotArraySizeInfos(service_elements_size_info); const auto calculated_size = CalculateServiceDataStorageShmSize( - score::cpp::span{service_elements_size_info}); + score::cpp::span{slot_array_size_infos}); // Then the calculated size exactly matches the number of bytes actually allocated when constructing a real // ServiceDataStorage (and its EventDataStorages) with the very same sizing information. @@ -230,14 +226,14 @@ INSTANTIATE_TEST_SUITE_P( ServiceDataStorageShmSizeParameterizedTestFixture, ::testing::Values( // No service-elements at all (an empty span) - ServiceElementsSizeInfo{}, - // A single service-element (event/field) - ServiceElementsSizeInfo{score::memory::DataTypeSizeInfo{80U, 16U}}, - // Multiple service-elements (events/fields) with differing raw slot-array sizes and alignments - ServiceElementsSizeInfo{ - score::memory::DataTypeSizeInfo{16U, 8U}, - score::memory::DataTypeSizeInfo{224U, kMaxSupportedAlignment}, - score::memory::DataTypeSizeInfo{128U, kMaxSupportedAlignment}, + EventsOrFieldsSizeInfo{}, + // A single service-element (event/field): 5 slots of a datatype of size/alignment 16 + EventsOrFieldsSizeInfo{EventOrFieldSizeInfo{score::memory::DataTypeSizeInfo{16U, 16U}, 5U}}, + // Multiple service-elements (events/fields) with differing per-sample sizes/alignments and slot-counts + EventsOrFieldsSizeInfo{ + EventOrFieldSizeInfo{score::memory::DataTypeSizeInfo{8U, 8U}, 2U}, + EventOrFieldSizeInfo{score::memory::DataTypeSizeInfo{kMaxSupportedAlignment, kMaxSupportedAlignment}, 14U}, + EventOrFieldSizeInfo{score::memory::DataTypeSizeInfo{kMaxSupportedAlignment, kMaxSupportedAlignment}, 8U}, })); } // namespace diff --git a/score/mw/com/impl/bindings/lola/skeleton.cpp b/score/mw/com/impl/bindings/lola/skeleton.cpp index 4c3457ea1..368f69512 100644 --- a/score/mw/com/impl/bindings/lola/skeleton.cpp +++ b/score/mw/com/impl/bindings/lola/skeleton.cpp @@ -571,10 +571,9 @@ bool Skeleton::VerifyAllMethodHandlersRegistered() const }); } -auto Skeleton::RegisterGeneric(const ElementFqId element_fq_id, - const SkeletonEventProperties& element_properties, - const size_t sample_size, - const size_t sample_alignment) -> GenericRegistrationResult +auto Skeleton::Register(const ElementFqId element_fq_id, + const SkeletonEventProperties& element_properties, + const memory::DataTypeSizeInfo sample_size_info) -> RegistrationResult { if (use_gateway_forwarded_shm_ || was_old_shm_region_reopened_) { @@ -597,17 +596,16 @@ auto Skeleton::RegisterGeneric(const ElementFqId element_fq_id, } } - auto* const event_data_storage = - memory_manager_.RetrieveGenericEventDataFromOpenedSharedMemory(element_fq_id, element_properties); + auto& event_data_storage = memory_manager_.RetrieveEventDataFromOpenedSharedMemory(element_fq_id); return {event_data_storage, event_data_control_qm, event_data_control_asil_b}; } - auto* const type_erased_event_data_storage = memory_manager_.CreateGenericEventDataInCreatedSharedMemory( - element_fq_id, element_properties, sample_size, sample_alignment); + auto& event_data_storage = + memory_manager_.CreateEventDataInCreatedSharedMemory(element_fq_id, element_properties, sample_size_info); auto [event_data_control_qm, event_data_control_asil_b] = memory_manager_.CreateEventControlsInCreatedSharedMemory(element_fq_id, element_properties); - return GenericRegistrationResult{type_erased_event_data_storage, event_data_control_qm, event_data_control_asil_b}; + return {event_data_storage, event_data_control_qm, event_data_control_asil_b}; } auto Skeleton::RegisterMethodHandlers(const QualityType asil_level, diff --git a/score/mw/com/impl/bindings/lola/skeleton.h b/score/mw/com/impl/bindings/lola/skeleton.h index ec651c633..8673d6631 100644 --- a/score/mw/com/impl/bindings/lola/skeleton.h +++ b/score/mw/com/impl/bindings/lola/skeleton.h @@ -71,17 +71,9 @@ class Skeleton final : public SkeletonBinding friend class SkeletonAttorney; public: - template struct RegistrationResult { - EventDataStorage& event_data_storage; - EventControl& event_control_qm; - EventControl* event_control_asil_b; - }; - - struct GenericRegistrationResult - { - void* type_erased_event_data_storage_ptr; + EventDataStorage& event_data_storage; EventControl& event_control_qm; EventControl* event_control_asil_b; }; @@ -122,31 +114,18 @@ class Skeleton final : public SkeletonBinding return BindingType::kLoLa; }; - /// \brief Enables dynamic registration of Generic (type-erased) Events at the Skeleton. + /// \brief Enables dynamic registration of Events (typed and generic) at the Skeleton. /// \param element_fq_id The full qualified ID of the element (event) that shall be registered. /// \param element_properties Properties of the element (e.g. number of slots, max subscribers). - /// \param sample_size The size of a single data sample in bytes. - /// \param sample_alignment The alignment requirement of the data sample in bytes. - /// \return A pair containing: - /// - An type erased pointer to the allocated data storage (void*). - /// - The EventDataControlComposite for managing the event's control data. - auto RegisterGeneric(const ElementFqId element_fq_id, - const SkeletonEventProperties& element_properties, - const size_t sample_size, - const size_t sample_alignment) -> GenericRegistrationResult; - - /// \brief Enables dynamic registration of Events at the Skeleton. - /// \tparam SampleType The type of the event - /// \param element_fq_id The full qualified of the element (event or field) that shall be registered - /// \param element_properties properties of the element, which are currently event specific properties. + /// \param data_type_size_info The size and alignment of a single data sample in bytes. /// \return The registered data structures within the Skeleton - /// (first: where to store data, second: control data - /// access) If PrepareOffer created the shared memory, then will create an EventDataControl (for QM and + /// (first: where to store data, second: control data access) + /// If PrepareOffer created the shared memory, then will create an EventDataControl (for QM and /// optionally for ASIL B) and an EventDataStorage which will be returned. If PrepareOffer opened the /// shared memory, then the opened event data from the existing shared memory will be returned. - template - auto Register(const ElementFqId element_fq_id, SkeletonEventProperties element_properties) - -> RegistrationResult; + auto Register(const ElementFqId element_fq_id, + const SkeletonEventProperties& element_properties, + const memory::DataTypeSizeInfo data_type_size_info) -> RegistrationResult; QualityType GetInstanceQualityType() const; @@ -302,51 +281,6 @@ class Skeleton final : public SkeletonBinding safecpp::Scope<> on_service_method_subscribed_handler_scope_; }; -template -auto Skeleton::Register(const ElementFqId element_fq_id, SkeletonEventProperties element_properties) - -> RegistrationResult -{ - // If the skeleton previously crashed and there are active proxies connected to the old shared memory, then we - // re-open that shared memory in PrepareOffer(). In that case, we should retrieve the EventDataControl and - // EventDataStorage from the shared memory and attempt to rollback the Skeleton tracing transaction log. - // use_gateway_forwarded_shm_ is only ever set in inter-VM gateway setups, where a GenericSkeleton - // (type-erased) is used exclusively. A GenericSkeleton registers events via RegisterGeneric(), NOT - // via this typed template. Therefore reaching this function with use_gateway_forwarded_shm_ == true - // is a programming error: a typed Skeleton must never be used in a gateway-forwarding context. - SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD_MESSAGE(!use_gateway_forwarded_shm_, - "Register must not be called for a gateway-forwarded " - "skeleton — use GenericSkeleton/RegisterGeneric instead"); - - if (was_old_shm_region_reopened_) - { - auto [event_data_control_qm, event_data_control_asil_b] = - memory_manager_.RetrieveEventControlsFromOpenedSharedMemory(element_fq_id); - - // We can have transactions in the TransactionLogs relating to tracing (QM only) or field getter logic (QM and / - // or ASIL-B). We try rolling back all TransactionLogSets which are found. - // We rollback any transactions in the TransactionLog that correspond to the SkeletonEvent even if - // tracing is disabled in the current process. It's possible that we could have tracing disabled in this process - // but the crashed process had tracing enabled and therefore may have transactions that need to be rolled back. - // If tracing was also disabled in the previous process or if there are no transactions to rollback, - // RollbackSkeletonTracingTransactions will simply do nothing. - memory_manager_.RollbackSkeletonTracingTransactions(event_data_control_qm); - if (event_data_control_asil_b != nullptr) - { - memory_manager_.RollbackSkeletonTracingTransactions(*event_data_control_asil_b); - } - - auto& event_data_storage = memory_manager_.RetrieveEventDataFromOpenedSharedMemory(element_fq_id); - return RegistrationResult{event_data_storage, event_data_control_qm, event_data_control_asil_b}; - } - - auto& event_data_storage = - memory_manager_.CreateEventDataInCreatedSharedMemory(element_fq_id, element_properties); - auto [event_data_control_qm, event_data_control_asil_b] = - memory_manager_.CreateEventControlsInCreatedSharedMemory(element_fq_id, element_properties); - - return RegistrationResult{event_data_storage, event_data_control_qm, event_data_control_asil_b}; -} - } // namespace score::mw::com::impl::lola #endif // SCORE_MW_COM_IMPL_BINDINGS_LOLA_SKELETON_H diff --git a/score/mw/com/impl/bindings/lola/skeleton_event.h b/score/mw/com/impl/bindings/lola/skeleton_event.h index 2c5f96906..36a232793 100644 --- a/score/mw/com/impl/bindings/lola/skeleton_event.h +++ b/score/mw/com/impl/bindings/lola/skeleton_event.h @@ -17,8 +17,6 @@ #include "score/mw/com/impl/bindings/lola/element_fq_id.h" #include "score/mw/com/impl/bindings/lola/event_data_control_composite.h" #include "score/mw/com/impl/bindings/lola/event_data_storage.h" -#include "score/mw/com/impl/bindings/lola/i_runtime.h" -#include "score/mw/com/impl/bindings/lola/provider_event_data_control_local_view.h" #include "score/mw/com/impl/bindings/lola/sample_allocatee_ptr.h" #include "score/mw/com/impl/bindings/lola/sample_ptr.h" #include "score/mw/com/impl/bindings/lola/skeleton.h" @@ -28,15 +26,16 @@ #include "score/mw/com/impl/configuration/quality_type.h" #include "score/mw/com/impl/plumbing/sample_allocatee_ptr.h" #include "score/mw/com/impl/plumbing/sample_ptr.h" -#include "score/mw/com/impl/runtime.h" #include "score/mw/com/impl/skeleton_event_binding.h" #include "score/mw/com/impl/tracing/skeleton_event_tracing_data.h" +#include "score/memory/data_type_size_info.h" +#include "score/mw/log/logging.h" #include "score/result/result.h" + #include #include -#include #include #include #include @@ -109,7 +108,7 @@ class SkeletonEvent final : public SkeletonEventBinding } private: - EventDataStorage* event_data_storage_; + EventDataStorage* event_data_storage_; SkeletonEventCommon skeleton_event_common_; }; @@ -186,7 +185,7 @@ Result> SkeletonEvent::Allocate // GetLatestSample(). return MakeSampleAllocateePtr( SampleAllocateePtr( - &event_data_storage_->at(static_cast(slot_index)), + static_cast(event_data_storage_->GetTypeErasedDataSlot(slot_index, sizeof(SampleType))), skeleton_event_common_.GetEventDataControlComposite(), skeleton_event_common_.GetConsumerEventDataControlLocalView(QualityType::kASIL_QM), slot_index), @@ -223,9 +222,10 @@ Result> SkeletonEvent::GetLatestSample(Q } return impl::SamplePtr{ - lola::SamplePtr{&event_data_storage_->at(static_cast(*slot_result)), - consumer_event_data_control_local, - slot_result.value()}, + lola::SamplePtr{ + static_cast(event_data_storage_->GetTypeErasedDataSlot(*slot_result, sizeof(SampleType))), + consumer_event_data_control_local, + slot_result.value()}, std::move(*guard)}; } @@ -240,8 +240,9 @@ Result SkeletonEvent::PrepareOffer() noexcept { // Invariant: after a successful PrepareOffer(), event_data_storage_ is guaranteed to be non-null. // All methods that require event_data_storage_ (e.g. GetLatestSample) rely on this invariant. - const auto registration_result = skeleton_event_common_.GetParent().template Register( - skeleton_event_common_.GetElementFQId(), skeleton_event_common_.GetEventProperties()); + memory::DataTypeSizeInfo sample_size_info{sizeof(SampleType), alignof(SampleType)}; + const auto registration_result = skeleton_event_common_.GetParent().Register( + skeleton_event_common_.GetElementFQId(), skeleton_event_common_.GetEventProperties(), sample_size_info); event_data_storage_ = ®istration_result.event_data_storage; SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD_MESSAGE(event_data_storage_ != nullptr, "event_data_storage_ must be non-null after PrepareOffer"); diff --git a/score/mw/com/impl/bindings/lola/skeleton_memory_manager.cpp b/score/mw/com/impl/bindings/lola/skeleton_memory_manager.cpp index dabb70ba3..e8683fe3b 100644 --- a/score/mw/com/impl/bindings/lola/skeleton_memory_manager.cpp +++ b/score/mw/com/impl/bindings/lola/skeleton_memory_manager.cpp @@ -107,25 +107,6 @@ std::uint64_t CalculateMemoryResourceId(const LolaServiceTypeDeployment::Service (static_cast(lola_instance_id) << 8U) + static_cast(object_type)); } -/// \brief Determines whether the given event/field bindings are generic (type-erased) or typed bindings. -/// \details Within a single Skeleton instance, event/field bindings are homogeneously either all generic -/// (GenericSkeletonEventBinding, used exclusively by a GenericSkeleton) or all typed (SkeletonEventBinding, -/// used exclusively by a code-generated, typed Skeleton) - a single skeleton instance never mixes both -/// kinds. It is therefore sufficient to inspect a single (arbitrary) binding to determine which kind ALL of them are. -/// \return true if the bindings are generic, false if they are typed. If both events and fields are empty, false is -/// returned (the value is irrelevant in that case, as there is nothing to size). -bool AreServiceElementBindingsGeneric(const SkeletonBinding::SkeletonEventBindings& events, - const SkeletonBinding::SkeletonFieldBindings& fields) -{ - const auto* const first_binding_map = !events.empty() ? &events : (!fields.empty() ? &fields : nullptr); - if (first_binding_map == nullptr) - { - return false; - } - const SkeletonEventBindingBase& first_binding = first_binding_map->begin()->second.get(); - return dynamic_cast(&first_binding) != nullptr; -} - } // namespace SkeletonMemoryManager::SkeletonMemoryManager(QualityType quality_type, @@ -226,73 +207,54 @@ auto SkeletonMemoryManager::CreateEventControlsInCreatedSharedMemory(const Eleme return {provider_event_control_qm, &provider_event_control_asil_b}; } -void* SkeletonMemoryManager::CreateGenericEventDataInCreatedSharedMemory( +EventDataStorage& SkeletonMemoryManager::CreateEventDataInCreatedSharedMemory( const ElementFqId element_fq_id, const SkeletonEventProperties& element_properties, - size_t sample_size, - size_t sample_alignment) + memory::DataTypeSizeInfo sample_size_info) { - // Guard against over-aligned types (Short-term solution protection) - if (sample_alignment > alignof(std::max_align_t)) - { - score::mw::log::LogFatal("Skeleton") - << "Requested sample alignment (" << sample_alignment << ") exceeds max_align_t (" - << alignof(std::max_align_t) << "). Safe shared memory layout cannot be guaranteed."; - - SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD_MESSAGE(sample_alignment <= alignof(std::max_align_t), - "Requested sample alignment exceeds maximum supported alignment."); - } - - // Calculate the aligned size for a single sample to ensure proper padding between slots - const auto aligned_sample_size = memory::shared::CalculateAlignedSize(sample_size, sample_alignment); - const auto total_data_size_bytes = aligned_sample_size * element_properties.GetTotalNumberOfSlots(); - - // Convert total bytes to the number of std::max_align_t elements needed (round up) - const size_t num_max_align_elements = - (total_data_size_bytes + sizeof(std::max_align_t) - 1) / sizeof(std::max_align_t); - - auto* data_storage = storage_resource_->construct>( - num_max_align_elements, memory::shared::PolymorphicOffsetPtrAllocator(*storage_resource_)); + auto* data_storage = storage_resource_->construct( + *storage_resource_, static_cast(element_properties.GetTotalNumberOfSlots()), sample_size_info); auto inserted_data_slots = storage_->events_.emplace( std::piecewise_construct, std::forward_as_tuple(element_fq_id), std::forward_as_tuple(data_storage)); SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD_MESSAGE(inserted_data_slots.second, "Couldn't register/emplace event-storage in data-section."); - const memory::DataTypeSizeInfo sample_meta_info{sample_size, sample_alignment}; - void* const event_data_raw_array = data_storage->data(); - - auto inserted_meta_info = - storage_->events_metainfo_.emplace(std::piecewise_construct, - std::forward_as_tuple(element_fq_id), - std::forward_as_tuple(sample_meta_info, event_data_raw_array)); + auto inserted_meta_info = storage_->events_metainfo_.emplace( + std::piecewise_construct, std::forward_as_tuple(element_fq_id), std::forward_as_tuple(sample_size_info)); SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD_MESSAGE(inserted_meta_info.second, "Couldn't register/emplace event-meta-info in data-section."); - return event_data_raw_array; + return *data_storage; } -void* SkeletonMemoryManager::RetrieveGenericEventDataFromOpenedSharedMemory( - const ElementFqId element_fq_id, - const SkeletonEventProperties& element_properties) +auto SkeletonMemoryManager::RetrieveEventDataFromOpenedSharedMemory(const ElementFqId element_fq_id) + -> EventDataStorage& { - SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD_MESSAGE(storage_ != nullptr, "Service data storage is not available."); + // Suppress "AUTOSAR C++14 A15-5-3": + // Justification: This is a false positive, std::less which is used by std::map::find could throw an exception if + // the key value is not comparable and in our case the key is comparable. so no way for 'event_controls_.find()' to + // throw an exception. + // coverity[autosar_cpp14_a15_5_3_violation : FALSE] + auto find_element = [](auto& map, const ElementFqId& target_element_fq_id) -> auto { + const auto it = map.find(target_element_fq_id); + SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD_MESSAGE(it != map.cend(), "Could not find element fq id in map"); + return it; + }; - const auto event_meta_info_it = storage_->events_metainfo_.find(element_fq_id); - SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD_MESSAGE(event_meta_info_it != storage_->events_metainfo_.cend(), - "Could not find element fq id in meta info map"); + score::cpp::ignore = find_element(storage_->events_metainfo_, element_fq_id); + const auto event_data_storage_it = find_element(storage_->events_, element_fq_id); - const auto sample_size = event_meta_info_it->second.data_type_info_.Size(); - const auto sample_alignment = event_meta_info_it->second.data_type_info_.Alignment(); - const auto aligned_sample_size = - memory::shared::CalculateAlignedSize(sample_size, static_cast(sample_alignment)); - const auto total_event_slots_size = safe_math::Multiply( - aligned_sample_size, element_properties.GetTotalNumberOfSlots()); + // Suppress "AUTOSAR C++14 A5-3-2": Don't dereference null pointers. + // Justification: The "event_data_storage_it" variable is an iterator of interprocess map returned by the + // "find_element" method. A check is made that the iterator is not equal to map.cend(). Therefore, the call to + // "event_data_storage_it->" does not return nullptr. + // coverity[autosar_cpp14_a5_3_2_violation] + auto* const type_erased_event_data_storage_ptr = event_data_storage_it->second.get(); + SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD_MESSAGE(type_erased_event_data_storage_ptr != nullptr, + "Could not get EventDataStorage*"); - void* const event_slots_raw_array = event_meta_info_it->second.event_slots_raw_array_.get(total_event_slots_size); - SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD_MESSAGE(event_slots_raw_array != nullptr, - "Could not get generic EventDataStorage raw array"); - return event_slots_raw_array; + return *type_erased_event_data_storage_ptr; } auto SkeletonMemoryManager::RetrieveEventControlsFromOpenedSharedMemory(const ElementFqId element_fq_id) @@ -563,47 +525,21 @@ std::size_t SkeletonMemoryManager::CalculateDataShmResourceStorageSize( SkeletonBinding::SkeletonEventBindings& events, SkeletonBinding::SkeletonFieldBindings& fields) const { - // Whether the event/field bindings are generic or typed is determined once, upfront (a skeleton instance never - // mixes both kinds - see AreServiceElementBindingsGeneric() for details). This distinction between generic/typed - // bindings is necessary as the memory allocation taking place is slightly different currently. When we switch to - // complete type-erasure at some point for the binding layer, this distinction will go away! - const bool are_bindings_generic = AreServiceElementBindingsGeneric(events, fields); - // Collect the per service-element sizing information from the deployment configuration and the (typed/generic) // event bindings. The layout-dependent size algorithm itself lives next to ServiceDataStorage // (CalculateServiceDataStorageShmSize), so that the data-structure and the algorithm reasoning about its memory // footprint stay closely coupled. const auto collect_service_elements = - [this, are_bindings_generic](std::vector& events_and_fields_size_infos, - auto& bindings, - const bool are_fields) { + [this](std::vector& events_and_fields_size_infos, + auto& bindings, + const bool are_fields) { for (const auto& binding : bindings) { const std::size_t number_of_slots = GetNumberOfSampleSlotsFromConfig(binding.first, are_fields); SkeletonEventBindingBase& event_binding = binding.second.get(); - // A generic (type-erased) event/field allocates its slot-array as an EventDataStorage - // (see SkeletonMemoryManager::CreateGenericEventDataInCreatedSharedMemory()): the raw byte count gets - // rounded up to a whole number of std::max_align_t elements and the array is aligned to - // alignof(std::max_align_t). A typed event/field allocates its slot-array as an - // EventDataStorage (see SkeletonMemoryManager::CreateEventDataInCreatedSharedMemory()): - // exactly number_of_slots * sizeof(SampleType) bytes, aligned to alignof(SampleType) - no rounding. - if (are_bindings_generic) - { - const auto& generic_event_binding = static_cast(event_binding); - const auto [sample_size, sample_alignment] = generic_event_binding.GetSizeInfo(); - const std::size_t total_data_size_bytes = number_of_slots * sample_size; - const std::size_t num_max_align_elements = - (total_data_size_bytes + sizeof(std::max_align_t) - 1U) / sizeof(std::max_align_t); - const std::size_t slot_array_size = num_max_align_elements * sizeof(std::max_align_t); - - events_and_fields_size_infos.emplace_back(slot_array_size, alignof(std::max_align_t)); - } - else - { - const std::size_t slot_array_size = number_of_slots * event_binding.GetMaxSize(); - events_and_fields_size_infos.emplace_back(slot_array_size, event_binding.GetAlignment()); - } + const std::size_t slot_array_size = number_of_slots * event_binding.GetSizeInfo().Size(); + events_and_fields_size_infos.emplace_back(slot_array_size, event_binding.GetSizeInfo().Alignment()); } }; std::vector events_and_fields_size_infos{}; @@ -925,17 +861,4 @@ EventControl& SkeletonMemoryManager::EmplaceEventControl(const QualityType asil_ return control_qm.first->second; } -EventMetaInfo& SkeletonMemoryManager::EmplaceEventMetaInfo(const ElementFqId element_fq_id, - const memory::DataTypeSizeInfo& sample_meta_info, - void* type_erased_event_data_storage) -{ - auto inserted_meta_info = - storage_->events_metainfo_.emplace(std::piecewise_construct, - std::forward_as_tuple(element_fq_id), - std::forward_as_tuple(sample_meta_info, type_erased_event_data_storage)); - SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD_MESSAGE(inserted_meta_info.second, - "Couldn't register/emplace event-meta-info in data-section."); - return inserted_meta_info.first->second; -} - } // namespace score::mw::com::impl::lola diff --git a/score/mw/com/impl/bindings/lola/skeleton_memory_manager.h b/score/mw/com/impl/bindings/lola/skeleton_memory_manager.h index 3a596f0b9..aed36fe31 100644 --- a/score/mw/com/impl/bindings/lola/skeleton_memory_manager.h +++ b/score/mw/com/impl/bindings/lola/skeleton_memory_manager.h @@ -70,7 +70,7 @@ class SkeletonMemoryManager final /// /// This function is called by a Skeleton during PrepareOffer in case we aren't in a partial restart case (or we are /// but there are no proxies connected to the old shared memory region). It will create the data and control shared - /// memory regions and will initialise the ServiceDataControl and ServiceDataStorage structures in the created + /// memory regions and will initialize the ServiceDataControl and ServiceDataStorage structures in the created /// shared memory. Result CreateSharedMemory( SkeletonBinding::SkeletonEventBindings& events, @@ -95,25 +95,16 @@ class SkeletonMemoryManager final const SkeletonEventProperties& element_properties) -> std::pair, EventControl*>; - /// \brief Creates an EventDataStorage for a specific event. - /// - /// The EventDataStorage are emplaced into the ServiceDataStorage in the shared memory region that was created with - /// CreateSharedMemory. - template - auto CreateEventDataInCreatedSharedMemory(const ElementFqId element_fq_id, - const SkeletonEventProperties& element_properties) - -> EventDataStorage&; - - /// \brief Creates shared memory storage for a generic (type-erased) event. + /// \brief Creates (type erased) shared memory storage for an event. + /// \details Since the event data storage on binding layer is generally type-erased, this functionality is used for + /// generic events as well as for "normal"/typed events. /// \param element_fq_id The full qualified ID of the element. /// \param element_properties Properties of the event. - /// \param sample_size The size of a single data sample. - /// \param sample_alignment The alignment of the data sample. - /// \return A raw pointer to the first byte of the generic event sample storage. - auto CreateGenericEventDataInCreatedSharedMemory(const ElementFqId element_fq_id, - const SkeletonEventProperties& element_properties, - size_t sample_size, - size_t sample_alignment) -> void*; + /// \param sample_size_info Information about the size and alignment of the data sample. + /// \return A raw pointer to the EventDataStorage. + auto CreateEventDataInCreatedSharedMemory(const ElementFqId element_fq_id, + const SkeletonEventProperties& element_properties, + memory::DataTypeSizeInfo sample_size_info) -> EventDataStorage&; /// \brief Opens an EventControl for QM and optionally for ASIL-B (if the Skeleton is ASIL-B) for a specific /// event that were created by a previous skeleton. @@ -123,19 +114,11 @@ class SkeletonMemoryManager final auto RetrieveEventControlsFromOpenedSharedMemory(const ElementFqId element_fq_id) -> std::pair, EventControl*>; - /// \brief Opens an EventDataStorage for a specific event that was created by a previous skeleton. + /// \brief Returns an EventDataStorage for a specific event that was created by a previous skeleton. /// /// The EventDataStorage are retrieved from the ServiceDataStorage in the shared memory region that was opened with /// OpenExistingSharedMemory. - template - auto RetrieveEventDataFromOpenedSharedMemory(const ElementFqId element_fq_id) -> EventDataStorage&; - - /// \brief Retrieves the raw event sample storage pointer for a generic event from opened shared memory. - /// - /// Generic events use EventMetaInfo as the stable type-erased contract. No interpretation to a - /// DynamicArray takes place in this case. - auto RetrieveGenericEventDataFromOpenedSharedMemory(const ElementFqId element_fq_id, - const SkeletonEventProperties& element_properties) -> void*; + auto RetrieveEventDataFromOpenedSharedMemory(const ElementFqId element_fq_id) -> EventDataStorage&; /// \brief Rolls back any existing operations in the TransactionLog corresponding to a SkeletonEvent /// @@ -233,14 +216,6 @@ class SkeletonMemoryManager final ElementFqId element_fq_id, const SkeletonEventProperties& element_properties); - template - EventDataStorage& EmplaceEventDataStorage(const ElementFqId element_fq_id, - const SkeletonEventProperties& element_properties); - - EventMetaInfo& EmplaceEventMetaInfo(const ElementFqId element_fq_id, - const memory::DataTypeSizeInfo& sample_meta_info, - void* type_erased_event_data_storage); - QualityType quality_type_; const IShmPathBuilder& shm_path_builder_; const LolaServiceInstanceDeployment& lola_service_instance_deployment_; @@ -268,82 +243,6 @@ class SkeletonMemoryManager final std::shared_ptr control_asil_resource_; }; -template -// Suppress "AUTOSAR C++14 M3-2-2": ODR (One Definition Rule) must not be violated. -// Justification: The "Skeleton" is a template class with its declaration and definition in different places within the -// same header file, it does not violate the One Definition Rule. -// Suppress "AUTOSAR C++14 A15-5-3": std::terminate() should not be called implicitly. -// Justification: This is a false positive, no way to throw std::bad_variant_access. -// coverity[autosar_cpp14_m3_2_2_violation] -// coverity[autosar_cpp14_a15_5_3_violation : FALSE] -auto SkeletonMemoryManager::CreateEventDataInCreatedSharedMemory(const ElementFqId element_fq_id, - const SkeletonEventProperties& element_properties) - -> EventDataStorage& -{ - auto& event_data_storage = EmplaceEventDataStorage(element_fq_id, element_properties); - - constexpr memory::DataTypeSizeInfo sample_meta_info{sizeof(SampleType), alignof(SampleType)}; - auto* const event_data_raw_array = event_data_storage.data(); - score::cpp::ignore = EmplaceEventMetaInfo(element_fq_id, sample_meta_info, event_data_raw_array); - - return event_data_storage; -} - -template -// Suppress "AUTOSAR C++14 M3-2-2": -// Justification: Same justification as above. -// Suppress "AUTOSAR C++14 A15-5-3": -// Justification: No way for 'OffsetPtr::get()' which called from 'event_data_storage_it->second.template' to throw an -// exception but we can't mark 'OffsetPtr::get()' as ''. -// coverity[autosar_cpp14_m3_2_2_violation] -// coverity[autosar_cpp14_a15_5_3_violation] -auto SkeletonMemoryManager::RetrieveEventDataFromOpenedSharedMemory(const ElementFqId element_fq_id) - -> EventDataStorage& -{ - // Suppress "AUTOSAR C++14 A15-5-3": - // Justification: This is a false positive, std::less which is used by std::map::find could throw an exception if - // the key value is not comparable and in our case the key is comparable. so no way for 'event_controls_.find()' to - // throw an exception. - // coverity[autosar_cpp14_a15_5_3_violation : FALSE] - auto find_element = [](auto& map, const ElementFqId& target_element_fq_id) -> auto { - const auto it = map.find(target_element_fq_id); - SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD_MESSAGE(it != map.cend(), "Could not find element fq id in map"); - return it; - }; - - score::cpp::ignore = find_element(storage_->events_metainfo_, element_fq_id); - const auto event_data_storage_it = find_element(storage_->events_, element_fq_id); - - // Suppress "AUTOSAR C++14 A5-3-2": Don't dereference null pointers. - // Justification: The "event_data_storage_it" variable is an iterator of interprocess map returned by the - // "find_element" method. A check is made that the iterator is not equal to map.cend(). Therefore, the call to - // "event_data_storage_it->" does not return nullptr. - // coverity[autosar_cpp14_a5_3_2_violation] - auto* const typed_event_data_storage_ptr = - event_data_storage_it->second.template get>(); - SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD_MESSAGE(typed_event_data_storage_ptr != nullptr, - "Could not get EventDataStorage*"); - - return *typed_event_data_storage_ptr; -} - -template -EventDataStorage& SkeletonMemoryManager::EmplaceEventDataStorage( - const ElementFqId element_fq_id, - const SkeletonEventProperties& element_properties) -{ - auto* typed_event_data_storage_ptr = storage_resource_->construct>( - element_properties.GetTotalNumberOfSlots(), - memory::shared::PolymorphicOffsetPtrAllocator(*storage_resource_)); - - auto inserted_data_slots = storage_->events_.emplace(std::piecewise_construct, - std::forward_as_tuple(element_fq_id), - std::forward_as_tuple(typed_event_data_storage_ptr)); - SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD_MESSAGE(inserted_data_slots.second, - "Couldn't register/emplace event-storage in data-section."); - return *typed_event_data_storage_ptr; -} - } // namespace score::mw::com::impl::lola #endif // SCORE_MW_COM_IMPL_BINDINGS_LOLA_SKELETON_MEMORY_MANAGER_H diff --git a/score/mw/com/impl/bindings/lola/skeleton_test.cpp b/score/mw/com/impl/bindings/lola/skeleton_test.cpp index 65e58d4aa..fc2f79ad8 100644 --- a/score/mw/com/impl/bindings/lola/skeleton_test.cpp +++ b/score/mw/com/impl/bindings/lola/skeleton_test.cpp @@ -926,7 +926,7 @@ class SkeletonRegisterParamaterisedFixture : public SkeletonTestMockedSharedMemo { public: EventDataControlComposite<> GetEventDataControlCompositeFromRegistrationResult( - const Skeleton::RegistrationResult& registration_result) + const Skeleton::RegistrationResult& registration_result) { event_control_qm_local_view_.emplace(registration_result.event_control_qm.data_control); @@ -962,7 +962,8 @@ TEST_P(SkeletonRegisterParamaterisedFixture, RegisterWillCreateEventDataIfShmReg test::kFooEventId, test::kDefaultLolaInstanceId, ServiceElementType::EVENT}; - auto registration_result = skeleton_->Register(element_fq_id, test::kDefaultEventProperties); + auto registration_result = + skeleton_->Register(element_fq_id, test::kDefaultEventProperties, test::kTestSampleTypeSizeInfo); // Then the Register call should return pointers to the created control (qm and asil-b) and data sections which can // be used to allocate slots @@ -995,7 +996,7 @@ TEST_P(SkeletonRegisterParamaterisedFixture, RegisterWillOpenEventDataIfShmRegio // when the event is registered with the skeleton auto registration_result = - skeleton_->Register(test::kDummyElementFqId, test::kDefaultEventProperties); + skeleton_->Register(test::kDummyElementFqId, test::kDefaultEventProperties, test::kTestSampleTypeSizeInfo); // Then the Register call should return views to the opened control section and pointer to the opened data section // in the opened shared memory region (we check the control section views by allocating a slot in the opened region @@ -1004,8 +1005,8 @@ TEST_P(SkeletonRegisterParamaterisedFixture, RegisterWillOpenEventDataIfShmRegio GetEventControlFromServiceDataControl(test::kDummyElementFqId, *existing_service_data_control_qm_); auto& existing_event_control_asil_b = GetEventControlFromServiceDataControl(test::kDummyElementFqId, *existing_service_data_control_b_); - auto& existing_event_data_storage = GetEventStorageFromServiceDataStorage( - test::kDummyElementFqId, existing_service_data_storage_); + auto& existing_event_data_storage = + GetEventStorageFromServiceDataStorage(test::kDummyElementFqId, existing_service_data_storage_); ProviderEventDataControlLocalView<> opened_event_control_local_qm = registration_result.event_control_qm.data_control; @@ -1056,7 +1057,7 @@ TEST_P(SkeletonRegisterParamaterisedFixture, RollbackWillBeCalledOnQmTransaction // when the event is registered with the skeleton score::cpp::ignore = - skeleton_->Register(test::kDummyElementFqId, test::kDefaultEventProperties); + skeleton_->Register(test::kDummyElementFqId, test::kDefaultEventProperties, test::kTestSampleTypeSizeInfo); // Then the TransactionLog should be rollbacked during construction and removed EXPECT_FALSE(IsSkeletonTransactionLogRegistered(transaction_log_set)); @@ -1094,7 +1095,7 @@ TEST_P(SkeletonRegisterParamaterisedFixture, RollbackWillBeCalledOnAsilBTransact // when the event is registered with the skeleton score::cpp::ignore = - skeleton_->Register(test::kDummyElementFqId, test::kDefaultEventProperties); + skeleton_->Register(test::kDummyElementFqId, test::kDefaultEventProperties, test::kTestSampleTypeSizeInfo); // Then the TransactionLog should be rollbacked during construction and removed EXPECT_FALSE(IsSkeletonTransactionLogRegistered(transaction_log_set)); @@ -1138,7 +1139,7 @@ TEST_P(SkeletonRegisterParamaterisedFixture, TracingWillBeDisabledAndTransaction // when the event is registered with the skeleton score::cpp::ignore = - skeleton_->Register(test::kDummyElementFqId, test::kDefaultEventProperties); + skeleton_->Register(test::kDummyElementFqId, test::kDefaultEventProperties, test::kTestSampleTypeSizeInfo); // Then the TransactionLog should still exist as it was not removed due to the rollback failing EXPECT_TRUE(IsSkeletonTransactionLogRegistered(transaction_log_set)); @@ -1175,11 +1176,15 @@ TEST_P(SkeletonRegisterParamaterisedFixture, ValidEventDataSlotsExistAfterEventI const auto* const lola_service_type_deployment = GetLolaServiceTypeDeployment(test::kValidMinimalTypeDeployment); ElementFqId element_fq_id{ lola_service_type_deployment->service_id_, test::kFooEventId, test::kDefaultLolaInstanceId, element_type}; - auto event_reg_result = skeleton_->Register(element_fq_id, test::kDefaultEventProperties); + auto event_reg_result = + skeleton_->Register(element_fq_id, test::kDefaultEventProperties, test::kTestSampleTypeSizeInfo); // Then a valid slot-vector with the right size exists and we can access/write to it: - ASSERT_EQ(event_reg_result.event_data_storage.size(), test::kMaxSlots); - event_reg_result.event_data_storage.at(3) = 0x42; + ASSERT_EQ(event_reg_result.event_data_storage.GetNumberOfSlots(), test::kMaxSlots); + auto* type_erased_slot_ptr = + event_reg_result.event_data_storage.GetTypeErasedDataSlot(3, test::kTestSampleTypeSizeInfo.Size()); + test::TestSampleType test_value = 42U; + memcpy(type_erased_slot_ptr, &test_value, test::kTestSampleTypeSizeInfo.Size()); CleanUpSkeleton(); } @@ -1214,7 +1219,8 @@ TEST_P(SkeletonRegisterParamaterisedFixture, CanAllocateSlotAfterEventIsRegister const auto* const lola_service_type_deployment = GetLolaServiceTypeDeployment(test::kValidMinimalTypeDeployment); ElementFqId element_fq_id{ lola_service_type_deployment->service_id_, test::kFooEventId, test::kDefaultLolaInstanceId, element_type}; - auto event_reg_result = skeleton_->Register(element_fq_id, test::kDefaultEventProperties); + auto event_reg_result = + skeleton_->Register(element_fq_id, test::kDefaultEventProperties, test::kTestSampleTypeSizeInfo); // Then we can allocate and free slots on that event auto event_data_control_composite = GetEventDataControlCompositeFromRegistrationResult(event_reg_result); @@ -1251,7 +1257,8 @@ TEST_P(SkeletonRegisterParamaterisedFixture, AllocateAfterCleanUp) const auto* const lola_service_type_deployment = GetLolaServiceTypeDeployment(test::kValidMinimalTypeDeployment); ElementFqId element_fq_id{ lola_service_type_deployment->service_id_, test::kFooEventId, test::kDefaultLolaInstanceId, element_type}; - auto event_reg_result = skeleton_->Register(element_fq_id, test::kDefaultEventProperties); + auto event_reg_result = + skeleton_->Register(element_fq_id, test::kDefaultEventProperties, test::kTestSampleTypeSizeInfo); auto event_data_control_composite = GetEventDataControlCompositeFromRegistrationResult(event_reg_result); auto allocation = event_data_control_composite.AllocateNextSlot(); @@ -1345,15 +1352,16 @@ TEST_P(SkeletonRegisterParamaterisedFixture, ValidEventMetaInfoExistAfterEventIs const auto* const lola_service_type_deployment = GetLolaServiceTypeDeployment(test::kValidMinimalTypeDeployment); ElementFqId foo_element_fq_id{ lola_service_type_deployment->service_id_, test::kFooEventId, test::kDefaultLolaInstanceId, element_type}; - auto foo_event_reg_result = skeleton_->Register(foo_element_fq_id, test::kDefaultEventProperties); - void* const foo_event_data_storage = foo_event_reg_result.event_data_storage.data(); + std::ignore = skeleton_->Register(foo_element_fq_id, + test::kDefaultEventProperties, + memory::DataTypeSizeInfo{sizeof(std::uint8_t), alignof(std::uint8_t)}); // and dumb_event is registered with the skeleton with 5 slots ElementFqId dumb_element_fq_id{ lola_service_type_deployment->service_id_, test::kDumbEventId, test::kDefaultLolaInstanceId, element_type}; - auto dumb_event_reg_result = - skeleton_->Register(dumb_element_fq_id, test::kDefaultEventProperties); - void* const dumb_event_data_storage = dumb_event_reg_result.event_data_storage.data(); + std::ignore = skeleton_->Register(dumb_element_fq_id, + test::kDefaultEventProperties, + memory::DataTypeSizeInfo{sizeof(VeryComplexType), alignof(VeryComplexType)}); // Expect, that we can then retrieve the meta-info of the registered events SkeletonAttorney skeleton_test_attorney{*skeleton_}; @@ -1372,24 +1380,6 @@ TEST_P(SkeletonRegisterParamaterisedFixture, ValidEventMetaInfoExistAfterEventIs ASSERT_EQ(event_dumb_meta_info_ptr->data_type_info_.Size(), sizeof(VeryComplexType)); ASSERT_EQ(event_dumb_meta_info_ptr->data_type_info_.Alignment(), alignof(VeryComplexType)); - const auto GetEventSlotsArraySize = [](const std::size_t sample_size, - const std::size_t sample_alignment, - const std::size_t number_of_sample_slots) noexcept -> std::size_t { - const auto aligned_size = - memory::shared::CalculateAlignedSize(sample_size, static_cast(sample_alignment)); - return aligned_size * number_of_sample_slots; - }; - - const auto foo_event_slots_size = GetEventSlotsArraySize(event_foo_meta_info_ptr->data_type_info_.Size(), - event_foo_meta_info_ptr->data_type_info_.Alignment(), - test::kDefaultEventProperties.GetTotalNumberOfSlots()); - ASSERT_EQ(event_foo_meta_info_ptr->event_slots_raw_array_.get(foo_event_slots_size), foo_event_data_storage); - - const auto dumb_event_slots_size = GetEventSlotsArraySize(event_foo_meta_info_ptr->data_type_info_.Size(), - event_foo_meta_info_ptr->data_type_info_.Alignment(), - test::kDefaultEventProperties.GetTotalNumberOfSlots()); - ASSERT_EQ(event_dumb_meta_info_ptr->event_slots_raw_array_.get(dumb_event_slots_size), dumb_event_data_storage); - CleanUpSkeleton(); } @@ -1419,7 +1409,9 @@ TEST_P(SkeletonRegisterParamaterisedFixture, NoMetaInfoExistsForInvalidElementId const auto* const lola_service_type_deployment = GetLolaServiceTypeDeployment(test::kValidMinimalTypeDeployment); ElementFqId element_fq_id{ lola_service_type_deployment->service_id_, test::kFooEventId, test::kDefaultLolaInstanceId, element_type}; - skeleton_->Register(element_fq_id, test::kDefaultEventProperties); + skeleton_->Register(element_fq_id, + test::kDefaultEventProperties, + memory::DataTypeSizeInfo{sizeof(std::uint8_t), alignof(std::uint8_t)}); // but when retrieving meta-info for a not registered ElementFqId const std::uint16_t UNKNOWN_EVENT_ID{99U}; @@ -1465,47 +1457,16 @@ TEST_P(SkeletonRegisterParamaterisedFixture, CallingRegisterWithSameServiceEleme ServiceElementType::EVENT}; // When calling register twice with the same ElementFqId - score::cpp::ignore = skeleton_->Register(element_fq_id, test::kDefaultEventProperties); - score::cpp::ignore = skeleton_->Register(element_fq_id, test::kDefaultEventProperties); + score::cpp::ignore = + skeleton_->Register(element_fq_id, test::kDefaultEventProperties, test::kTestSampleTypeSizeInfo); + score::cpp::ignore = + skeleton_->Register(element_fq_id, test::kDefaultEventProperties, test::kTestSampleTypeSizeInfo); }; // Then we should terminate EXPECT_DEATH(test_function(), ".*"); } -TEST_P(SkeletonRegisterParamaterisedFixture, RegisterDiesWhenCalledForGatewayForwardedSkeleton) -{ - const ServiceElementType element_type = GetParam(); - - if (element_type == ServiceElementType::EVENT) - { - events_.emplace(test::kFooEventName, mock_event_binding_); - } - else - { - fields_.emplace(test::kFooEventName, mock_event_binding_); - } - - // Given a gateway-forwarded ASIL-B skeleton deployment (inter_vm_support_ and inter_vm_forwarded_ set) - auto service_instance_deployment = element_type == ServiceElementType::EVENT - ? test::kValidAsilInstanceDeploymentWithEvent - : test::kValidAsilInstanceDeploymentWithField; - auto* const lola_deployment = std::get_if(&service_instance_deployment.bindingInfo_); - ASSERT_NE(lola_deployment, nullptr); - lola_deployment->inter_vm_support_ = true; - lola_deployment->inter_vm_forwarded_ = true; - const auto gateway_instance_identifier = - make_InstanceIdentifier(service_instance_deployment, test::kValidMinimalTypeDeployment); - - // When PrepareOffer is called it opens (not creates) the existing shared memory (use_gateway_forwarded_shm_ = true) - InitialiseSkeleton(gateway_instance_identifier).WithNoConnectedProxy(); - EXPECT_TRUE(skeleton_->PrepareOffer(events_, fields_, std::move(kEmptyRegisterShmObjectTraceCallback)).has_value()); - - // Then calling the typed Register on a gateway-forwarded skeleton must die: - // gateway setups use GenericSkeleton exclusively — typed Register is a programming error in this context. - EXPECT_DEATH(skeleton_->Register(test::kDummyElementFqId, test::kDefaultEventProperties), ""); -} - -TEST_P(SkeletonRegisterParamaterisedFixture, RegisterGenericWillOpenEventDataForGatewayForwardedSkeleton) +TEST_P(SkeletonRegisterParamaterisedFixture, RegisterWillOpenEventDataForGatewayForwardedSkeleton) { const ServiceElementType element_type = GetParam(); @@ -1533,16 +1494,16 @@ TEST_P(SkeletonRegisterParamaterisedFixture, RegisterGenericWillOpenEventDataFor InitialiseSkeleton(gateway_instance_identifier).WithNoConnectedProxy(); EXPECT_TRUE(skeleton_->PrepareOffer(events_, fields_, std::move(kEmptyRegisterShmObjectTraceCallback)).has_value()); - // When RegisterGeneric is called on the gateway-forwarded skeleton - const auto result = skeleton_->RegisterGeneric( - test::kDummyElementFqId, test::kDefaultEventProperties, sizeof(std::uint8_t), alignof(std::uint8_t)); + // When Register is called on the gateway-forwarded skeleton + const auto result = skeleton_->Register(test::kDummyElementFqId, + test::kDefaultEventProperties, + memory::DataTypeSizeInfo{sizeof(std::uint8_t), alignof(std::uint8_t)}); - // Then it returns valid pointers to the opened SHM data and event controls (not newly created) - EXPECT_NE(result.type_erased_event_data_storage_ptr, nullptr); + // Then it returns valid pointer for the ASIL-B control. EXPECT_NE(result.event_control_asil_b, nullptr); } -TEST_P(SkeletonRegisterParamaterisedFixture, RegisterGenericWillOpenEventDataForReopenedSkeleton) +TEST_P(SkeletonRegisterParamaterisedFixture, RegisterWillOpenEventDataForReopenedSkeleton) { const ServiceElementType element_type = GetParam(); @@ -1563,52 +1524,15 @@ TEST_P(SkeletonRegisterParamaterisedFixture, RegisterGenericWillOpenEventDataFor InitialiseSkeleton(instance_identifier).WithAlreadyConnectedProxy(); EXPECT_TRUE(skeleton_->PrepareOffer(events_, fields_, std::move(kEmptyRegisterShmObjectTraceCallback)).has_value()); - // When RegisterGeneric is called on the reopened skeleton - const auto result = skeleton_->RegisterGeneric( - test::kDummyElementFqId, test::kDefaultEventProperties, sizeof(std::uint8_t), alignof(std::uint8_t)); + // When Register is called on the reopened skeleton + const auto result = skeleton_->Register(test::kDummyElementFqId, + test::kDefaultEventProperties, + memory::DataTypeSizeInfo{sizeof(std::uint8_t), alignof(std::uint8_t)}); - // Then it returns valid pointers to the opened (not newly created) SHM data and event controls - EXPECT_NE(result.type_erased_event_data_storage_ptr, nullptr); + // Then it returns valid pointer to the opened (not newly created) SHM ASIL-B event control EXPECT_NE(result.event_control_asil_b, nullptr); } -TEST_P(SkeletonRegisterParamaterisedFixture, RegisterGenericWillRollbackTransactionLogForReopenedSkeleton) -{ - // Given a QM ServiceDataControl which contains a TransactionLogSet with valid transactions - auto proxy_event_data_control_qm_local = GetConsumerEventDataControlLocalFromServiceDataControl( - test::kDummyElementFqId, *existing_service_data_control_qm_); - auto& transaction_log_set = - GetTransactionLogSetFromServiceDataControl(test::kDummyElementFqId, *existing_service_data_control_qm_); - InsertSkeletonTransactionLogWithValidTransactions(proxy_event_data_control_qm_local, transaction_log_set); - EXPECT_TRUE(IsSkeletonTransactionLogRegistered(transaction_log_set)); - - const ServiceElementType element_type = GetParam(); - - if (element_type == ServiceElementType::EVENT) - { - events_.emplace(test::kFooEventName, mock_event_binding_); - } - else - { - fields_.emplace(test::kFooEventName, mock_event_binding_); - } - const InstanceIdentifier instance_identifier{element_type == ServiceElementType::EVENT - ? GetValidInstanceIdentifierWithEvent() - : GetValidInstanceIdentifierWithField()}; - - // Given a Skeleton with an already-connected proxy: flock of usage marker file fails, - // causing PrepareOffer to go through kReuseExistingShm and set was_old_shm_region_reopened_ = true - InitialiseSkeleton(instance_identifier).WithAlreadyConnectedProxy(); - EXPECT_TRUE(skeleton_->PrepareOffer(events_, fields_, std::move(kEmptyRegisterShmObjectTraceCallback)).has_value()); - - // When RegisterGeneric is called on the reopened skeleton - score::cpp::ignore = skeleton_->RegisterGeneric( - test::kDummyElementFqId, test::kDefaultEventProperties, sizeof(std::uint8_t), alignof(std::uint8_t)); - - // Then the QM TransactionLog should be rolled back and unregistered - EXPECT_FALSE(IsSkeletonTransactionLogRegistered(transaction_log_set)); -} - INSTANTIATE_TEST_SUITE_P(SkeletonRegisterParamaterisedFixture, SkeletonRegisterParamaterisedFixture, Values(ServiceElementType::EVENT, ServiceElementType::FIELD)); diff --git a/score/mw/com/impl/bindings/lola/test/proxy_event_test_resources.cpp b/score/mw/com/impl/bindings/lola/test/proxy_event_test_resources.cpp index d9c1a7051..c10a97ec4 100644 --- a/score/mw/com/impl/bindings/lola/test/proxy_event_test_resources.cpp +++ b/score/mw/com/impl/bindings/lola/test/proxy_event_test_resources.cpp @@ -71,7 +71,7 @@ ProxyMockedMemoryFixture::ProxyMockedMemoryFixture() noexcept ON_CALL(*(fake_data_->control_memory), getUsableBaseAddress()) .WillByDefault(Return(static_cast(fake_data_->data_control))); - ON_CALL(*(fake_data_->data_memory), getUsableBaseAddress()) + ON_CALL(*(fake_data_->data_memory_resource), getUsableBaseAddress()) .WillByDefault(Return(static_cast(fake_data_->data_storage))); ON_CALL(binding_runtime_, GetRollbackSynchronization()).WillByDefault(ReturnRef(rollback_synchronization_)); @@ -89,7 +89,7 @@ void ProxyMockedMemoryFixture::ExpectDataSegmentOpened() { ON_CALL(shared_memory_factory_mock_guard_.mock_, Open(StartsWith(kDataChannelPrefix), false, _)) .WillByDefault(InvokeWithoutArgs([this]() -> std::shared_ptr { - return fake_data_->data_memory; + return fake_data_->data_memory_resource; })); } @@ -144,7 +144,7 @@ void ProxyMockedMemoryFixture::InitialiseProxyWithConstructor(const InstanceIden Proxy::EventNameToElementFqIdConverter event_name_to_element_fq_id_converter{lola_service_deployment_, lola_service_instance_id_.GetId()}; proxy_ = std::make_unique(fake_data_->control_memory, - fake_data_->data_memory, + fake_data_->data_memory_resource, service_quality_type_, std::move(event_name_to_element_fq_id_converter), make_HandleType(instance_identifier), @@ -170,8 +170,6 @@ void ProxyMockedMemoryFixture::InitialiseDummySkeletonEvent(const ElementFqId el fake_data_->AddEvent(element_fq_id, skeleton_event_properties); SCORE_LANGUAGE_FUTURECPP_ASSERT(event_control_ != nullptr); SCORE_LANGUAGE_FUTURECPP_ASSERT(event_data_storage_ != nullptr); - event_slots_raw_array_ = event_data_storage_->data(); - SCORE_LANGUAGE_FUTURECPP_ASSERT(event_slots_raw_array_ != nullptr); consumer_event_data_control_local_.emplace(event_control_->data_control); provider_event_data_control_local_.emplace(event_control_->data_control); } @@ -235,7 +233,8 @@ SlotIndexType LolaProxyEventResources::PutData(const std::uint32_t value, auto slot_result = provider_event_data_control_local_->AllocateNextSlot(); EXPECT_TRUE(slot_result.has_value()); auto slot_index = slot_result.value(); - event_data_storage_->at(slot_index) = value; + auto* storage_slot = event_data_storage_->GetTypeErasedDataSlot(slot_index, sizeof(value)); + memcpy(storage_slot, &value, sizeof(value)); provider_event_data_control_local_->EventReady(slot_index, timestamp); return slot_index; } diff --git a/score/mw/com/impl/bindings/lola/test/proxy_event_test_resources.h b/score/mw/com/impl/bindings/lola/test/proxy_event_test_resources.h index af358c02d..64b7aca7a 100644 --- a/score/mw/com/impl/bindings/lola/test/proxy_event_test_resources.h +++ b/score/mw/com/impl/bindings/lola/test/proxy_event_test_resources.h @@ -218,8 +218,7 @@ class ProxyMockedMemoryFixture : public ::testing::Test EventControl* event_control_{nullptr}; std::optional> provider_event_data_control_local_{}; std::optional> consumer_event_data_control_local_{}; - EventDataStorage* event_data_storage_{nullptr}; - void* event_slots_raw_array_{nullptr}; + EventDataStorage* event_data_storage_{nullptr}; RollbackSynchronization rollback_synchronization_{}; std::shared_ptr mock_service_{std::make_shared()}; diff --git a/score/mw/com/impl/bindings/lola/test/skeleton_component_test.cpp b/score/mw/com/impl/bindings/lola/test/skeleton_component_test.cpp index d31335aab..fab8638fb 100644 --- a/score/mw/com/impl/bindings/lola/test/skeleton_component_test.cpp +++ b/score/mw/com/impl/bindings/lola/test/skeleton_component_test.cpp @@ -55,6 +55,7 @@ constexpr auto control_shm = "/dev/shm/lola-ctl-0000000000000001-00016"; constexpr auto asil_control_shm = "/dev/shm/lola-ctl-0000000000000001-00016-b"; #endif +const memory::DataTypeSizeInfo kSampleTypeSizeInfo{sizeof(TestSampleType), alignof(TestSampleType)}; const auto kInstanceSpecifier = InstanceSpecifier::Create(std::string{"abc/abc/TirePressurePort"}).value(); constexpr std::size_t kNumberOfSlots{10U}; const SkeletonEventProperties kEventProperties{kNumberOfSlots, 0U, 0U, false, 10U, true}; @@ -150,8 +151,10 @@ class SkeletonComponentTestFixture : public ::testing::Test ON_CALL(lola_runtime_mock_, GetLolaMessaging()).WillByDefault(ReturnRef(message_passing_service_mock_)); ON_CALL(runtime_mock_, GetTracingRuntime()).WillByDefault(Return(&tracing_runtime_mock_)); - ON_CALL(mock_event_binding_, GetMaxSize()).WillByDefault(Return(sizeof(TestSampleType))); - ON_CALL(mock_field_binding_, GetMaxSize()).WillByDefault(Return(sizeof(TestSampleType))); + ON_CALL(mock_event_binding_, GetSizeInfo()) + .WillByDefault(Return(memory::DataTypeSizeInfo{sizeof(TestSampleType), alignof(TestSampleType)})); + ON_CALL(mock_field_binding_, GetSizeInfo()) + .WillByDefault(Return(memory::DataTypeSizeInfo{sizeof(TestSampleType), alignof(TestSampleType)})); } void TearDown() override @@ -243,13 +246,13 @@ class SkeletonComponentTestFixture : public ::testing::Test ON_CALL(mock_event_binding_, PrepareOffer()).WillByDefault(testing::Invoke([&skeleton]() -> Result { const ElementFqId element_fq_id{ test::kLolaServiceId, test::kFooEventId, test::kDefaultLolaInstanceId, ServiceElementType::EVENT}; - skeleton.Register(element_fq_id, kEventProperties); + skeleton.Register(element_fq_id, kEventProperties, kSampleTypeSizeInfo); return {}; })); ON_CALL(mock_field_binding_, PrepareOffer()).WillByDefault(testing::Invoke([&skeleton]() -> Result { const ElementFqId element_fq_id{ test::kLolaServiceId, test::kFooFieldId, test::kDefaultLolaInstanceId, ServiceElementType::FIELD}; - skeleton.Register(element_fq_id, kEventProperties); + skeleton.Register(element_fq_id, kEventProperties, kSampleTypeSizeInfo); return {}; })); } @@ -579,11 +582,6 @@ TEST_F(SkeletonComponentTestFixture, ShmObjectSizeCalc_Analysis_Data_QM) // Expect, that the LoLa runtime returns that ShmSize calculation shall be done via (analytic) estimation EXPECT_CALL(lola_runtime_mock_, GetShmSizeCalculationMode()).WillOnce(Return(ShmSizeCalculationMode::kAnalysis)); - // The analytic size calculation queries the maximum sample-size of each event/field binding. Since the events are - // registered below as uint8_t, GetMaxSize() has to report the matching size. - ON_CALL(mock_event_binding_, GetMaxSize()).WillByDefault(Return(sizeof(std::uint8_t))); - ON_CALL(mock_field_binding_, GetMaxSize()).WillByDefault(Return(sizeof(std::uint8_t))); - // Expecting that the event and field are registered RegisterEventAndFieldOnPrepareOffer(*unit); @@ -628,11 +626,6 @@ TEST_F(SkeletonComponentTestFixture, ShmObjectSizeCalc_Analysis_Control_QM) // Expect, that the LoLa runtime returns that ShmSize calculation shall be done via (analytic) estimation EXPECT_CALL(lola_runtime_mock_, GetShmSizeCalculationMode()).WillOnce(Return(ShmSizeCalculationMode::kAnalysis)); - // The analytic size calculation queries the maximum sample-size of each event/field binding. Since the events are - // registered below as uint8_t, GetMaxSize() has to report the matching size. - ON_CALL(mock_event_binding_, GetMaxSize()).WillByDefault(Return(sizeof(std::uint8_t))); - ON_CALL(mock_field_binding_, GetMaxSize()).WillByDefault(Return(sizeof(std::uint8_t))); - // Expecting that the event and field are registered RegisterEventAndFieldOnPrepareOffer(*unit); diff --git a/score/mw/com/impl/bindings/lola/test/skeleton_event_component_test.cpp b/score/mw/com/impl/bindings/lola/test/skeleton_event_component_test.cpp index 1f39ffea8..b2307e7b8 100644 --- a/score/mw/com/impl/bindings/lola/test/skeleton_event_component_test.cpp +++ b/score/mw/com/impl/bindings/lola/test/skeleton_event_component_test.cpp @@ -135,7 +135,7 @@ class SkeletonEventComponentTestTemplateFixture : public ::testing::Test score::memory::shared::SharedMemoryFactory::Open(path_builder.GetDataChannelShmName(instance_id), false); auto* storage = static_cast(memory->getUsableBaseAddress()); - auto* values = storage->events_.at(fake_element_fq_id_).template get>(); + auto* event_data_storage = storage->events_.at(fake_element_fq_id_).get(); auto path = path_builder.GetControlChannelShmName(instance_id, QualityType::kASIL_QM); auto memory_control = score::memory::shared::SharedMemoryFactory::Open(path, false); @@ -153,11 +153,12 @@ class SkeletonEventComponentTestTemplateFixture : public ::testing::Test dummy_transaction_log_id, consumer_event_data_control_local); auto slot_index = consumer_event_data_control_local.ReferenceNextEvent(0); EXPECT_TRUE(slot_index.has_value()); - const auto value = values->at(slot_index.value()); + auto* const value = static_cast( + event_data_storage->GetTypeErasedDataSlot(slot_index.value(), sizeof(std::uint32_t))); consumer_event_data_control_local.DereferenceEvent(slot_index.value()); - return value; + return *value; } std::size_t GetFreeSampleSlots() const diff --git a/score/mw/com/impl/bindings/lola/test/skeleton_test_resources.h b/score/mw/com/impl/bindings/lola/test/skeleton_test_resources.h index 248d93326..f52eceffc 100644 --- a/score/mw/com/impl/bindings/lola/test/skeleton_test_resources.h +++ b/score/mw/com/impl/bindings/lola/test/skeleton_test_resources.h @@ -141,6 +141,9 @@ static constexpr std::size_t kConfiguredDeploymentControlAsilBShmSize{1024U}; static constexpr std::size_t kConfiguredDeploymentControlQmShmSize{1024U}; static constexpr LolaServiceInstanceId::InstanceId kDefaultLolaInstanceId{16U}; +static constexpr memory::DataTypeSizeInfo kTestSampleTypeSizeInfo{sizeof(test::TestSampleType), + alignof(test::TestSampleType)}; + static const auto kFooEventName{"fooEvent"}; static const auto kDumbEventName{"dumbEvent"}; static const auto kFooFieldName{"fooField"}; @@ -470,39 +473,33 @@ class SkeletonMockedMemoryFixture : public ::testing::Test template ServiceDataStorage CreateServiceDataStorageWithEvent(ElementFqId element_fq_id) noexcept { + memory::DataTypeSizeInfo sample_size_info{sizeof(SampleType), alignof(SampleType)}; ServiceDataStorage service_data_storage{1U, *data_shared_memory_resource_mock_}; - auto* event_data_storage = data_shared_memory_resource_mock_->construct>( - 10U, *data_shared_memory_resource_mock_); + auto* event_data_storage = data_shared_memory_resource_mock_->construct( + *data_shared_memory_resource_mock_, 10U, sample_size_info); auto inserted_data_slots = service_data_storage.events_.emplace( std::piecewise_construct, std::forward_as_tuple(element_fq_id), std::forward_as_tuple(event_data_storage)); EXPECT_TRUE(inserted_data_slots.second); - const score::memory::DataTypeSizeInfo sample_meta_info{16U, 16U}; - auto* event_data_raw_array = event_data_storage->data(); auto inserted_meta_info = service_data_storage.events_metainfo_.emplace( - std::piecewise_construct, - std::forward_as_tuple(element_fq_id), - std::forward_as_tuple(sample_meta_info, event_data_raw_array)); + std::piecewise_construct, std::forward_as_tuple(element_fq_id), std::forward_as_tuple(sample_size_info)); EXPECT_TRUE(inserted_meta_info.second); return service_data_storage; } - template - EventDataStorage& GetEventStorageFromServiceDataStorage( - ElementFqId element_fq_id, - ServiceDataStorage& service_data_storage) noexcept + EventDataStorage& GetEventStorageFromServiceDataStorage(ElementFqId element_fq_id, + ServiceDataStorage& service_data_storage) noexcept { auto event_data_storage_it = service_data_storage.events_.find(element_fq_id); EXPECT_NE(event_data_storage_it, service_data_storage.events_.cend()); auto event_data_storage_offset_ptr = event_data_storage_it->second; - auto* const typed_event_data_storage = - event_data_storage_offset_ptr.template get>(); - EXPECT_NE(typed_event_data_storage, nullptr); - return *typed_event_data_storage; + auto* const event_data_storage = event_data_storage_offset_ptr.template get(); + EXPECT_NE(event_data_storage, nullptr); + return *event_data_storage; } void CleanUpSkeleton(); diff --git a/score/mw/com/impl/bindings/lola/test_doubles/fake_mocked_service_data.cpp b/score/mw/com/impl/bindings/lola/test_doubles/fake_mocked_service_data.cpp index d373492f2..6a3d2bcca 100644 --- a/score/mw/com/impl/bindings/lola/test_doubles/fake_mocked_service_data.cpp +++ b/score/mw/com/impl/bindings/lola/test_doubles/fake_mocked_service_data.cpp @@ -41,10 +41,12 @@ FakeMockedServiceData::FakeMockedServiceData(const pid_t skeleton_process_pid_in { control_memory = std::make_shared<::testing::NiceMock>(kControlMemoryResourceId); - data_memory = std::make_shared<::testing::NiceMock>(kDataMemoryResourceId); + data_memory_resource = + std::make_shared<::testing::NiceMock>(kDataMemoryResourceId); data_control = control_memory->construct(kMaxNumberOfServiceElements, *control_memory); - data_storage = data_memory->construct(kMaxNumberOfServiceElements, *data_memory); + data_storage = + data_memory_resource->construct(kMaxNumberOfServiceElements, *data_memory_resource); data_storage->skeleton_pid_ = skeleton_process_pid_in; data_storage->skeleton_uid_ = skeleton_uid_in; diff --git a/score/mw/com/impl/bindings/lola/test_doubles/fake_mocked_service_data.h b/score/mw/com/impl/bindings/lola/test_doubles/fake_mocked_service_data.h index 16af6ccd0..3192b77f8 100644 --- a/score/mw/com/impl/bindings/lola/test_doubles/fake_mocked_service_data.h +++ b/score/mw/com/impl/bindings/lola/test_doubles/fake_mocked_service_data.h @@ -40,7 +40,8 @@ struct FakeMockedServiceData ServiceDataControl* data_control{nullptr}; ServiceDataStorage* data_storage{nullptr}; std::shared_ptr<::testing::NiceMock> control_memory{nullptr}; - std::shared_ptr<::testing::NiceMock> data_memory{nullptr}; + std::shared_ptr<::testing::NiceMock> data_memory_resource{ + nullptr}; /// Add a new event to the event structures inside the shared memory regions. /// @@ -50,12 +51,11 @@ struct FakeMockedServiceData /// \param max_subscribers maximum number of subscribers /// \return A tuple that points to the newly initialized event-specific data structures. template - std::tuple*> AddEvent(ElementFqId id, - SkeletonEventProperties event_properties); + std::tuple AddEvent(ElementFqId id, SkeletonEventProperties event_properties); }; template -inline std::tuple*> FakeMockedServiceData::AddEvent( +inline std::tuple FakeMockedServiceData::AddEvent( const ElementFqId id, const SkeletonEventProperties event_properties) { @@ -72,20 +72,17 @@ inline std::tuple*> FakeMockedServic *control_memory)); auto& event_control = std::get(*inserted_control); - EventDataStorage* event_data_slots = - data_memory->construct>(total_number_of_slots, *data_memory); - const memory::shared::OffsetPtr rel_event_data_buffer{static_cast(event_data_slots)}; - data_storage->events_.emplace(id, rel_event_data_buffer); + const score::memory::DataTypeSizeInfo data_type_size_info{sizeof(SampleType), alignof(SampleType)}; + auto* event_data_storage = data_memory_resource->construct( + *data_memory_resource, static_cast(total_number_of_slots), data_type_size_info); + const memory::shared::OffsetPtr event_data_storage_offset_ptr{event_data_storage}; + data_storage->events_.emplace(id, event_data_storage_offset_ptr); - const score::memory::DataTypeSizeInfo sample_meta_info{sizeof(SampleType), alignof(SampleType)}; - auto* event_data_raw_array = event_data_slots->data(); - const auto inserted_meta_info = - data_storage->events_metainfo_.emplace(std::piecewise_construct, - std::forward_as_tuple(id), - std::forward_as_tuple(sample_meta_info, event_data_raw_array)); + const auto inserted_meta_info = data_storage->events_metainfo_.emplace( + std::piecewise_construct, std::forward_as_tuple(id), std::forward_as_tuple(data_type_size_info)); SCORE_LANGUAGE_FUTURECPP_ASSERT(inserted_meta_info.second); - return std::make_tuple(&event_control, event_data_slots); + return std::make_tuple(&event_control, event_data_storage); } } // namespace score::mw::com::impl::lola diff --git a/score/mw/com/impl/bindings/lola/test_doubles/fake_service_data.cpp b/score/mw/com/impl/bindings/lola/test_doubles/fake_service_data.cpp index 10e05b896..4b34655c8 100644 --- a/score/mw/com/impl/bindings/lola/test_doubles/fake_service_data.cpp +++ b/score/mw/com/impl/bindings/lola/test_doubles/fake_service_data.cpp @@ -86,7 +86,7 @@ FakeServiceData::FakeServiceData(const std::string& control_file_name, }, 65535U); - data_memory = score::memory::shared::SharedMemoryFactory::Create( + data_memory_resource = score::memory::shared::SharedMemoryFactory::Create( data_file_name, [this, initialise_skeleton_data, skeleton_process_pid_in, skeleton_uid_in]( std::shared_ptr memory_resource) { diff --git a/score/mw/com/impl/bindings/lola/test_doubles/fake_service_data.h b/score/mw/com/impl/bindings/lola/test_doubles/fake_service_data.h index 0ef393001..c16fb6a6f 100644 --- a/score/mw/com/impl/bindings/lola/test_doubles/fake_service_data.h +++ b/score/mw/com/impl/bindings/lola/test_doubles/fake_service_data.h @@ -63,7 +63,7 @@ struct FakeServiceData ServiceDataControl* data_control{nullptr}; ServiceDataStorage* data_storage{nullptr}; std::shared_ptr control_memory{nullptr}; - std::shared_ptr data_memory{nullptr}; + std::shared_ptr data_memory_resource{nullptr}; const std::string control_path; const std::string data_path; score::filesystem::Filesystem filesystem; @@ -78,13 +78,12 @@ struct FakeServiceData /// \param max_subscribers maximum number of subscribers /// \return A tuple that points to the newly initialized event-specific data structures. template - std::tuple*> AddEvent( - ElementFqId id, - SkeletonEventProperties event_properties) noexcept; + std::tuple AddEvent(ElementFqId id, + SkeletonEventProperties event_properties) noexcept; }; template -inline std::tuple*> FakeServiceData::AddEvent( +inline std::tuple FakeServiceData::AddEvent( const ElementFqId id, const SkeletonEventProperties event_properties) noexcept { @@ -100,19 +99,16 @@ inline std::tuple*> FakeServiceData: *control_memory)); auto& event_control = std::get(*inserted_control); - EventDataStorage* event_data_slots = - data_memory->construct>(total_number_of_slots, *data_memory); - const memory::shared::OffsetPtr rel_event_data_buffer{static_cast(event_data_slots)}; - data_storage->events_.emplace(id, rel_event_data_buffer); + const memory::DataTypeSizeInfo data_type_size_info{sizeof(SampleType), alignof(SampleType)}; + auto* event_data_storage = data_memory_resource->construct( + *data_memory_resource, static_cast(total_number_of_slots), data_type_size_info); + const memory::shared::OffsetPtr event_data_storage_offset_ptr{event_data_storage}; + data_storage->events_.emplace(id, event_data_storage_offset_ptr); - const score::memory::DataTypeSizeInfo sample_meta_info{sizeof(SampleType), alignof(SampleType)}; - auto* event_data_raw_array = event_data_slots->data(); - const auto inserted_meta_info = - data_storage->events_metainfo_.emplace(std::piecewise_construct, - std::forward_as_tuple(id), - std::forward_as_tuple(sample_meta_info, event_data_raw_array)); + const auto inserted_meta_info = data_storage->events_metainfo_.emplace( + std::piecewise_construct, std::forward_as_tuple(id), std::forward_as_tuple(data_type_size_info)); SCORE_LANGUAGE_FUTURECPP_ASSERT(inserted_meta_info.second); - return std::make_tuple(&event_control, event_data_slots); + return std::make_tuple(&event_control, event_data_storage); } } // namespace score::mw::com::impl::lola diff --git a/score/mw/com/impl/bindings/mock_binding/generic_skeleton_event.h b/score/mw/com/impl/bindings/mock_binding/generic_skeleton_event.h index 384d1a897..56875252d 100644 --- a/score/mw/com/impl/bindings/mock_binding/generic_skeleton_event.h +++ b/score/mw/com/impl/bindings/mock_binding/generic_skeleton_event.h @@ -36,13 +36,11 @@ class GenericSkeletonEvent : public GenericSkeletonEventBinding MOCK_METHOD(Result, Notify, (), (noexcept, override)); - MOCK_METHOD((std::pair), GetSizeInfo, (), (const, noexcept, override)); MOCK_METHOD(Result, PrepareOffer, (), (noexcept, override)); MOCK_METHOD(void, PrepareStopOffer, (), (noexcept, override)); MOCK_METHOD(BindingType, GetBindingType, (), (const, noexcept, override)); MOCK_METHOD(void, SetSkeletonEventTracingData, (impl::tracing::SkeletonEventTracingData), (noexcept, override)); - MOCK_METHOD(std::size_t, GetMaxSize, (), (const, noexcept, override)); - MOCK_METHOD(std::size_t, GetAlignment, (), (const, noexcept, override)); + MOCK_METHOD(memory::DataTypeSizeInfo, GetSizeInfo, (), (const, noexcept, override)); MOCK_METHOD(Result, SetReceiveHandlerRegistrationChangedHandler, (ReceiveHandlerRegistrationChangedCallback), diff --git a/score/mw/com/impl/bindings/mock_binding/skeleton_event.h b/score/mw/com/impl/bindings/mock_binding/skeleton_event.h index 8c61f76dd..04880d844 100644 --- a/score/mw/com/impl/bindings/mock_binding/skeleton_event.h +++ b/score/mw/com/impl/bindings/mock_binding/skeleton_event.h @@ -29,8 +29,7 @@ class SkeletonEventBase : public SkeletonEventBindingBase public: MOCK_METHOD(Result, PrepareOffer, (), (noexcept, override)); MOCK_METHOD(void, PrepareStopOffer, (), (noexcept, override)); - MOCK_METHOD(std::size_t, GetMaxSize, (), (const, noexcept, override)); - MOCK_METHOD(std::size_t, GetAlignment, (), (const, noexcept, override)); + MOCK_METHOD(memory::DataTypeSizeInfo, GetSizeInfo, (), (const, noexcept, override)); MOCK_METHOD(BindingType, GetBindingType, (), (const, noexcept, override)); MOCK_METHOD(void, SetSkeletonEventTracingData, (impl::tracing::SkeletonEventTracingData), (noexcept, override)); }; @@ -57,7 +56,7 @@ class SkeletonEvent : public SkeletonEventBinding MOCK_METHOD(Result>, GetLatestSample, (QualityType), (override)); MOCK_METHOD(Result, PrepareOffer, (), (noexcept, override)); MOCK_METHOD(void, PrepareStopOffer, (), (noexcept, override)); - MOCK_METHOD(std::size_t, GetMaxSize, (), (const, noexcept, override)); + MOCK_METHOD(memory::DataTypeSizeInfo, GetSizeInfo, (), (const, noexcept, override)); MOCK_METHOD(BindingType, GetBindingType, (), (const, noexcept, override)); MOCK_METHOD(void, SetSkeletonEventTracingData, (impl::tracing::SkeletonEventTracingData), (noexcept, override)); }; @@ -102,9 +101,9 @@ class SkeletonEventFacade : public SkeletonEventBinding { return skeleton_event_.PrepareStopOffer(); } - std::size_t GetMaxSize() const noexcept override + memory::DataTypeSizeInfo GetSizeInfo() const noexcept override { - return skeleton_event_.GetMaxSize(); + return skeleton_event_.GetSizeInfo(); } BindingType GetBindingType() const noexcept override { diff --git a/score/mw/com/impl/generic_skeleton_event.cpp b/score/mw/com/impl/generic_skeleton_event.cpp index 601d4bf87..30aabefcb 100644 --- a/score/mw/com/impl/generic_skeleton_event.cpp +++ b/score/mw/com/impl/generic_skeleton_event.cpp @@ -106,8 +106,8 @@ DataTypeMetaInfo GenericSkeletonEvent::GetSizeInfo() const noexcept const auto* const binding = static_cast(binding_.get()); if (!binding) return {}; - const auto size_info_pair = binding->GetSizeInfo(); - return {size_info_pair.first, size_info_pair.second}; + const auto data_type_size_info = binding->GetSizeInfo(); + return {data_type_size_info.Size(), data_type_size_info.Alignment()}; } Result GenericSkeletonEvent::SetReceiveHandlerRegistrationChangedHandler( diff --git a/score/mw/com/impl/generic_skeleton_event_binding.h b/score/mw/com/impl/generic_skeleton_event_binding.h index 270e9eb6b..bb7f95319 100644 --- a/score/mw/com/impl/generic_skeleton_event_binding.h +++ b/score/mw/com/impl/generic_skeleton_event_binding.h @@ -18,6 +18,8 @@ #include "score/mw/com/impl/plumbing/sample_allocatee_ptr.h" #include "score/mw/com/impl/sample_allocatee_guard.h" + +#include "score/memory/data_type_size_info.h" #include "score/result/result.h" #include @@ -36,8 +38,6 @@ class GenericSkeletonEventBinding : public SkeletonEventBindingBase /// \brief Get Notification when new sample is available. virtual Result Notify() noexcept = 0; - virtual std::pair GetSizeInfo() const noexcept = 0; - virtual Result SetReceiveHandlerRegistrationChangedHandler( ReceiveHandlerRegistrationChangedCallback callback) noexcept = 0; diff --git a/score/mw/com/impl/generic_skeleton_event_test.cpp b/score/mw/com/impl/generic_skeleton_event_test.cpp index aff030cfc..17ab2f0c3 100644 --- a/score/mw/com/impl/generic_skeleton_event_test.cpp +++ b/score/mw/com/impl/generic_skeleton_event_test.cpp @@ -266,15 +266,15 @@ TEST_F(GenericSkeletonEventTest, GetSizeInfoDispatchesToBinding) this->GivenAGenericSkeletonWithOneEvent(); // Expect the binding to return specific size info - std::pair expected_size_info{32, 16}; + memory::DataTypeSizeInfo expected_size_info{32, 16}; EXPECT_CALL(*mock_event_binding_ptr_, GetSizeInfo()).WillOnce(Return(expected_size_info)); // When calling GetSizeInfo auto result_info = event_->GetSizeInfo(); // Then it matches the binding's return values - EXPECT_EQ(result_info.size, expected_size_info.first); - EXPECT_EQ(result_info.alignment, expected_size_info.second); + EXPECT_EQ(result_info.size, expected_size_info.Size()); + EXPECT_EQ(result_info.alignment, expected_size_info.Alignment()); } TEST_F(GenericSkeletonEventTest, NotifyBeforeOfferReturnsError) diff --git a/score/mw/com/impl/skeleton_event_binding.h b/score/mw/com/impl/skeleton_event_binding.h index b39557872..348d2df54 100644 --- a/score/mw/com/impl/skeleton_event_binding.h +++ b/score/mw/com/impl/skeleton_event_binding.h @@ -20,6 +20,7 @@ #include "score/mw/com/impl/sample_allocatee_guard.h" #include "score/mw/com/impl/tracing/skeleton_event_tracing_data.h" +#include "score/memory/data_type_size_info.h" #include "score/result/result.h" #include @@ -60,12 +61,8 @@ class SkeletonEventBindingBase /// de-initialization) virtual void PrepareStopOffer() noexcept = 0; - /// \brief Calculate the necessary memory for the underlying event-type (including possible dynamic memory - /// allocations) - virtual std::size_t GetMaxSize() const noexcept = 0; - - /// \brief Alignment requirement (in bytes) of the underlying event-type's sample data. - virtual std::size_t GetAlignment() const noexcept = 0; + /// \brief Get size for the underlying event-type (including possible dynamic memory allocations) and its alignment + virtual memory::DataTypeSizeInfo GetSizeInfo() const noexcept = 0; /// \brief Gets the binding type of the binding virtual BindingType GetBindingType() const noexcept = 0; @@ -98,14 +95,9 @@ class SkeletonEventBinding : public SkeletonEventBindingBase /// \brief Retrieves the latest sample, intended to support the getter of a SkeletonField. virtual Result> GetLatestSample(QualityType quality_type) = 0; - std::size_t GetMaxSize() const noexcept override - { - return sizeof(SampleType); - } - - std::size_t GetAlignment() const noexcept override + memory::DataTypeSizeInfo GetSizeInfo() const noexcept override { - return alignof(SampleType); + return memory::DataTypeSizeInfo{sizeof(SampleType), alignof(SampleType)}; } }; diff --git a/score/mw/com/impl/skeleton_event_binding_test.cpp b/score/mw/com/impl/skeleton_event_binding_test.cpp index 7d7906a2f..23c690d85 100644 --- a/score/mw/com/impl/skeleton_event_binding_test.cpp +++ b/score/mw/com/impl/skeleton_event_binding_test.cpp @@ -61,10 +61,11 @@ class MyEvent final : public SkeletonEventBinding void SetSkeletonEventTracingData(impl::tracing::SkeletonEventTracingData) noexcept override {} }; -TEST(SkeletonEventBindingTest, CanGetMaxSizeOfLiteralType) +TEST(SkeletonEventBindingTest, CanGetSizeInfoOfLiteralType) { MyEvent unit{}; - EXPECT_EQ(unit.GetMaxSize(), 1); + EXPECT_EQ(unit.GetSizeInfo().Size(), 1); + EXPECT_EQ(unit.GetSizeInfo().Alignment(), 1); } TEST(SkeletonEventBindingTest, SkeletonEventBindingShouldNotBeCopyable)