From 1400c817e78fcd8c13a7a6c5ea1306fc26d21a89 Mon Sep 17 00:00:00 2001 From: manuelfehlhammer Date: Fri, 2 Oct 2026 20:27:51 +0200 Subject: [PATCH] impl/lola: Rework AddEventDataStorageShmSizeAllocation Up till now AddEventDataStorageShmSizeAllocation was handed over an already calculated size for an internal EventDataStorage slot array. This mean, that the caller already anticipated, how EventDataStorage would internally store/layout its slots. This has been now fixed, by the caller just handing down the number of slots and the size of a slot element and the size calculation is left to AddEventDataStorageShmSizeAllocation, which is semantical the correct way. --- .../impl/bindings/lola/event_data_storage.cpp | 16 ++++- .../impl/bindings/lola/event_data_storage.h | 20 ++++-- .../bindings/lola/service_data_storage.cpp | 11 ++- .../impl/bindings/lola/service_data_storage.h | 9 ++- .../lola/service_data_storage_test.cpp | 70 ++++++------------- .../bindings/lola/skeleton_memory_manager.cpp | 28 ++++---- 6 files changed, 72 insertions(+), 82 deletions(-) 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 5dbcdbf4ad..64efc10471 100644 --- a/score/mw/com/impl/bindings/lola/event_data_storage.cpp +++ b/score/mw/com/impl/bindings/lola/event_data_storage.cpp @@ -115,11 +115,23 @@ SlotIndexType EventDataStorage::GetNumberOfSlots() const } void AddEventDataStorageShmSizeAllocation(std::vector& allocation_sequence, - memory::DataTypeSizeInfo event_sample_array_size_info) + EventDataStorageSizeInfo event_data_storage_size_info) { std::ignore = allocation_sequence.emplace_back(sizeof(EventDataStorage), alignof(EventDataStorage)); + + // This mirrors exactly how EventDataStorage's constructor computes the size of its type_erased_data_slots_ + // allocation (see above): number_of_slots * per_sample_size_info.Size(), aligned to per_sample_size_info's + // alignment. + const auto& per_sample_size_info = event_data_storage_size_info.per_sample_size_info; + const auto storage_bytes_needed_result = + safe_math::Multiply( + event_data_storage_size_info.number_of_slots, per_sample_size_info.Size()); + SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD_MESSAGE( + storage_bytes_needed_result.has_value(), + "Overflow while calculating the total size of the raw event-data slot-array."); + std::ignore = - allocation_sequence.emplace_back(event_sample_array_size_info.Size(), event_sample_array_size_info.Alignment()); + allocation_sequence.emplace_back(storage_bytes_needed_result.value(), per_sample_size_info.Alignment()); } } // 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 b71e833d4f..08a82ca77c 100644 --- a/score/mw/com/impl/bindings/lola/event_data_storage.h +++ b/score/mw/com/impl/bindings/lola/event_data_storage.h @@ -21,6 +21,7 @@ #include #include +#include namespace score::mw::com::impl::lola { @@ -90,16 +91,23 @@ class EventDataStorage final std::size_t type_erased_data_slots_storage_size_in_bytes_; }; +/// \brief Per service-element (event/field) information required to analytically size the EventDataStorage of a +/// service-element, i.e. the exact number of (type-erased) slots plus the size/alignment of a single slot. +struct EventDataStorageSizeInfo +{ + /// \brief Number of (type-erased) event-data slots for the service-element. + std::size_t number_of_slots; + /// \brief Size/alignment of a single sample of the service-element's datatype. + memory::DataTypeSizeInfo per_sample_size_info; +}; + /// \brief Adds allocation done by EventDataStorage to an existing allocation_sequence /// \details Gets called by the "parent" CalculateServiceDataStorageShmSize() in its calculation. /// \param allocation_sequence The sequence of allocations to which the EventDataStorage allocations will be added. -/// \param event_sample_array_size_info The size information of the event sample array. -/// \todo Handing over the complete event sample array should be changed, because it already contains the expectation, -/// how EventDataStorage will internally store the events/slots! But it needs a rework in the call chain! -/// I.e. we should hand down number_of_slots/DataTypeSizeInfo per single event separately. -/// Ticket: SWP-281780 +/// \param event_data_storage_size_info The number of slots plus the size/alignment of a single slot of the +/// service-element's EventDataStorage. void AddEventDataStorageShmSizeAllocation(std::vector& allocation_sequence, - memory::DataTypeSizeInfo event_sample_array_size_info); + EventDataStorageSizeInfo event_data_storage_size_info); } // namespace score::mw::com::impl::lola 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 42b2fd632c..991eedb353 100644 --- a/score/mw/com/impl/bindings/lola/service_data_storage.cpp +++ b/score/mw/com/impl/bindings/lola/service_data_storage.cpp @@ -25,7 +25,7 @@ namespace score::mw::com::impl::lola { std::size_t CalculateServiceDataStorageShmSize( - const score::cpp::span event_and_fields_size_info) + const score::cpp::span event_and_fields_size_info) { // The number of events + fields determines the (fixed) capacity of the two LinearSearchMaps within the // ServiceDataStorage. It equals the number of sizing entries handed over. @@ -52,12 +52,9 @@ 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 - // data-slot-array (type_erased_data_slots_). The exact size/alignment of the slot-array (see - // SkeletonMemoryManager::CreateEventDataInCreatedSharedMemory()) is provided by the caller. - // \ToDo see also comment in AddEventDataStorageShmSizeAllocation(): We should eventually hand down number_of_slots/ - // event sample size info separated, instead of aggregated arrays as it "anticipates", what EventDataStorage does! - // Ticket: SWP-281780 - + // data-slot-array (type_erased_data_slots_). The number of slots plus the size/alignment of a single slot is + // provided by the caller; AddEventDataStorageShmSizeAllocation() itself computes the actual slot-array allocation + // size/alignment needs. for (const auto& service_element : event_and_fields_size_info) { AddEventDataStorageShmSizeAllocation(allocation_sequence, service_element); 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 005f9c1c23..0a3cf7c1fe 100644 --- a/score/mw/com/impl/bindings/lola/service_data_storage.h +++ b/score/mw/com/impl/bindings/lola/service_data_storage.h @@ -84,15 +84,14 @@ class ServiceDataStorage /// aligned location) and we know the size/alignment/order of every individual allocation performed by the /// real construction, we can reconstruct the exact same sequence of allocations here and compute the exact /// alignment-padding between them (see score::memory::shared::CalculateAlignedSizeOfSequence()). -/// \param event_and_fields_size_info per service-element sizing information (Size() being the exact size, in bytes, -/// of the raw slot-array that will be allocated for the service-element; Alignment() being its required -/// alignment). The caller (SkeletonMemoryManager) is responsible for computing these values -/// (see SkeletonMemoryManager::CreateEventDataInCreatedSharedMemory()). +/// \param event_and_fields_size_info per service-element sizing information: the number of (type-erased) slots plus +/// the size/alignment of a single slot. The caller (SkeletonMemoryManager) is responsible for providing these +/// values (see SkeletonMemoryManager::CalculateDataShmResourceStorageSize()). /// 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. std::size_t CalculateServiceDataStorageShmSize( - score::cpp::span event_and_fields_size_info); + score::cpp::span event_and_fields_size_info); } // namespace score::mw::com::impl::lola 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 7a44b4b9be..1dbc4204cf 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 @@ -112,16 +112,16 @@ TEST_F(ServiceDataStorageFixture, GetsUidFromRuntimAndStoresItOnConstruction) TEST(ServiceDataStorageShmSizeTest, IncreasingAlignedSlotArraySizeOfAServiceElementIncreasesCalculatedSize) { // Given two sizing infos for a single service-element that only differ in the size of their raw slot-array - const std::vector service_elements_with_smaller_slot_array{ - score::memory::DataTypeSizeInfo{32U, 16U}}; - const std::vector service_elements_with_bigger_slot_array{ - score::memory::DataTypeSizeInfo{320U, 16U}}; + const std::vector service_elements_with_smaller_slot_array{ + EventDataStorageSizeInfo{2U, score::memory::DataTypeSizeInfo{32U, 16U}}}; + const std::vector service_elements_with_bigger_slot_array{ + EventDataStorageSizeInfo{20U, score::memory::DataTypeSizeInfo{32U, 16U}}}; // When calculating the required shm-size for both sizing infos const auto size_with_fewer_slots = CalculateServiceDataStorageShmSize( - score::cpp::span{service_elements_with_smaller_slot_array}); + score::cpp::span{service_elements_with_smaller_slot_array}); const auto size_with_more_slots = CalculateServiceDataStorageShmSize( - score::cpp::span{service_elements_with_bigger_slot_array}); + score::cpp::span{service_elements_with_bigger_slot_array}); // Then the calculated size for the service-element with the bigger raw slot-array is bigger. EXPECT_GT(size_with_more_slots, size_with_fewer_slots); @@ -131,31 +131,23 @@ TEST(ServiceDataStorageShmSizeTest, AddingAnAdditionalServiceElementIncreasesCal { // Given the sizing information of one service-element and, additionally, the very same sizing information for // two service-elements - const std::vector single_service_element{ - score::memory::DataTypeSizeInfo{48U, 16U}}; - const std::vector two_service_elements{score::memory::DataTypeSizeInfo{48U, 16U}, - score::memory::DataTypeSizeInfo{48U, 16U}}; + const std::vector single_service_element{ + EventDataStorageSizeInfo{1U, score::memory::DataTypeSizeInfo{48U, 16U}}}; + const std::vector two_service_elements{ + EventDataStorageSizeInfo{1U, score::memory::DataTypeSizeInfo{48U, 16U}}, + EventDataStorageSizeInfo{1U, score::memory::DataTypeSizeInfo{48U, 16U}}}; // When calculating the required shm-size for both sizing infos - const auto size_for_single_service_element = CalculateServiceDataStorageShmSize( - score::cpp::span{single_service_element}); - const auto size_for_two_service_elements = CalculateServiceDataStorageShmSize( - score::cpp::span{two_service_elements}); + const auto size_for_single_service_element = + CalculateServiceDataStorageShmSize(score::cpp::span{single_service_element}); + const auto size_for_two_service_elements = + CalculateServiceDataStorageShmSize(score::cpp::span{two_service_elements}); // Then the calculated size for two service-elements is bigger than for a single one. EXPECT_GT(size_for_two_service_elements, size_for_single_service_element); } -/// \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 -{ - score::memory::DataTypeSizeInfo per_sample_size_info; - std::size_t number_of_slots; -}; - -using EventsOrFieldsSizeInfo = std::vector; +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 @@ -179,23 +171,6 @@ std::size_t ConstructServiceDataStorageAndGetAllocatedBytes(const EventsOrFields return resource.GetUserAllocatedBytes(); } -/// \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 { @@ -208,9 +183,8 @@ TEST_P(ServiceDataStorageShmSizeParameterizedTestFixture, CalculatedSizeMatchesA 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{slot_array_size_infos}); + score::cpp::span{service_elements_size_info}); // 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. @@ -228,12 +202,14 @@ INSTANTIATE_TEST_SUITE_P( // No service-elements at all (an empty span) EventsOrFieldsSizeInfo{}, // A single service-element (event/field): 5 slots of a datatype of size/alignment 16 - EventsOrFieldsSizeInfo{EventOrFieldSizeInfo{score::memory::DataTypeSizeInfo{16U, 16U}, 5U}}, + EventsOrFieldsSizeInfo{EventDataStorageSizeInfo{5U, score::memory::DataTypeSizeInfo{16U, 16U}}}, // 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}, + EventDataStorageSizeInfo{2U, score::memory::DataTypeSizeInfo{8U, 8U}}, + EventDataStorageSizeInfo{14U, + score::memory::DataTypeSizeInfo{kMaxSupportedAlignment, kMaxSupportedAlignment}}, + EventDataStorageSizeInfo{8U, + score::memory::DataTypeSizeInfo{kMaxSupportedAlignment, kMaxSupportedAlignment}}, })); } // namespace 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 8ffa7c0c05..322e2a8bbb 100644 --- a/score/mw/com/impl/bindings/lola/skeleton_memory_manager.cpp +++ b/score/mw/com/impl/bindings/lola/skeleton_memory_manager.cpp @@ -536,21 +536,19 @@ std::size_t SkeletonMemoryManager::CalculateDataShmResourceStorageSize( // 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](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); - SkeletonEventBinding& event_binding = binding.second.get(); - - const std::size_t slot_array_size = number_of_slots * event_binding.GetEventDataTypeSizeInfo().Size(); - std::ignore = events_and_fields_size_infos.emplace_back( - slot_array_size, event_binding.GetEventDataTypeSizeInfo().Alignment()); - } - }; - std::vector events_and_fields_size_infos{}; + const auto collect_service_elements = [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); + SkeletonEventBinding& event_binding = binding.second.get(); + + std::ignore = events_and_fields_size_infos.emplace_back( + EventDataStorageSizeInfo{number_of_slots, event_binding.GetEventDataTypeSizeInfo()}); + } + }; + std::vector events_and_fields_size_infos{}; collect_service_elements(events_and_fields_size_infos, events, false); collect_service_elements(events_and_fields_size_infos, fields, true);