From 5c9e3df135aac3a278abf6bdd4cb1519f76a276f Mon Sep 17 00:00:00 2001 From: Mina Hamdi Date: Tue, 30 Dec 2025 21:46:09 +0200 Subject: [PATCH 1/3] PoC implementation of the Generic Skeleton for Events and with initial partial restart handling implementation. --- .../design/events_fields/NestedCallbacks.svg | 0 score/mw/com/example/ipc_bridge/main.cpp | 6 +- .../ipc_bridge/sample_sender_receiver.cpp | 86 +++++++++ .../ipc_bridge/sample_sender_receiver.h | 4 + score/mw/com/impl/BUILD | 75 ++++++++ score/mw/com/impl/bindings/lola/BUILD | 4 + .../bindings/lola/generic_skeleton_event.cpp | 98 ++++++++++ .../bindings/lola/generic_skeleton_event.h | 71 +++++++ score/mw/com/impl/bindings/lola/skeleton.cpp | 178 ++++++++++++++---- score/mw/com/impl/bindings/lola/skeleton.h | 82 ++++---- .../com/impl/bindings/mock_binding/skeleton.h | 10 + score/mw/com/impl/generic_skeleton.cpp | 92 +++++++++ score/mw/com/impl/generic_skeleton.h | 67 +++++++ score/mw/com/impl/generic_skeleton_event.cpp | 64 +++++++ score/mw/com/impl/generic_skeleton_event.h | 44 +++++ .../com/impl/generic_skeleton_event_binding.h | 38 ++++ score/mw/com/impl/generic_skeleton_test.cpp | 103 ++++++++++ .../com/impl/plumbing/sample_allocatee_ptr.h | 159 ++++++++++++++++ score/mw/com/impl/size_info.h | 26 +++ score/mw/com/impl/skeleton_base.h | 5 + score/mw/com/impl/skeleton_binding.h | 10 + 21 files changed, 1143 insertions(+), 79 deletions(-) mode change 100755 => 100644 score/mw/com/design/events_fields/NestedCallbacks.svg create mode 100644 score/mw/com/impl/bindings/lola/generic_skeleton_event.cpp create mode 100644 score/mw/com/impl/bindings/lola/generic_skeleton_event.h create mode 100644 score/mw/com/impl/generic_skeleton.cpp create mode 100644 score/mw/com/impl/generic_skeleton.h create mode 100644 score/mw/com/impl/generic_skeleton_event.cpp create mode 100644 score/mw/com/impl/generic_skeleton_event.h create mode 100644 score/mw/com/impl/generic_skeleton_event_binding.h create mode 100644 score/mw/com/impl/generic_skeleton_test.cpp create mode 100644 score/mw/com/impl/size_info.h diff --git a/score/mw/com/design/events_fields/NestedCallbacks.svg b/score/mw/com/design/events_fields/NestedCallbacks.svg old mode 100755 new mode 100644 diff --git a/score/mw/com/example/ipc_bridge/main.cpp b/score/mw/com/example/ipc_bridge/main.cpp index 8711a3cb..7c282c8f 100644 --- a/score/mw/com/example/ipc_bridge/main.cpp +++ b/score/mw/com/example/ipc_bridge/main.cpp @@ -51,7 +51,7 @@ Params ParseCommandLineArguments(const int argc, const char** argv) "Number of cycles that are executed before determining success or failure. 0 indicates no limit."); options.add_options()("mode,m", po::value(), - "Set to either send/skeleton or recv/proxy to determine the role of the process"); + "Set to: send/skeleton (typed skeleton), gen_skeleton (generic skeleton) or recv/proxy to determine the role of the process"); options.add_options()("cycle-time,t", po::value(), "Cycle time in milliseconds for sending/polling"); options.add_options()( "service_instance_manifest,s", po::value(), "Path to the com configuration file"); @@ -119,6 +119,10 @@ int main(const int argc, const char** argv) { return event_sender_receiver.RunAsSkeleton(instance_specifier, cycle_time, cycles); } + else if (mode == "gen_skeleton") + { + return event_sender_receiver.RunAsGenericSkeleton(instance_specifier, cycle_time, cycles); + } else if (mode == "recv" || mode == "proxy") { return event_sender_receiver.RunAsProxy(instance_specifier, cycle_time, cycles, false, check_sample_hash); diff --git a/score/mw/com/example/ipc_bridge/sample_sender_receiver.cpp b/score/mw/com/example/ipc_bridge/sample_sender_receiver.cpp index db24972d..1445473a 100644 --- a/score/mw/com/example/ipc_bridge/sample_sender_receiver.cpp +++ b/score/mw/com/example/ipc_bridge/sample_sender_receiver.cpp @@ -13,6 +13,7 @@ #include "sample_sender_receiver.h" #include "score/mw/com/impl/generic_proxy.h" #include "score/mw/com/impl/generic_proxy_event.h" +#include "score/mw/com/impl/generic_skeleton.h" #include "score/mw/com/impl/handle_type.h" #include "score/concurrency/notification.h" @@ -268,6 +269,36 @@ Result> PrepareMapLaneSample(IpcBridgeSke return sample; } +Result> PrepareMapLaneSample(impl::GenericSkeletonEvent& event, + const std::size_t cycle) +{ + const std::default_random_engine::result_type seed{static_cast( + std::chrono::steady_clock::now().time_since_epoch().count())}; + std::default_random_engine rng{seed}; + + auto sample_result = event.Allocate(); + + if (!sample_result.has_value()) + { + return sample_result; + } + auto sample = std::move(sample_result).value(); + auto* typed_sample = static_cast(sample.Get()); + typed_sample->hash_value = START_HASH; + typed_sample->x = static_cast(cycle); + + std::cout << ToString("Sending sample: ", typed_sample->x, "\n"); + for (MapApiLaneData& lane : typed_sample->lanes) { + for (LaneIdType& successor : lane.successor_lanes) + { + successor = std::uniform_int_distribution()(rng); + } + + HashArray(lane.successor_lanes, typed_sample->hash_value); + } + return sample; +} + } // namespace template @@ -447,6 +478,61 @@ int EventSenderReceiver::RunAsSkeleton(const score::mw::com::InstanceSpecifier& return EXIT_SUCCESS; } +int EventSenderReceiver::RunAsGenericSkeleton(const score::mw::com::InstanceSpecifier& instance_specifier, + const std::chrono::milliseconds cycle_time, + const std::size_t num_cycles) +{ + auto create_result = impl::GenericSkeleton::Create(instance_specifier); + if (!create_result.has_value()) + { + std::cerr << "Unable to construct skeleton: " << create_result.error() << ", bailing!\n"; + return EXIT_FAILURE; + } + auto& skeleton = create_result.value(); + + const auto event_name = "map_api_lanes_stamped"; + const SizeInfo size_info{sizeof(MapApiLanesStamped), alignof(MapApiLanesStamped)}; + auto event_result = skeleton.AddEvent(event_name, size_info); + if (!event_result.has_value()) + { + std::cerr << "Unable to add event to skeleton: " << event_result.error() << ", bailing!\n"; + return EXIT_FAILURE; + } + auto& event = *event_result.value(); + + const auto offer_result = skeleton.OfferService(); + if (!offer_result.has_value()) + { + std::cerr << "Unable to offer service for skeleton: " << offer_result.error() << ", bailing!\n"; + return EXIT_FAILURE; + } + std::cout << "Starting to send data\n"; + + for (std::size_t cycle = 0U; cycle < num_cycles || num_cycles == 0U; ++cycle) + { + auto sample_result = PrepareMapLaneSample(event, cycle); + if (!sample_result.has_value()) + { + std::cerr << "No sample received. Exiting.\n"; + return EXIT_FAILURE; + } + auto sample = std::move(sample_result).value(); + + { + std::lock_guard lock{event_sending_mutex_}; + event.Send(std::move(sample)); + event_published_ = true; + } + std::this_thread::sleep_for(cycle_time); + } + + std::cout << "Stop offering service..."; + skeleton.StopOfferService(); + std::cout << "and terminating, bye bye\n"; + + return EXIT_SUCCESS; +} + template int EventSenderReceiver::RunAsProxy>( const score::mw::com::InstanceSpecifier&, const score::cpp::optional, diff --git a/score/mw/com/example/ipc_bridge/sample_sender_receiver.h b/score/mw/com/example/ipc_bridge/sample_sender_receiver.h index 541ad6de..6ce7fc04 100644 --- a/score/mw/com/example/ipc_bridge/sample_sender_receiver.h +++ b/score/mw/com/example/ipc_bridge/sample_sender_receiver.h @@ -33,6 +33,10 @@ class EventSenderReceiver const std::chrono::milliseconds cycle_time, const std::size_t num_cycles); + int RunAsGenericSkeleton(const score::mw::com::InstanceSpecifier& instance_specifier, + const std::chrono::milliseconds cycle_time, + const std::size_t num_cycles); + template > int RunAsProxy(const score::mw::com::InstanceSpecifier& instance_specifier, diff --git a/score/mw/com/impl/BUILD b/score/mw/com/impl/BUILD index 908c0aa2..a03ad876 100644 --- a/score/mw/com/impl/BUILD +++ b/score/mw/com/impl/BUILD @@ -30,6 +30,7 @@ cc_library( ":generic_proxy_event", ":proxy_event", ":proxy_field", + ":generic_skeleton", ":skeleton_event", ":skeleton_field", ":traits", @@ -110,6 +111,78 @@ cc_library( ], ) +cc_library( + name = "generic_skeleton", + srcs = ["generic_skeleton.cpp"], + hdrs = ["generic_skeleton.h"], + features = COMPILER_WARNING_FEATURES, + implementation_deps = [ + ":error", + "//score/mw/com/impl/plumbing", + ], + tags = ["FFI"], + visibility = [ + "//score/mw/com:__subpackages__", + ], + deps = [ + ":generic_skeleton_event", + ":instance_identifier", + ":instance_specifier", + ":runtime", + ":skeleton_base", + ":skeleton_binding", + ":size_info", + "@score_baselibs//score/result", + ], +) + +cc_library( + name = "generic_skeleton_event", + srcs = ["generic_skeleton_event.cpp"], + hdrs = ["generic_skeleton_event.h"], + features = COMPILER_WARNING_FEATURES, + tags = ["FFI"], + visibility = [ + "//score/mw/com:__subpackages__", + ], + deps = [ + ":generic_skeleton_event_binding", + ":skeleton_event_base", + ":skeleton_event_binding", + ":size_info", + "//score/mw/com/impl/plumbing:sample_allocatee_ptr", + "@score_baselibs//score/result", + ], +) + +cc_library( + name = "generic_skeleton_event_binding", + hdrs = [ + "generic_skeleton_event_binding.h", + ], + features = COMPILER_WARNING_FEATURES, + tags = ["FFI"], + visibility = [ + "//score/mw/com/impl/bindings/lola:__pkg__", + "//score/mw/com/impl/bindings/mock_binding:__pkg__", + ], + deps = [ + ":skeleton_event_binding", + "@score_baselibs//score/result", + ], +) + +cc_library( + name = "size_info", + hdrs = ["size_info.h"], + features = COMPILER_WARNING_FEATURES, + tags = ["FFI"], + visibility = [ + "//score/mw/com:__pkg__", + "//score/mw/com/impl:__subpackages__", + ], +) + cc_library( name = "skeleton_event", srcs = ["skeleton_event.cpp"], @@ -381,6 +454,8 @@ cc_library( ], deps = [ ":binding_type", + ":generic_skeleton_event_binding", + ":size_info", "//score/mw/com/impl/configuration", "@score_baselibs//score/language/futurecpp", "@score_baselibs//score/memory/shared:i_shared_memory_resource", diff --git a/score/mw/com/impl/bindings/lola/BUILD b/score/mw/com/impl/bindings/lola/BUILD index 02c5c3fe..584a435b 100644 --- a/score/mw/com/impl/bindings/lola/BUILD +++ b/score/mw/com/impl/bindings/lola/BUILD @@ -247,12 +247,14 @@ cc_library( "skeleton_event.cpp", "skeleton_event_properties.cpp", "skeleton_method.cpp", + "generic_skeleton_event.cpp", ], hdrs = [ "skeleton.h", "skeleton_event.h", "skeleton_event_properties.h", "skeleton_method.h", + "generic_skeleton_event.h", ], features = COMPILER_WARNING_FEATURES, tags = ["FFI"], @@ -282,6 +284,8 @@ cc_library( "//score/mw/com/impl/plumbing:sample_allocatee_ptr", "//score/mw/com/impl/tracing:skeleton_event_tracing", "//score/mw/com/impl/util:arithmetic_utils", + "//score/mw/com/impl:generic_skeleton_event_binding", + "//score/mw/com/impl:error", "@score_baselibs//score/filesystem", "@score_baselibs//score/language/futurecpp", "@score_baselibs//score/language/safecpp/safe_math", diff --git a/score/mw/com/impl/bindings/lola/generic_skeleton_event.cpp b/score/mw/com/impl/bindings/lola/generic_skeleton_event.cpp new file mode 100644 index 00000000..1ec2b3e3 --- /dev/null +++ b/score/mw/com/impl/bindings/lola/generic_skeleton_event.cpp @@ -0,0 +1,98 @@ +/******************************************************************************** + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional information + * regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#include "score/mw/com/impl/bindings/lola/generic_skeleton_event.h" +#include "score/mw/com/impl/bindings/lola/skeleton.h" +#include "score/mw/com/impl/bindings/lola/skeleton_event_properties.h" + +namespace score::mw::com::impl::lola +{ +GenericSkeletonEvent::GenericSkeletonEvent(Skeleton& parent, + const SkeletonEventProperties& event_properties, + const ElementFqId& event_fqn, + const SizeInfo& size_info) + : parent_(parent), size_info_(size_info), event_properties_(event_properties), event_fqn_(event_fqn) +{ + +} + +ResultBlank GenericSkeletonEvent::PrepareOffer() noexcept +{ + auto [data_storage, control_composite] = + parent_.RegisterGeneric(event_fqn_, event_properties_, size_info_.size, size_info_.alignment); + + data_storage_ = data_storage; + control_.emplace(std::move(control_composite)); + + return {}; +} + +Result GenericSkeletonEvent::Send(const void* /*data*/) noexcept +{ + + return MakeUnexpected(ComErrc::kIllegalUseOfAllocate); +} + +Result GenericSkeletonEvent::Send(lola::ControlSlotCompositeIndicator control_slot_indicator) noexcept +{ + SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD(control_.has_value()); + control_.value().EventReady(control_slot_indicator, ++current_timestamp_); + return {}; +} + +Result> GenericSkeletonEvent::Allocate() noexcept +{ + SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD(control_.has_value()); + auto slot = control_.value().AllocateNextSlot(); + + if (!slot.IsValidQM() && !slot.IsValidAsilB()) + { + return MakeUnexpected>( + ComErrc::kSampleAllocationFailure); + } + + + auto* data_storage = data_storage_.get>(); + SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD(data_storage != nullptr); + auto* data_base_ptr = data_storage->data(); + void* data_ptr = data_base_ptr + (slot.GetIndex() * size_info_.size); + return std::make_pair(data_ptr, slot); +} + +void GenericSkeletonEvent::Deallocate(lola::ControlSlotCompositeIndicator control_slot_indicator) noexcept +{ + SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD(control_.has_value()); + control_.value().Discard(control_slot_indicator); +} + +std::pair GenericSkeletonEvent::GetSizeInfo() const noexcept +{ + return {size_info_.size, size_info_.alignment}; +} + +void GenericSkeletonEvent::PrepareStopOffer() noexcept +{ + control_.reset(); + data_storage_ = nullptr; +} + +BindingType GenericSkeletonEvent::GetBindingType() const noexcept +{ + return BindingType::kLoLa; +} + +void GenericSkeletonEvent::SetSkeletonEventTracingData(impl::tracing::SkeletonEventTracingData tracing_data) noexcept +{ + tracing_data_ = tracing_data; +} + +} // namespace score::mw::com::impl::lola \ No newline at end of file diff --git a/score/mw/com/impl/bindings/lola/generic_skeleton_event.h b/score/mw/com/impl/bindings/lola/generic_skeleton_event.h new file mode 100644 index 00000000..8ad349aa --- /dev/null +++ b/score/mw/com/impl/bindings/lola/generic_skeleton_event.h @@ -0,0 +1,71 @@ +/******************************************************************************** + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#pragma once + +#include "score/mw/com/impl/generic_skeleton_event_binding.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/size_info.h" +#include "score/mw/com/impl/bindings/lola/skeleton_event_properties.h" +#include "score/memory/shared/offset_ptr.h" +#include "score/mw/com/impl/bindings/lola/event_slot_status.h" + +#include "score/mw/com/impl/tracing/skeleton_event_tracing_data.h" + +namespace score::mw::com::impl::lola +{ + +class Skeleton; + +/// @brief The LoLa binding implementation for a generic skeleton event. +class GenericSkeletonEvent : public GenericSkeletonEventBinding +{ + public: + GenericSkeletonEvent(Skeleton& parent, + const SkeletonEventProperties& event_properties, + const ElementFqId& event_fqn, + const SizeInfo& size_info); + + Result Send(const void* data) noexcept override; + + Result Send(lola::ControlSlotCompositeIndicator control_slot_indicator) noexcept override; + + Result> Allocate() noexcept override; + + void Deallocate(lola::ControlSlotCompositeIndicator control_slot_indicator) noexcept override; + + std::pair GetSizeInfo() const noexcept override; + + + ResultBlank PrepareOffer() noexcept override; + void PrepareStopOffer() noexcept override; + BindingType GetBindingType() const noexcept override; + void SetSkeletonEventTracingData(impl::tracing::SkeletonEventTracingData tracing_data) noexcept override; + + std::size_t GetMaxSize() const noexcept override + { + return size_info_.size; + } + + private: + Skeleton& parent_; + SizeInfo size_info_; + const SkeletonEventProperties event_properties_; + const ElementFqId event_fqn_; + score::cpp::optional control_; + std::atomic current_timestamp_{0U}; + score::memory::shared::OffsetPtr data_storage_{nullptr}; + impl::tracing::SkeletonEventTracingData tracing_data_{}; +}; + +} // namespace score::mw::com::impl::lola \ No newline at end of file diff --git a/score/mw/com/impl/bindings/lola/skeleton.cpp b/score/mw/com/impl/bindings/lola/skeleton.cpp index ccc61605..c68dc3f3 100644 --- a/score/mw/com/impl/bindings/lola/skeleton.cpp +++ b/score/mw/com/impl/bindings/lola/skeleton.cpp @@ -26,6 +26,7 @@ #include "score/mw/com/impl/com_error.h" #include "score/mw/com/impl/configuration/lola_event_instance_deployment.h" #include "score/mw/com/impl/configuration/lola_service_instance_deployment.h" +#include "score/mw/com/impl/bindings/lola/generic_skeleton_event.h" #include "score/mw/com/impl/configuration/lola_service_type_deployment.h" #include "score/mw/com/impl/configuration/quality_type.h" #include "score/mw/com/impl/runtime.h" @@ -74,20 +75,6 @@ const LolaServiceTypeDeployment& GetLolaServiceTypeDeployment(const InstanceIden return *lola_service_type_deployment_ptr; } -const LolaServiceInstanceDeployment& GetLolaServiceInstanceDeployment(const InstanceIdentifier& identifier) -{ - const auto& instance_depl_info = InstanceIdentifierView{identifier}.GetServiceInstanceDeployment(); - const auto* lola_service_instance_deployment_ptr = - std::get_if(&instance_depl_info.bindingInfo_); - if (lola_service_instance_deployment_ptr == nullptr) - { - score::mw::log::LogError("lola") << "GetLolaServiceInstanceDeployment: Wrong Binding! ServiceInstanceDeployment " - "doesn't contain a LoLa deployment!"; - std::terminate(); - } - return *lola_service_instance_deployment_ptr; -} - ServiceDataControl* GetServiceDataControlSkeletonSide(const memory::shared::ManagedMemoryResource& control) { // Suppress "AUTOSAR C++14 M5-2-8" rule. The rule declares: @@ -143,22 +130,6 @@ bool CreatePartialRestartDirectory(const score::filesystem::Filesystem& filesyst return true; } -std::optional CreateOrOpenServiceInstanceExistenceMarkerFile( - const LolaServiceInstanceId::InstanceId lola_instance_id, - const IPartialRestartPathBuilder& partial_restart_path_builder) -{ - auto service_instance_existence_marker_file_path = - partial_restart_path_builder.GetServiceInstanceExistenceMarkerFilePath(lola_instance_id); - - // The instance existence marker file can be opened in the case that another skeleton of the same service currently - // exists or that a skeleton of the same service previously crashed. We cannot determine which is true until we try - // to flock the file. Therefore, we do not take ownership on construction and take ownership later if we can - // exclusively flock the file. - bool take_ownership{false}; - return memory::shared::LockFile::CreateOrOpen(std::move(service_instance_existence_marker_file_path), - take_ownership); -} - std::optional CreateOrOpenServiceInstanceUsageMarkerFile( const LolaServiceInstanceId::InstanceId lola_instance_id, const IPartialRestartPathBuilder& partial_restart_path_builder) @@ -213,7 +184,7 @@ std::unique_ptr Skeleton::Create(const InstanceIdentifier& identifier, std::unique_ptr partial_restart_path_builder) { SCORE_LANGUAGE_FUTURECPP_PRECONDITION_PRD_MESSAGE(partial_restart_path_builder != nullptr, - "Skeleton::Create: partial restart path builder pointer is Null"); + "Skeleton::Create: partial restart path builder pointer is Null"); const auto partial_restart_dir_creation_result = CreatePartialRestartDirectory(filesystem, *partial_restart_path_builder); if (!partial_restart_dir_creation_result) @@ -222,10 +193,23 @@ std::unique_ptr Skeleton::Create(const InstanceIdentifier& identifier, return nullptr; } - const auto& lola_service_instance_deployment = GetLolaServiceInstanceDeployment(identifier); + // --- FIX 1: Only declare these ONCE --- + const auto& instance_depl_info = InstanceIdentifierView{identifier}.GetServiceInstanceDeployment(); + const auto* lola_service_instance_deployment_ptr = + std::get_if(&instance_depl_info.bindingInfo_); + SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD(lola_service_instance_deployment_ptr != nullptr); + const auto& lola_service_instance_deployment = *lola_service_instance_deployment_ptr; + const auto lola_instance_id = lola_service_instance_deployment.instance_id_.value().GetId(); - auto service_instance_existence_marker_file = - CreateOrOpenServiceInstanceExistenceMarkerFile(lola_instance_id, *partial_restart_path_builder); + auto service_instance_existence_marker_file_path = + partial_restart_path_builder->GetServiceInstanceExistenceMarkerFilePath(lola_instance_id); + + // --- FIX 2: Correct LockFile logic (only one declaration) --- + bool take_ownership{true}; + auto service_instance_existence_marker_file = memory::shared::LockFile::CreateOrOpen( + std::move(service_instance_existence_marker_file_path), + take_ownership); + if (!service_instance_existence_marker_file.has_value()) { score::mw::log::LogError("lola") << "Could not create or open service instance existence marker file."; @@ -238,15 +222,13 @@ std::unique_ptr Skeleton::Create(const InstanceIdentifier& identifier, if (!service_instance_existence_mutex_and_lock->TryLock()) { score::mw::log::LogError("lola") - << "Flock try_lock failed: Another Skeleton could have already flocked the marker file and is " - "actively offering the same service instance."; + << "Flock try_lock failed: Another Skeleton could have already flocked the marker file."; return nullptr; } + // --- FIX 3: Use the helper to get the type deployment --- const auto& lola_service_type_deployment = GetLolaServiceTypeDeployment(identifier); - // Since we were able to flock the existence marker file, it means that either we created it or the skeleton that - // created it previously crashed. Either way, we take ownership of the LockFile so that it's destroyed when this - // Skeleton is destroyed. + service_instance_existence_marker_file.value().TakeOwnership(); return std::make_unique(identifier, lola_service_instance_deployment, @@ -848,6 +830,18 @@ score::cpp::optional Skeleton::GetEventMetaInfo(const ElementFqId } } +bool Skeleton::IsEventControlRegistered(const ElementFqId element_fq_id) const noexcept +{ + if (control_qm_ == nullptr) + { + return false; + } + const bool found_qm = (control_qm_->event_controls_.count(element_fq_id) > 0); + + // For ASIL-B, it must be in both. For QM, control_asil_b_ is nullptr. + return found_qm && (control_asil_b_ == nullptr || control_asil_b_->event_controls_.count(element_fq_id) > 0); +} + QualityType Skeleton::GetInstanceQualityType() const { return InstanceIdentifierView{identifier_}.GetServiceInstanceDeployment().asilLevel_; @@ -919,6 +913,106 @@ void Skeleton::InitializeSharedMemoryForControl( control = memory->construct(memory->getMemoryResourceProxy()); } +Result> +Skeleton::CreateGenericEventBinding(std::string_view event_name, size_t size, size_t alignment) noexcept +{ + const auto& event_deployment = GetServiceElementInstanceDeployment( + lola_service_instance_deployment_, std::string(event_name)); + + const SkeletonEventProperties event_properties{event_deployment.GetNumberOfSampleSlots().value_or(1U), + event_deployment.max_subscribers_.value_or(1U), + event_deployment.enforce_max_samples_}; + + const ElementFqId event_fqn{ + GetLolaServiceId(), GetLolaEventTypeId(event_name), GetLolaInstanceId(), ServiceElementType::EVENT}; + + return std::make_unique(*this, + event_properties, + event_fqn, + SizeInfo{size, alignment}); +} + +EventDataControlComposite Skeleton::CreateEventControlComposite(const ElementFqId element_fq_id, + const SkeletonEventProperties& element_properties) noexcept +{ + if (was_old_shm_region_reopened_) { + auto it_qm = control_qm_->event_controls_.find(element_fq_id); + if (it_qm != control_qm_->event_controls_.end()) { + + it_qm->second.data_control.RemoveAllocationsForWriting(); + + EventDataControl* asil_ctrl = nullptr; + if (control_asil_b_ != nullptr) { + auto it_asil = control_asil_b_->event_controls_.find(element_fq_id); + if (it_asil != control_asil_b_->event_controls_.end()) { + it_asil->second.data_control.RemoveAllocationsForWriting(); + asil_ctrl = &it_asil->second.data_control; + } + } + return EventDataControlComposite{&it_qm->second.data_control, asil_ctrl}; + } + } + + auto control_qm = control_qm_->event_controls_.emplace(std::piecewise_construct, + std::forward_as_tuple(element_fq_id), + std::forward_as_tuple(element_properties.number_of_slots, + element_properties.max_subscribers, + element_properties.enforce_max_samples, + control_qm_resource_->getMemoryResourceProxy())); + + EventDataControl* control_asil_result{nullptr}; + if (control_asil_resource_ != nullptr) + { + auto iterator = control_asil_b_->event_controls_.emplace(std::piecewise_construct, + std::forward_as_tuple(element_fq_id), + std::forward_as_tuple(element_properties.number_of_slots, + element_properties.max_subscribers, + element_properties.enforce_max_samples, + control_asil_resource_->getMemoryResourceProxy())); + control_asil_result = &iterator.first->second.data_control; + } + return EventDataControlComposite{&control_qm.first->second.data_control, control_asil_result}; +} + +std::pair, EventDataControlComposite> +Skeleton::CreateEventDataFromOpenedSharedMemory(const ElementFqId element_fq_id, + const SkeletonEventProperties& element_properties, + size_t sample_size, + size_t sample_alignment) noexcept +{ + // 1. Check if we are REOPENING an existing region + if (was_old_shm_region_reopened_) { + // Find existing data instead of constructing + auto it = storage_->events_.find(element_fq_id); + if (it != storage_->events_.end()) { + return {it->second, CreateEventControlComposite(element_fq_id, element_properties)}; + } + } + +// 2. If it's a fresh start OR the event wasn't found, ONLY THEN construct + auto* data_storage = storage_resource_->construct>( + sample_size * element_properties.number_of_slots, + memory::shared::PolymorphicOffsetPtrAllocator(storage_resource_->getMemoryResourceProxy())); + + storage_->events_.emplace(element_fq_id, data_storage); + + const DataTypeMetaInfo sample_meta_info{sample_size, static_cast(sample_alignment)}; + void* const event_data_raw_array = data_storage->data(); + storage_->events_metainfo_.emplace(element_fq_id, EventMetaInfo{sample_meta_info, event_data_raw_array}); + + return {data_storage, CreateEventControlComposite(element_fq_id, element_properties)}; +} + +std::pair, EventDataControlComposite> Skeleton::RegisterGeneric( + const ElementFqId element_fq_id, + const SkeletonEventProperties& element_properties, + size_t sample_size, + size_t sample_alignment) noexcept +{ + return CreateEventDataFromOpenedSharedMemory( + element_fq_id, element_properties, sample_size, sample_alignment); +} + ResultBlank Skeleton::OnServiceMethodsSubscribed(const ProxyInstanceIdentifier& proxy_instance_identifier, uid_t proxy_uid, const QualityType asil_level, @@ -1000,7 +1094,11 @@ ResultBlank Skeleton::OnServiceMethodsSubscribed(const ProxyInstanceIdentifier& bool Skeleton::IsProxyInAllowedConsumerList(const uid_t proxy_uid, const QualityType asil_level) const { - const auto& lola_service_instance_deployment = GetLolaServiceInstanceDeployment(identifier_); + const auto& instance_depl_info = InstanceIdentifierView{identifier_}.GetServiceInstanceDeployment(); + const auto* lola_service_instance_deployment_ptr = + std::get_if(&instance_depl_info.bindingInfo_); + SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD(lola_service_instance_deployment_ptr != nullptr); + const auto& lola_service_instance_deployment = *lola_service_instance_deployment_ptr; const auto& allowed_consumer = lola_service_instance_deployment.allowed_consumer_; // Check if there is an allowed consumer list for the specified quality (ASIL-B / QM) diff --git a/score/mw/com/impl/bindings/lola/skeleton.h b/score/mw/com/impl/bindings/lola/skeleton.h index ecbc9e07..f7746095 100644 --- a/score/mw/com/impl/bindings/lola/skeleton.h +++ b/score/mw/com/impl/bindings/lola/skeleton.h @@ -107,6 +107,15 @@ class Skeleton final : public SkeletonBinding return BindingType::kLoLa; }; + Result> + CreateGenericEventBinding(std::string_view event_name, size_t size, size_t alignment) noexcept override; + + std::pair, EventDataControlComposite> RegisterGeneric( + const ElementFqId element_fq_id, + const SkeletonEventProperties& element_properties, + size_t sample_size, + size_t sample_alignment) noexcept; + /// \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 @@ -126,6 +135,31 @@ class Skeleton final : public SkeletonBinding /// \return Events meta-info, if it has been registered, null else. score::cpp::optional GetEventMetaInfo(const ElementFqId element_fq_id) const; + /// @brief Checks if an event's control block is registered. + /// @param element_fq_id The fully qualified ID of the event. + /// @return True if the control block is registered, false otherwise. + bool IsEventControlRegistered(const ElementFqId element_fq_id) const noexcept; + + const LolaServiceInstanceDeployment& GetLolaServiceInstanceDeployment() const noexcept + { + return lola_service_instance_deployment_; + } + + LolaServiceId GetLolaServiceId() const noexcept + { + return lola_service_id_; + } + + LolaEventId GetLolaEventTypeId(std::string_view event_name) const noexcept + { + return lola_service_type_deployment_.events_.at(std::string(event_name)); + } + + LolaServiceInstanceId::InstanceId GetLolaInstanceId() const noexcept + { + return lola_instance_id_; + } + QualityType GetInstanceQualityType() const; /// \brief Cleans up all allocated slots for this SkeletonEvent of any previous running instance @@ -169,6 +203,15 @@ class Skeleton final : public SkeletonBinding const ElementFqId element_fq_id, const SkeletonEventProperties& element_properties); + EventDataControlComposite CreateEventControlComposite(const ElementFqId element_fq_id, + const SkeletonEventProperties& element_properties) noexcept; + + std::pair, EventDataControlComposite> + CreateEventDataFromOpenedSharedMemory(const ElementFqId element_fq_id, + const SkeletonEventProperties& element_properties, + size_t sample_size, + size_t sample_alignment) noexcept; + class ShmResourceStorageSizes { public: @@ -385,44 +428,7 @@ auto Skeleton::CreateEventDataFromOpenedSharedMemory(const ElementFqId element_f std::forward_as_tuple(sample_meta_info, event_data_raw_array)); SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD_MESSAGE(inserted_meta_info.second, "Couldn't register/emplace event-meta-info in data-section."); - auto control_qm = - control_qm_->event_controls_.emplace(std::piecewise_construct, - std::forward_as_tuple(element_fq_id), - std::forward_as_tuple(element_properties.number_of_slots, - element_properties.max_subscribers, - element_properties.enforce_max_samples, - control_qm_resource_->getMemoryResourceProxy())); - SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD_MESSAGE(control_qm.second, "Couldn't register/emplace event-meta-info in data-section."); - - EventDataControl* control_asil_result{nullptr}; - if (control_asil_resource_ != nullptr) - { - auto iterator = control_asil_b_->event_controls_.emplace( - std::piecewise_construct, - std::forward_as_tuple(element_fq_id), - std::forward_as_tuple(element_properties.number_of_slots, - element_properties.max_subscribers, - element_properties.enforce_max_samples, - control_asil_resource_->getMemoryResourceProxy())); - - // Suppress "AUTOSAR C++14 M7-5-1" rule. This rule declares: - // A function shall not return a reference or a pointer to an automatic variable (including parameters), defined - // within the function. - // Suppress "AUTOSAR C++14 M7-5-2": The address of an object with automatic storage shall not be assigned to - // another object that may persist after the first object has ceased to exist. - // The result pointer is still valid outside this method until Skeleton object (as a holder) is alive. - // coverity[autosar_cpp14_m7_5_1_violation] - // coverity[autosar_cpp14_m7_5_2_violation] - // coverity[autosar_cpp14_a3_8_1_violation] - control_asil_result = &iterator.first->second.data_control; - } - // clang-format off - // The lifetime of the "control_asil_result" object lasts as long as the Skeleton is alive. - // coverity[autosar_cpp14_m7_5_1_violation] - // coverity[autosar_cpp14_m7_5_2_violation] - // coverity[autosar_cpp14_a3_8_1_violation] - return {typed_event_data_storage_ptr, EventDataControlComposite{&control_qm.first->second.data_control, control_asil_result}}; - // clang-format on + return {typed_event_data_storage_ptr, CreateEventControlComposite(element_fq_id, element_properties)}; } } // namespace score::mw::com::impl::lola diff --git a/score/mw/com/impl/bindings/mock_binding/skeleton.h b/score/mw/com/impl/bindings/mock_binding/skeleton.h index bc75857b..613f00ff 100644 --- a/score/mw/com/impl/bindings/mock_binding/skeleton.h +++ b/score/mw/com/impl/bindings/mock_binding/skeleton.h @@ -34,6 +34,10 @@ class Skeleton : public SkeletonBinding (noexcept, override, final)); MOCK_METHOD(void, PrepareStopOffer, (std::optional), (noexcept, override, final)); MOCK_METHOD(BindingType, GetBindingType, (), (const, noexcept, override, final)); + MOCK_METHOD(Result>, + CreateGenericEventBinding, + (std::string_view event_name, size_t size, size_t alignment), + (noexcept, override, final)); }; class SkeletonFacade : public SkeletonBinding @@ -59,6 +63,12 @@ class SkeletonFacade : public SkeletonBinding return skeleton_.GetBindingType(); } + Result> + CreateGenericEventBinding(std::string_view event_name, size_t size, size_t alignment) noexcept override final + { + return skeleton_.CreateGenericEventBinding(event_name, size, alignment); + } + private: Skeleton& skeleton_; }; diff --git a/score/mw/com/impl/generic_skeleton.cpp b/score/mw/com/impl/generic_skeleton.cpp new file mode 100644 index 00000000..40828bb0 --- /dev/null +++ b/score/mw/com/impl/generic_skeleton.cpp @@ -0,0 +1,92 @@ +/******************************************************************************** + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#include "score/mw/com/impl/generic_skeleton.h" + +#include "score/mw/com/impl/com_error.h" +#include "score/mw/com/impl/plumbing/skeleton_binding_factory.h" +#include "score/mw/com/impl/runtime.h" +#include "score/mw/com/impl/size_info.h" +#include "score/mw/com/impl/skeleton_binding.h" + +namespace score::mw::com::impl +{ + +Result GenericSkeleton::Create(const InstanceSpecifier& specifier) noexcept +{ + const auto identifier_result = Runtime::getInstance().resolve(specifier); + if (identifier_result.empty()) + { + return MakeUnexpected(ComErrc::kInstanceIDCouldNotBeResolved); + } + + if (identifier_result.size() > 1) + { + score::mw::log::LogWarn("com") << "InstanceSpecifier resolved to more than one InstanceIdentifier. Using the first one."; + } + + return Create(identifier_result[0]); +} + +Result GenericSkeleton::Create(const InstanceIdentifier& identifier) noexcept +{ + auto binding = SkeletonBindingFactory::Create(identifier); + if (!binding) + { + return MakeUnexpected(ComErrc::kBindingFailure); + } + + return GenericSkeleton(identifier, std::move(binding)); +} + +Result GenericSkeleton::AddEvent(std::string_view name, const SizeInfo& size_info) noexcept +{ + if (SkeletonBaseView{*this}.IsOffered()) + { + return MakeUnexpected(ComErrc::kNotOffered); + } + + auto event_binding_result = + SkeletonBaseView{*this}.GetBinding()->CreateGenericEventBinding(name, size_info.size, size_info.alignment); + if (!event_binding_result.has_value()) + { + return MakeUnexpected(ComErrc::kNotOffered); + } + + auto emplace_result = events_.emplace( + name, std::make_unique(*this, name, std::move(event_binding_result).value())); + if (!emplace_result.second) + { + return MakeUnexpected(ComErrc::kEventNotExisting); + } + auto& event = *emplace_result.first->second; + SkeletonBaseView{*this}.RegisterEvent(name, event); + + return &event; +} + +Result GenericSkeleton::OfferService() noexcept +{ + return SkeletonBase::OfferService(); +} + +void GenericSkeleton::StopOfferService() noexcept +{ + SkeletonBase::StopOfferService(); +} + +GenericSkeleton::GenericSkeleton(const InstanceIdentifier& identifier, std::unique_ptr binding) + : SkeletonBase(std::move(binding), identifier) +{ +} + +} // namespace score::mw::com::impl \ No newline at end of file diff --git a/score/mw/com/impl/generic_skeleton.h b/score/mw/com/impl/generic_skeleton.h new file mode 100644 index 00000000..f696418a --- /dev/null +++ b/score/mw/com/impl/generic_skeleton.h @@ -0,0 +1,67 @@ +/******************************************************************************** + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#pragma once + +#include "score/mw/com/impl/generic_skeleton_event.h" +#include "score/mw/com/impl/instance_identifier.h" +#include "score/mw/com/impl/instance_specifier.h" +#include "score/mw/com/impl/skeleton_base.h" +#include "score/mw/com/impl/size_info.h" + +#include "score/result/result.h" + +#include +#include + +namespace score::mw::com::impl +{ + +class SkeletonBinding; + + +class GenericSkeleton : public SkeletonBase +{ + public: + /// @brief Creates a GenericSkeleton for a given instance specifier. + /// @param specifier The instance specifier. + /// @return A GenericSkeleton or an error. + static Result Create(const InstanceSpecifier& specifier) noexcept; + + /// @brief Creates a GenericSkeleton for a given instance identifier. + /// @param identifier The instance identifier. + /// @return A GenericSkeleton or an error. + static Result Create(const InstanceIdentifier& identifier) noexcept; + + /// @brief Adds a type-erased event to the skeleton. + /// + /// This must be called before OfferService(). + /// + /// @param name The name of the event. + /// @param size_info The size and alignment requirements for the event's sample data. + /// @return A reference to the created event or an error. + Result AddEvent(std::string_view name, const SizeInfo& size_info) noexcept; + + /// @brief Offers the service instance. + /// @return A blank result, or an error if offering fails. + Result OfferService() noexcept; + + /// @brief Stops offering the service instance. + void StopOfferService() noexcept; + + private: + GenericSkeleton(const InstanceIdentifier& identifier, std::unique_ptr binding); + + std::map> events_; +}; + +} // namespace score::mw::com::impl \ No newline at end of file diff --git a/score/mw/com/impl/generic_skeleton_event.cpp b/score/mw/com/impl/generic_skeleton_event.cpp new file mode 100644 index 00000000..92c730d1 --- /dev/null +++ b/score/mw/com/impl/generic_skeleton_event.cpp @@ -0,0 +1,64 @@ +/******************************************************************************** + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#include "score/mw/com/impl/generic_skeleton_event.h" +#include "score/mw/com/impl/generic_skeleton_event_binding.h" + +#include + +namespace score::mw::com::impl +{ + +GenericSkeletonEvent::GenericSkeletonEvent(SkeletonBase& skeleton_base, + const std::string_view event_name, + std::unique_ptr binding) + : SkeletonEventBase(skeleton_base, event_name, std::move(binding)) +{ +} + +Result GenericSkeletonEvent::Send(SampleAllocateePtr sample) noexcept +{ + auto* const binding = static_cast(binding_.get()); + std::cout<<"auto* const binding = static_cast(binding_.get());"<{sample}.As(); + std::cout<<"auto* const generic_sample_ptr = SampleAllocateePtrView{sample}.As();"<Send(generic_sample_ptr->GetControlSlotIndicator()); + return result; +} + +Result> GenericSkeletonEvent::Allocate() noexcept +{ + auto* binding = static_cast(binding_.get()); + auto result = binding->Allocate(); + if (!result.has_value()) + { + return MakeUnexpected>(ComErrc::kSampleAllocationFailure); + } + + auto deallocator = [binding_ptr = binding_.get()](lola::ControlSlotCompositeIndicator indicator) { + auto* const generic_binding = static_cast(binding_ptr); + generic_binding->Deallocate(indicator); + }; + + return MakeSampleAllocateePtr( + GenericEventSamplePtr(result.value().first, result.value().second, std::move(deallocator))); +} + +SizeInfo GenericSkeletonEvent::GetSizeInfo() const noexcept +{ + const auto* const binding = static_cast(binding_.get()); + const auto size_info_pair = binding->GetSizeInfo(); + return {size_info_pair.first, size_info_pair.second}; +} + +} // namespace score::mw::com::impl \ No newline at end of file diff --git a/score/mw/com/impl/generic_skeleton_event.h b/score/mw/com/impl/generic_skeleton_event.h new file mode 100644 index 00000000..a4a12af0 --- /dev/null +++ b/score/mw/com/impl/generic_skeleton_event.h @@ -0,0 +1,44 @@ +/******************************************************************************** + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#pragma once + +#include "score/mw/com/impl/skeleton_event_base.h" +#include "score/mw/com/impl/plumbing/sample_allocatee_ptr.h" +#include "score/mw/com/impl/size_info.h" + +#include "score/result/result.h" + +namespace score::mw::com::impl +{ + +class GenericSkeletonEventBinding; + + +class GenericSkeletonEvent : public SkeletonEventBase +{ + public: + GenericSkeletonEvent(SkeletonBase& skeleton_base, + const std::string_view event_name, + std::unique_ptr binding); + + + Result Send(SampleAllocateePtr sample) noexcept; + + + Result> Allocate() noexcept; + + + SizeInfo GetSizeInfo() const noexcept; +}; + +} // namespace score::mw::com::impl \ No newline at end of file diff --git a/score/mw/com/impl/generic_skeleton_event_binding.h b/score/mw/com/impl/generic_skeleton_event_binding.h new file mode 100644 index 00000000..c3a55361 --- /dev/null +++ b/score/mw/com/impl/generic_skeleton_event_binding.h @@ -0,0 +1,38 @@ +/******************************************************************************** + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional information + * regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#pragma once + +#include "score/mw/com/impl/skeleton_event_binding.h" +#include "score/mw/com/impl/bindings/lola/control_slot_composite_indicator.h" +#include "score/result/result.h" + +#include + +namespace score::mw::com::impl +{ + +class GenericSkeletonEventBinding : public SkeletonEventBindingBase +{ + public: + virtual Result Send(const void* data) noexcept = 0; + + virtual Result Send(lola::ControlSlotCompositeIndicator control_slot_indicator) noexcept = 0; + + virtual Result> Allocate() noexcept = 0; + + virtual void Deallocate(lola::ControlSlotCompositeIndicator control_slot_indicator) noexcept = 0; + + virtual std::pair GetSizeInfo() const noexcept = 0; +}; + +} // namespace score::mw::com::impl \ No newline at end of file diff --git a/score/mw/com/impl/generic_skeleton_test.cpp b/score/mw/com/impl/generic_skeleton_test.cpp new file mode 100644 index 00000000..072717d9 --- /dev/null +++ b/score/mw/com/impl/generic_skeleton_test.cpp @@ -0,0 +1,103 @@ +/******************************************************************************** + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#include "score/mw/com/impl/generic_skeleton.h" + +#include "score/mw/com/impl/bindings/mock_binding/skeleton.h" +#include "score/mw/com/impl/runtime_mock.h" +#include "score/mw/com/impl/test/dummy_instance_identifier_builder.h" +#include "score/mw/com/impl/test/runtime_mock_guard.h" +#include "score/mw/com/impl/test/skeleton_binding_factory_mock_guard.h" + +#include +#include + +namespace score::mw::com::impl +{ +namespace +{ + +using ::testing::_; +using ::testing::ByMove; +using ::testing::Return; + +class GenericSkeletonTest : public ::testing::Test +{ + public: + GenericSkeletonTest() + { + auto skeleton_binding_mock = std::make_unique(); + skeleton_binding_mock_ = skeleton_binding_mock.get(); + + ON_CALL(skeleton_binding_factory_mock_guard_.factory_mock_, Create(_)) + .WillByDefault(Return(ByMove(std::move(skeleton_binding_mock)))); + } + + RuntimeMockGuard runtime_mock_guard_{}; + SkeletonBindingFactoryMockGuard skeleton_binding_factory_mock_guard_{}; + mock_binding::Skeleton* skeleton_binding_mock_{nullptr}; + DummyInstanceIdentifierBuilder dummy_instance_identifier_builder_{}; +}; + +TEST_F(GenericSkeletonTest, CreateWithInstanceSpecifier) +{ + auto instance_specifier = InstanceSpecifier::Create("a/b/c").value(); + auto instance_identifier = dummy_instance_identifier_builder_.CreateInstanceIdentifier(); + + EXPECT_CALL(runtime_mock_guard_.runtime_mock_, ResolveInstanceIdentifier(instance_specifier)) + .WillOnce(Return(instance_identifier)); + + auto skeleton_result = GenericSkeleton::Create(instance_specifier); + ASSERT_TRUE(skeleton_result.has_value()); +} + +TEST_F(GenericSkeletonTest, CreateWithInstanceIdentifier) +{ + auto instance_identifier = dummy_instance_identifier_builder_.CreateInstanceIdentifier(); + auto skeleton_result = GenericSkeleton::Create(instance_identifier); + ASSERT_TRUE(skeleton_result.has_value()); +} + +TEST_F(GenericSkeletonTest, AddEvent) +{ + auto skeleton = GenericSkeleton::Create(dummy_instance_identifier_builder_.CreateInstanceIdentifier()).value(); + const SizeInfo size_info{16, 8}; + auto event_result = skeleton.AddEvent("MyEvent", size_info); + ASSERT_TRUE(event_result.has_value()); + + // Adding the same event again fails + auto second_event_result = skeleton.AddEvent("MyEvent", size_info); + ASSERT_FALSE(second_event_result.has_value()); +} + +TEST_F(GenericSkeletonTest, OfferAndStopOfferService) +{ + auto skeleton = GenericSkeleton::Create(dummy_instance_identifier_builder_.CreateInstanceIdentifier()).value(); + + EXPECT_CALL(*skeleton_binding_mock_, OfferService()).WillOnce(Return(score::result::Blank{})); + auto offer_result = skeleton.OfferService(); + EXPECT_TRUE(offer_result.has_value()); + + EXPECT_CALL(*skeleton_binding_mock_, StopOfferService()); + skeleton.StopOfferService(); +} + +TEST_F(GenericSkeletonTest, CreateFailsIfBindingFails) +{ + EXPECT_CALL(skeleton_binding_factory_mock_guard_.factory_mock_, Create(_)).WillOnce(Return(ByMove(nullptr))); + auto skeleton_result = GenericSkeleton::Create(dummy_instance_identifier_builder_.CreateInstanceIdentifier()); + ASSERT_FALSE(skeleton_result.has_value()); + EXPECT_EQ(skeleton_result.error(), ComErrc::kBindingFailure); +} + +} // namespace +} // namespace score::mw::com::impl \ No newline at end of file diff --git a/score/mw/com/impl/plumbing/sample_allocatee_ptr.h b/score/mw/com/impl/plumbing/sample_allocatee_ptr.h index c12d712d..6878847d 100644 --- a/score/mw/com/impl/plumbing/sample_allocatee_ptr.h +++ b/score/mw/com/impl/plumbing/sample_allocatee_ptr.h @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -26,6 +27,56 @@ namespace score::mw::com::impl { +/// @brief An RAII wrapper for a type-erased sample allocated by a GenericSkeletonEvent. +class GenericEventSamplePtr +{ + public: + using element_type = void; + + GenericEventSamplePtr(void* data, + lola::ControlSlotCompositeIndicator control_slot_indicator, + std::function deallocator) + : data_(data), control_slot_indicator_(control_slot_indicator), deallocator_(std::move(deallocator)) + { + } + + ~GenericEventSamplePtr() + { + if (data_ != nullptr) + { + deallocator_(control_slot_indicator_); + } + } + + GenericEventSamplePtr(const GenericEventSamplePtr&) = delete; + GenericEventSamplePtr& operator=(const GenericEventSamplePtr&) = delete; + + GenericEventSamplePtr(GenericEventSamplePtr&& other) noexcept + : data_(other.data_), + control_slot_indicator_(other.control_slot_indicator_), + deallocator_(std::move(other.deallocator_)) + { + other.data_ = nullptr; + } + + GenericEventSamplePtr& operator=(GenericEventSamplePtr&&) = default; + + void* get() const + { + return data_; + } + + lola::ControlSlotCompositeIndicator GetControlSlotIndicator() const + { + return control_slot_indicator_; + } + + private: + void* data_; + lola::ControlSlotCompositeIndicator control_slot_indicator_; + std::function deallocator_; +}; + /// \brief Pointer to a data sample allocated by the Communication Management implementation (mimics std::unique_ptr) /// /// \details We try to implement certain functionality that facades an std::unique_ptr, but some functionalities (e.g. @@ -338,6 +389,114 @@ auto MakeSampleAllocateePtr(T ptr) noexcept -> SampleAllocateePtr{std::move(ptr)}; } +/// \brief Template specialization of SampleAllocateePtr for void. +template <> +class SampleAllocateePtr +{ + public: + using pointer = void*; + using element_type = void; + + constexpr SampleAllocateePtr() noexcept : SampleAllocateePtr(score::cpp::blank{}) {} + constexpr explicit SampleAllocateePtr(std::nullptr_t) noexcept : SampleAllocateePtr() {} + + SampleAllocateePtr(const SampleAllocateePtr&) = delete; + SampleAllocateePtr& operator=(const SampleAllocateePtr&) & = delete; + + SampleAllocateePtr(SampleAllocateePtr&& other) noexcept : SampleAllocateePtr() + { + this->Swap(other); + } + + SampleAllocateePtr& operator=(SampleAllocateePtr&& other) & noexcept + { + this->Swap(other); + return *this; + } + + SampleAllocateePtr& operator=(std::nullptr_t) noexcept + { + reset(); + return *this; + } + + ~SampleAllocateePtr() noexcept = default; + + void reset() noexcept + { + auto visitor = score::cpp::overload( + [](lola::SampleAllocateePtr& internal_ptr) noexcept -> void { internal_ptr.reset(); }, + [](GenericEventSamplePtr& internal_ptr) noexcept -> void { + GenericEventSamplePtr temp = std::move(internal_ptr); + }, + [](const score::cpp::blank&) noexcept -> void {}); + std::visit(visitor, internal_); + } + + void Swap(SampleAllocateePtr& other) noexcept + { + using std::swap; + swap(internal_, other.internal_); + } + + pointer Get() const noexcept + { + auto visitor = score::cpp::overload( + [](const lola::SampleAllocateePtr& internal_ptr) noexcept -> pointer { return internal_ptr.get(); }, + [](const GenericEventSamplePtr& internal_ptr) noexcept -> pointer { return internal_ptr.get(); }, + [](const score::cpp::blank&) noexcept -> pointer { return nullptr; }); + return std::visit(visitor, internal_); + } + + explicit operator bool() const noexcept + { + auto visitor = score::cpp::overload( + [](const lola::SampleAllocateePtr& internal_ptr) noexcept -> bool { + return static_cast(internal_ptr); + }, + [](const GenericEventSamplePtr& internal_ptr) noexcept -> bool { return internal_ptr.get() != nullptr; }, + [](const score::cpp::blank&) noexcept -> bool { return false; }); + return std::visit(visitor, internal_); + } + + // operator* is intentionally omitted for void specialization. + + pointer operator->() const noexcept + { + auto visitor = score::cpp::overload( + [](const lola::SampleAllocateePtr& internal_ptr) noexcept -> pointer { return internal_ptr.get(); }, + [](const GenericEventSamplePtr& internal_ptr) noexcept -> pointer { return internal_ptr.get(); }, + [](const score::cpp::blank&) noexcept -> pointer { + std::terminate(); + return nullptr; + }); + return std::visit(visitor, internal_); + } + + private: + template + constexpr explicit SampleAllocateePtr(T ptr) : internal_{std::move(ptr)} + { + } + + template + friend auto MakeSampleAllocateePtr(T ptr) noexcept -> SampleAllocateePtr; + + template + friend class SampleAllocateePtrView; + + template + friend class SampleAllocateePtrMutableView; + + std::variant, GenericEventSamplePtr> internal_; +}; + +template <> +inline void swap(SampleAllocateePtr& lhs, SampleAllocateePtr& rhs) noexcept +{ + lhs.Swap(rhs); +} + /// \brief SampleAllocateePtr is user facing, in order to interact with its internals we provide a view towards it template class SampleAllocateePtrView diff --git a/score/mw/com/impl/size_info.h b/score/mw/com/impl/size_info.h new file mode 100644 index 00000000..d69131fa --- /dev/null +++ b/score/mw/com/impl/size_info.h @@ -0,0 +1,26 @@ +/******************************************************************************** + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#pragma once + +#include + +namespace score::mw::com +{ +/// @brief A struct to hold size and alignment information for generic type-erased data. +struct SizeInfo +{ + size_t size; + size_t alignment; +}; + +} // namespace score::mw::com \ No newline at end of file diff --git a/score/mw/com/impl/skeleton_base.h b/score/mw/com/impl/skeleton_base.h index e949652d..a5a05728 100644 --- a/score/mw/com/impl/skeleton_base.h +++ b/score/mw/com/impl/skeleton_base.h @@ -146,6 +146,11 @@ class SkeletonBaseView return skeleton_base_.binding_.get(); } + bool IsOffered() const + { + return skeleton_base_.service_offered_flag_.IsSet(); + } + void RegisterEvent(const std::string_view event_name, SkeletonEventBase& event) { const auto result = skeleton_base_.events_.emplace(event_name, event); diff --git a/score/mw/com/impl/skeleton_binding.h b/score/mw/com/impl/skeleton_binding.h index bc111ebe..67f867ea 100644 --- a/score/mw/com/impl/skeleton_binding.h +++ b/score/mw/com/impl/skeleton_binding.h @@ -16,7 +16,9 @@ #include "score/memory/shared/i_shared_memory_resource.h" #include "score/result/result.h" #include "score/mw/com/impl/binding_type.h" +#include "score/mw/com/impl/size_info.h" #include "score/mw/com/impl/service_element_type.h" +#include "score/mw/com/impl/generic_skeleton_event_binding.h" #include #include @@ -96,6 +98,14 @@ class SkeletonBinding /// \brief Gets the binding type of the binding virtual BindingType GetBindingType() const noexcept = 0; + + /// @brief Creates a binding for a generic event. + /// @param event_name The name of the event. + /// @param size The size for the event's data. + /// @param alignment The alignment for the event's data. + /// @return A unique pointer to the created binding or an error. + virtual Result> + CreateGenericEventBinding(std::string_view event_name, size_t size, size_t alignment) noexcept = 0; }; } // namespace score::mw::com::impl From 3edd8b65f8562c880eeb0d4a5f7717fdc3f6fbae Mon Sep 17 00:00:00 2001 From: Abhishek GOYAL Date: Fri, 9 Jan 2026 14:40:30 +0100 Subject: [PATCH 2/3] Enhance Generic Skeleton Code --- score/mw/com/impl/BUILD | 4 + score/mw/com/impl/bindings/lola/BUILD | 20 ++ .../bindings/lola/generic_skeleton_event.cpp | 82 ++++--- .../bindings/lola/generic_skeleton_event.h | 70 +++++- score/mw/com/impl/bindings/lola/skeleton.cpp | 208 +++++++++--------- score/mw/com/impl/bindings/lola/skeleton.h | 34 +-- .../com/impl/bindings/lola/skeleton_event.h | 189 ++++++++++------ .../com/impl/bindings/mock_binding/skeleton.h | 10 - score/mw/com/impl/com_error.h | 4 + score/mw/com/impl/generic_skeleton.cpp | 51 +++-- score/mw/com/impl/generic_skeleton.h | 12 +- score/mw/com/impl/generic_skeleton_event.cpp | 62 ++++-- .../com/impl/generic_skeleton_event_binding.h | 5 +- score/mw/com/impl/plumbing/BUILD | 15 ++ .../generic_skeleton_event_binding_factory.h | 48 ++++ .../com/impl/plumbing/sample_allocatee_ptr.h | 58 +---- ...ton_service_element_binding_factory_impl.h | 59 ++++- score/mw/com/impl/skeleton_binding.h | 7 - 18 files changed, 583 insertions(+), 355 deletions(-) create mode 100644 score/mw/com/impl/plumbing/generic_skeleton_event_binding_factory.h diff --git a/score/mw/com/impl/BUILD b/score/mw/com/impl/BUILD index a03ad876..5b03bb49 100644 --- a/score/mw/com/impl/BUILD +++ b/score/mw/com/impl/BUILD @@ -118,6 +118,7 @@ cc_library( features = COMPILER_WARNING_FEATURES, implementation_deps = [ ":error", + "//score/mw/com/impl/plumbing:generic_skeleton_event_binding_factory", "//score/mw/com/impl/plumbing", ], tags = ["FFI"], @@ -146,7 +147,9 @@ cc_library( "//score/mw/com:__subpackages__", ], deps = [ + "//score/mw/com/impl/bindings/lola:generic_skeleton_event", ":generic_skeleton_event_binding", + ":skeleton_base", ":skeleton_event_base", ":skeleton_event_binding", ":size_info", @@ -165,6 +168,7 @@ cc_library( visibility = [ "//score/mw/com/impl/bindings/lola:__pkg__", "//score/mw/com/impl/bindings/mock_binding:__pkg__", + "//score/mw/com/impl/plumbing:__pkg__", ], deps = [ ":skeleton_event_binding", diff --git a/score/mw/com/impl/bindings/lola/BUILD b/score/mw/com/impl/bindings/lola/BUILD index 584a435b..5ec4eecc 100644 --- a/score/mw/com/impl/bindings/lola/BUILD +++ b/score/mw/com/impl/bindings/lola/BUILD @@ -240,6 +240,26 @@ cc_library( ], ) +cc_library( + name = "generic_skeleton_event", + srcs = ["generic_skeleton_event.cpp"], + hdrs = ["generic_skeleton_event.h"], + features = COMPILER_WARNING_FEATURES, + tags = ["FFI"], + visibility = [ + "//score/mw/com/impl:__subpackages__", + ], + deps = [ + ":event", + ":skeleton", + "//score/mw/com/impl:error", + "//score/mw/com/impl:generic_skeleton_event_binding", + "//score/mw/com/impl:size_info", + "//score/mw/com/impl/tracing:skeleton_event_tracing", + ":type_erased_sample_ptrs_guard", + ], +) + cc_library( name = "skeleton", srcs = [ 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 1ec2b3e3..ad701b67 100644 --- a/score/mw/com/impl/bindings/lola/generic_skeleton_event.cpp +++ b/score/mw/com/impl/bindings/lola/generic_skeleton_event.cpp @@ -12,7 +12,10 @@ ********************************************************************************/ #include "score/mw/com/impl/bindings/lola/generic_skeleton_event.h" #include "score/mw/com/impl/bindings/lola/skeleton.h" +#include "score/mw/com/impl/bindings/lola/skeleton_event.h" #include "score/mw/com/impl/bindings/lola/skeleton_event_properties.h" +#include "score/mw/com/impl/runtime.h" +#include "score/mw/com/impl/tracing/skeleton_event_tracing.h" namespace score::mw::com::impl::lola { @@ -27,18 +30,15 @@ GenericSkeletonEvent::GenericSkeletonEvent(Skeleton& parent, ResultBlank GenericSkeletonEvent::PrepareOffer() noexcept { - auto [data_storage, control_composite] = + std::tie(data_storage_, control_) = parent_.RegisterGeneric(event_fqn_, event_properties_, size_info_.size, size_info_.alignment); - - data_storage_ = data_storage; - control_.emplace(std::move(control_composite)); + PrepareOfferImpl(*this); return {}; } Result GenericSkeletonEvent::Send(const void* /*data*/) noexcept { - return MakeUnexpected(ComErrc::kIllegalUseOfAllocate); } @@ -46,32 +46,65 @@ Result GenericSkeletonEvent::Send(lola::ControlSlotCompositeIndica { SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD(control_.has_value()); control_.value().EventReady(control_slot_indicator, ++current_timestamp_); + + // Only call NotifyEvent if there are any registered receive handlers for each quality level. + // This avoids the expensive lock operation in the common case where no handlers are registered. + // Using memory_order_relaxed is safe here as this is an optimisation, if we miss a very recent + // handler registration, the next Send() will pick it up. + if (qm_event_update_notifications_registered_.load() && !qm_disconnect_) + { + GetBindingRuntime(BindingType::kLoLa) + .GetLolaMessaging() + .NotifyEvent(QualityType::kASIL_QM, event_fqn_); + } + if (asil_b_event_update_notifications_registered_.load() && parent_.GetInstanceQualityType() == QualityType::kASIL_B) + { + GetBindingRuntime(BindingType::kLoLa) + .GetLolaMessaging() + .NotifyEvent(QualityType::kASIL_B, event_fqn_); + } + return {}; } -Result> GenericSkeletonEvent::Allocate() noexcept +Result> GenericSkeletonEvent::Allocate() noexcept { - SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD(control_.has_value()); - auto slot = control_.value().AllocateNextSlot(); - - if (!slot.IsValidQM() && !slot.IsValidAsilB()) + if (!control_.has_value()) { - return MakeUnexpected>( - ComErrc::kSampleAllocationFailure); + ::score::mw::log::LogError("lola") << "Tried to allocate event, but the EventDataControl does not exist!"; + return MakeUnexpected(ComErrc::kBindingFailure); } + const auto slot = control_.value().AllocateNextSlot(); - - auto* data_storage = data_storage_.get>(); - SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD(data_storage != nullptr); - auto* data_base_ptr = data_storage->data(); - void* data_ptr = data_base_ptr + (slot.GetIndex() * size_info_.size); - return std::make_pair(data_ptr, slot); -} + if (!qm_disconnect_ && control_->GetAsilBEventDataControl().has_value() && !slot.IsValidQM()) + { + qm_disconnect_ = true; + score::mw::log::LogWarn("lola") + << __func__ << __LINE__ + << "Disconnecting unsafe QM consumers as slot allocation failed on an ASIL-B enabled event: " << event_fqn_; + parent_.DisconnectQmConsumers(); + } -void GenericSkeletonEvent::Deallocate(lola::ControlSlotCompositeIndicator control_slot_indicator) noexcept -{ - SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD(control_.has_value()); - control_.value().Discard(control_slot_indicator); + if (slot.IsValidQM() || slot.IsValidAsilB()) + { + auto* data_storage = data_storage_.get>(); + SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD(data_storage != nullptr); + // The at() method on EventDataStorage (which is a DynamicArray) correctly handles + // the pointer arithmetic based on its template type (std::uint8_t). + // We multiply by size_info_.size here because the underlying storage is a byte array. + void* data_ptr = &data_storage->at(static_cast(slot.GetIndex()) * size_info_.size); + return lola::SampleAllocateePtr(data_ptr, control_.value(), slot); + } + else + { + if (!event_properties_.enforce_max_samples) + { + ::score::mw::log::LogError("lola") + << "GenericSkeletonEvent: Allocation of event slot failed. Hint: enforceMaxSamples was " + "disabled by config. Might be the root cause!"; + } + return MakeUnexpected(ComErrc::kBindingFailure); + } } std::pair GenericSkeletonEvent::GetSizeInfo() const noexcept @@ -81,8 +114,7 @@ std::pair GenericSkeletonEvent::GetSizeInfo() const noexcept void GenericSkeletonEvent::PrepareStopOffer() noexcept { - control_.reset(); - data_storage_ = nullptr; + PrepareStopOfferImpl(*this); } BindingType GenericSkeletonEvent::GetBindingType() const noexcept 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 8ad349aa..15c9abde 100644 --- a/score/mw/com/impl/bindings/lola/generic_skeleton_event.h +++ b/score/mw/com/impl/bindings/lola/generic_skeleton_event.h @@ -20,11 +20,14 @@ #include "score/memory/shared/offset_ptr.h" #include "score/mw/com/impl/bindings/lola/event_slot_status.h" +#include "score/mw/com/impl/bindings/lola/transaction_log_registration_guard.h" +#include "score/mw/com/impl/bindings/lola/type_erased_sample_ptrs_guard.h" #include "score/mw/com/impl/tracing/skeleton_event_tracing_data.h" namespace score::mw::com::impl::lola { +class TransactionLogRegistrationGuard; class Skeleton; /// @brief The LoLa binding implementation for a generic skeleton event. @@ -40,9 +43,7 @@ class GenericSkeletonEvent : public GenericSkeletonEventBinding Result Send(lola::ControlSlotCompositeIndicator control_slot_indicator) noexcept override; - Result> Allocate() noexcept override; - - void Deallocate(lola::ControlSlotCompositeIndicator control_slot_indicator) noexcept override; + Result> Allocate() noexcept override; std::pair GetSizeInfo() const noexcept override; @@ -62,10 +63,69 @@ class GenericSkeletonEvent : public GenericSkeletonEventBinding SizeInfo size_info_; const SkeletonEventProperties event_properties_; const ElementFqId event_fqn_; - score::cpp::optional control_; - std::atomic current_timestamp_{0U}; + score::cpp::optional control_{}; + EventSlotStatus::EventTimeStamp current_timestamp_{0U}; score::memory::shared::OffsetPtr data_storage_{nullptr}; + bool qm_disconnect_{false}; impl::tracing::SkeletonEventTracingData tracing_data_{}; + std::atomic qm_event_update_notifications_registered_{false}; + std::atomic asil_b_event_update_notifications_registered_{false}; + std::optional transaction_log_registration_guard_{}; + std::optional type_erased_sample_ptrs_guard_{}; + + public: + // The following methods are public but intended for use by PrepareOfferImpl helper + impl::tracing::SkeletonEventTracingData& GetTracingData() + { + return tracing_data_; + } + + void EmplaceTransactionLogRegistrationGuard() + { + score::cpp::ignore = transaction_log_registration_guard_.emplace( + TransactionLogRegistrationGuard::Create(control_.value().GetQmEventDataControl())); + } + + void EmplaceTypeErasedSamplePtrsGuard() + { + score::cpp::ignore = type_erased_sample_ptrs_guard_.emplace(tracing_data_.service_element_tracing_data); + } + + void UpdateCurrentTimestamp() + { + current_timestamp_ = control_.value().GetLatestTimestamp(); + } + + Skeleton& GetParent() + { + return parent_; + } + + const ElementFqId& GetElementFQId() const + { + return event_fqn_; + } + + void SetQmNotificationsRegistered(bool value) + { + qm_event_update_notifications_registered_.store(value); + } + + void SetAsilBNotificationsRegistered(bool value) + { + asil_b_event_update_notifications_registered_.store(value); + } + + void ResetGuards() noexcept + { + type_erased_sample_ptrs_guard_.reset(); + if (control_.has_value()) + { + transaction_log_registration_guard_.reset(); + } + control_.reset(); + data_storage_ = nullptr; + } }; } // namespace score::mw::com::impl::lola \ No newline at end of file diff --git a/score/mw/com/impl/bindings/lola/skeleton.cpp b/score/mw/com/impl/bindings/lola/skeleton.cpp index c68dc3f3..8b9a0cb3 100644 --- a/score/mw/com/impl/bindings/lola/skeleton.cpp +++ b/score/mw/com/impl/bindings/lola/skeleton.cpp @@ -75,6 +75,20 @@ const LolaServiceTypeDeployment& GetLolaServiceTypeDeployment(const InstanceIden return *lola_service_type_deployment_ptr; } +const LolaServiceInstanceDeployment& GetLolaServiceInstanceDeployment(const InstanceIdentifier& identifier) +{ + const auto& instance_depl_info = InstanceIdentifierView{identifier}.GetServiceInstanceDeployment(); + const auto* lola_service_instance_deployment_ptr = + std::get_if(&instance_depl_info.bindingInfo_); + if (lola_service_instance_deployment_ptr == nullptr) + { + score::mw::log::LogError("lola") << "GetLolaServiceInstanceDeployment: Wrong Binding! ServiceInstanceDeployment " + "doesn't contain a LoLa deployment!"; + std::terminate(); + } + return *lola_service_instance_deployment_ptr; +} + ServiceDataControl* GetServiceDataControlSkeletonSide(const memory::shared::ManagedMemoryResource& control) { // Suppress "AUTOSAR C++14 M5-2-8" rule. The rule declares: @@ -98,6 +112,22 @@ ServiceDataStorage* GetServiceDataStorageSkeletonSide(const memory::shared::Mana return service_data_storage; } +std::optional CreateOrOpenServiceInstanceExistenceMarkerFile( + const LolaServiceInstanceId::InstanceId lola_instance_id, + const IPartialRestartPathBuilder& partial_restart_path_builder) +{ + auto service_instance_existence_marker_file_path = + partial_restart_path_builder.GetServiceInstanceExistenceMarkerFilePath(lola_instance_id); + + // The instance existence marker file can be opened in the case that another skeleton of the same service currently + // exists or that a skeleton of the same service previously crashed. We cannot determine which is true until we try + // to flock the file. Therefore, we do not take ownership on construction and take ownership later if we can + // exclusively flock the file. + bool take_ownership{false}; + return memory::shared::LockFile::CreateOrOpen(std::move(service_instance_existence_marker_file_path), + take_ownership); +} + enum class ShmObjectType : std::uint8_t { kControl_QM = 0x00, @@ -193,42 +223,32 @@ std::unique_ptr Skeleton::Create(const InstanceIdentifier& identifier, return nullptr; } - // --- FIX 1: Only declare these ONCE --- - const auto& instance_depl_info = InstanceIdentifierView{identifier}.GetServiceInstanceDeployment(); - const auto* lola_service_instance_deployment_ptr = - std::get_if(&instance_depl_info.bindingInfo_); - SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD(lola_service_instance_deployment_ptr != nullptr); - const auto& lola_service_instance_deployment = *lola_service_instance_deployment_ptr; - + const auto& lola_service_instance_deployment = GetLolaServiceInstanceDeployment(identifier); const auto lola_instance_id = lola_service_instance_deployment.instance_id_.value().GetId(); - auto service_instance_existence_marker_file_path = - partial_restart_path_builder->GetServiceInstanceExistenceMarkerFilePath(lola_instance_id); - - // --- FIX 2: Correct LockFile logic (only one declaration) --- - bool take_ownership{true}; - auto service_instance_existence_marker_file = memory::shared::LockFile::CreateOrOpen( - std::move(service_instance_existence_marker_file_path), - take_ownership); + auto service_instance_existence_marker_file = + CreateOrOpenServiceInstanceExistenceMarkerFile(lola_instance_id, *partial_restart_path_builder); if (!service_instance_existence_marker_file.has_value()) { score::mw::log::LogError("lola") << "Could not create or open service instance existence marker file."; return nullptr; } - + auto service_instance_existence_mutex_and_lock = std::make_unique>( *service_instance_existence_marker_file); if (!service_instance_existence_mutex_and_lock->TryLock()) { score::mw::log::LogError("lola") - << "Flock try_lock failed: Another Skeleton could have already flocked the marker file."; + << "Flock try_lock failed: Another Skeleton could have already flocked the marker file and is " + "actively offering the same service instance."; return nullptr; } - - // --- FIX 3: Use the helper to get the type deployment --- + const auto& lola_service_type_deployment = GetLolaServiceTypeDeployment(identifier); - + // Since we were able to flock the existence marker file, it means that either we created it or the skeleton that + // created it previously crashed. Either way, we take ownership of the LockFile so that it's destroyed when this + // Skeleton is destroyed. service_instance_existence_marker_file.value().TakeOwnership(); return std::make_unique(identifier, lola_service_instance_deployment, @@ -830,18 +850,6 @@ score::cpp::optional Skeleton::GetEventMetaInfo(const ElementFqId } } -bool Skeleton::IsEventControlRegistered(const ElementFqId element_fq_id) const noexcept -{ - if (control_qm_ == nullptr) - { - return false; - } - const bool found_qm = (control_qm_->event_controls_.count(element_fq_id) > 0); - - // For ASIL-B, it must be in both. For QM, control_asil_b_ is nullptr. - return found_qm && (control_asil_b_ == nullptr || control_asil_b_->event_controls_.count(element_fq_id) > 0); -} - QualityType Skeleton::GetInstanceQualityType() const { return InstanceIdentifierView{identifier_}.GetServiceInstanceDeployment().asilLevel_; @@ -913,92 +921,72 @@ void Skeleton::InitializeSharedMemoryForControl( control = memory->construct(memory->getMemoryResourceProxy()); } -Result> -Skeleton::CreateGenericEventBinding(std::string_view event_name, size_t size, size_t alignment) noexcept -{ - const auto& event_deployment = GetServiceElementInstanceDeployment( - lola_service_instance_deployment_, std::string(event_name)); - - const SkeletonEventProperties event_properties{event_deployment.GetNumberOfSampleSlots().value_or(1U), - event_deployment.max_subscribers_.value_or(1U), - event_deployment.enforce_max_samples_}; - - const ElementFqId event_fqn{ - GetLolaServiceId(), GetLolaEventTypeId(event_name), GetLolaInstanceId(), ServiceElementType::EVENT}; - - return std::make_unique(*this, - event_properties, - event_fqn, - SizeInfo{size, alignment}); -} - EventDataControlComposite Skeleton::CreateEventControlComposite(const ElementFqId element_fq_id, const SkeletonEventProperties& element_properties) noexcept { - if (was_old_shm_region_reopened_) { - auto it_qm = control_qm_->event_controls_.find(element_fq_id); - if (it_qm != control_qm_->event_controls_.end()) { - - it_qm->second.data_control.RemoveAllocationsForWriting(); - - EventDataControl* asil_ctrl = nullptr; - if (control_asil_b_ != nullptr) { - auto it_asil = control_asil_b_->event_controls_.find(element_fq_id); - if (it_asil != control_asil_b_->event_controls_.end()) { - it_asil->second.data_control.RemoveAllocationsForWriting(); - asil_ctrl = &it_asil->second.data_control; - } - } - return EventDataControlComposite{&it_qm->second.data_control, asil_ctrl}; - } - } - auto control_qm = control_qm_->event_controls_.emplace(std::piecewise_construct, std::forward_as_tuple(element_fq_id), std::forward_as_tuple(element_properties.number_of_slots, element_properties.max_subscribers, element_properties.enforce_max_samples, control_qm_resource_->getMemoryResourceProxy())); + SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD_MESSAGE(control_qm.second, "Couldn't register/emplace event-meta-info in data-section."); EventDataControl* control_asil_result{nullptr}; if (control_asil_resource_ != nullptr) { - auto iterator = control_asil_b_->event_controls_.emplace(std::piecewise_construct, - std::forward_as_tuple(element_fq_id), - std::forward_as_tuple(element_properties.number_of_slots, - element_properties.max_subscribers, - element_properties.enforce_max_samples, - control_asil_resource_->getMemoryResourceProxy())); + auto iterator = control_asil_b_->event_controls_.emplace( + std::piecewise_construct, + std::forward_as_tuple(element_fq_id), + std::forward_as_tuple(element_properties.number_of_slots, + element_properties.max_subscribers, + element_properties.enforce_max_samples, + control_asil_resource_->getMemoryResourceProxy())); + + // Suppress "AUTOSAR C++14 M7-5-1" rule. This rule declares: + // A function shall not return a reference or a pointer to an automatic variable (including parameters), defined + // within the function. + // Suppress "AUTOSAR C++14 M7-5-2": The address of an object with automatic storage shall not be assigned to + // another object that may persist after the first object has ceased to exist. + // The result pointer is still valid outside this method until Skeleton object (as a holder) is alive. + // coverity[autosar_cpp14_m7_5_1_violation] + // coverity[autosar_cpp14_m7_5_2_violation] + // coverity[autosar_cpp14_a3_8_1_violation] control_asil_result = &iterator.first->second.data_control; } + // clang-format off + // The lifetime of the "control_asil_result" object lasts as long as the Skeleton is alive. + // coverity[autosar_cpp14_m7_5_1_violation] + // coverity[autosar_cpp14_m7_5_2_violation] + // coverity[autosar_cpp14_a3_8_1_violation] return EventDataControlComposite{&control_qm.first->second.data_control, control_asil_result}; } -std::pair, EventDataControlComposite> -Skeleton::CreateEventDataFromOpenedSharedMemory(const ElementFqId element_fq_id, - const SkeletonEventProperties& element_properties, - size_t sample_size, - size_t sample_alignment) noexcept +std::pair, EventDataControlComposite> +Skeleton::CreateEventDataFromOpenedSharedMemory( + const ElementFqId element_fq_id, + const SkeletonEventProperties& element_properties, + size_t sample_size, + size_t sample_alignment) noexcept { - // 1. Check if we are REOPENING an existing region - if (was_old_shm_region_reopened_) { - // Find existing data instead of constructing - auto it = storage_->events_.find(element_fq_id); - if (it != storage_->events_.end()) { - return {it->second, CreateEventControlComposite(element_fq_id, element_properties)}; - } - } - -// 2. If it's a fresh start OR the event wasn't found, ONLY THEN construct auto* data_storage = storage_resource_->construct>( sample_size * element_properties.number_of_slots, memory::shared::PolymorphicOffsetPtrAllocator(storage_resource_->getMemoryResourceProxy())); - storage_->events_.emplace(element_fq_id, data_storage); + 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 DataTypeMetaInfo sample_meta_info{sample_size, static_cast(sample_alignment)}; void* const event_data_raw_array = data_storage->data(); - storage_->events_metainfo_.emplace(element_fq_id, EventMetaInfo{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_meta_info, event_data_raw_array)); + SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD_MESSAGE(inserted_meta_info.second, + "Couldn't register/emplace event-meta-info in data-section."); return {data_storage, CreateEventControlComposite(element_fq_id, element_properties)}; } @@ -1006,11 +994,33 @@ Skeleton::CreateEventDataFromOpenedSharedMemory(const ElementFqId element_fq_id, std::pair, EventDataControlComposite> Skeleton::RegisterGeneric( const ElementFqId element_fq_id, const SkeletonEventProperties& element_properties, - size_t sample_size, - size_t sample_alignment) noexcept + const size_t sample_size, + const size_t sample_alignment) noexcept { - return CreateEventDataFromOpenedSharedMemory( - element_fq_id, element_properties, sample_size, sample_alignment); + if (was_old_shm_region_reopened_) + { + auto [data_storage, control_composite] = OpenEventDataFromOpenedSharedMemory(element_fq_id); + + auto& event_data_control_qm = control_composite.GetQmEventDataControl(); + auto rollback_result = event_data_control_qm.GetTransactionLogSet().RollbackSkeletonTracingTransactions( + [&event_data_control_qm](const TransactionLog::SlotIndexType slot_index) { + event_data_control_qm.DereferenceEventWithoutTransactionLogging(slot_index); + }); + if (!rollback_result.has_value()) + { + ::score::mw::log::LogWarn("lola") + << "SkeletonEvent: PrepareOffer failed: Could not rollback tracing consumer after " + "crash. Disabling tracing."; + impl::Runtime::getInstance().GetTracingRuntime()->DisableTracing(); + } + + return {data_storage, control_composite}; + } + else + { + return CreateEventDataFromOpenedSharedMemory( + element_fq_id, element_properties, sample_size, sample_alignment); + } } ResultBlank Skeleton::OnServiceMethodsSubscribed(const ProxyInstanceIdentifier& proxy_instance_identifier, @@ -1094,11 +1104,7 @@ ResultBlank Skeleton::OnServiceMethodsSubscribed(const ProxyInstanceIdentifier& bool Skeleton::IsProxyInAllowedConsumerList(const uid_t proxy_uid, const QualityType asil_level) const { - const auto& instance_depl_info = InstanceIdentifierView{identifier_}.GetServiceInstanceDeployment(); - const auto* lola_service_instance_deployment_ptr = - std::get_if(&instance_depl_info.bindingInfo_); - SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD(lola_service_instance_deployment_ptr != nullptr); - const auto& lola_service_instance_deployment = *lola_service_instance_deployment_ptr; + const auto& lola_service_instance_deployment = GetLolaServiceInstanceDeployment(identifier_); const auto& allowed_consumer = lola_service_instance_deployment.allowed_consumer_; // Check if there is an allowed consumer list for the specified quality (ASIL-B / QM) diff --git a/score/mw/com/impl/bindings/lola/skeleton.h b/score/mw/com/impl/bindings/lola/skeleton.h index f7746095..d1ba3f6e 100644 --- a/score/mw/com/impl/bindings/lola/skeleton.h +++ b/score/mw/com/impl/bindings/lola/skeleton.h @@ -107,14 +107,11 @@ class Skeleton final : public SkeletonBinding return BindingType::kLoLa; }; - Result> - CreateGenericEventBinding(std::string_view event_name, size_t size, size_t alignment) noexcept override; - std::pair, EventDataControlComposite> RegisterGeneric( const ElementFqId element_fq_id, const SkeletonEventProperties& element_properties, - size_t sample_size, - size_t sample_alignment) noexcept; + const size_t sample_size, + const size_t sample_alignment) noexcept; /// \brief Enables dynamic registration of Events at the Skeleton. /// \tparam SampleType The type of the event @@ -135,31 +132,6 @@ class Skeleton final : public SkeletonBinding /// \return Events meta-info, if it has been registered, null else. score::cpp::optional GetEventMetaInfo(const ElementFqId element_fq_id) const; - /// @brief Checks if an event's control block is registered. - /// @param element_fq_id The fully qualified ID of the event. - /// @return True if the control block is registered, false otherwise. - bool IsEventControlRegistered(const ElementFqId element_fq_id) const noexcept; - - const LolaServiceInstanceDeployment& GetLolaServiceInstanceDeployment() const noexcept - { - return lola_service_instance_deployment_; - } - - LolaServiceId GetLolaServiceId() const noexcept - { - return lola_service_id_; - } - - LolaEventId GetLolaEventTypeId(std::string_view event_name) const noexcept - { - return lola_service_type_deployment_.events_.at(std::string(event_name)); - } - - LolaServiceInstanceId::InstanceId GetLolaInstanceId() const noexcept - { - return lola_instance_id_; - } - QualityType GetInstanceQualityType() const; /// \brief Cleans up all allocated slots for this SkeletonEvent of any previous running instance @@ -251,7 +223,7 @@ class Skeleton final : public SkeletonBinding pid_t proxy_pid); static MethodData& GetMethodData(const memory::shared::ManagedMemoryResource& resource); - /// \brief Checks whether the Proxy which sent a notification to the Skeleton that it subscribed to a method is in + /// \brief Checks whether the Proxy which sent a notification to the Skeleton that it subscribed to a method is in /// the allowed_consumers list in the configuration. bool IsProxyInAllowedConsumerList(const uid_t proxy_uid, const QualityType asil_level) const; diff --git a/score/mw/com/impl/bindings/lola/skeleton_event.h b/score/mw/com/impl/bindings/lola/skeleton_event.h index 3246dc10..0ddac8ba 100644 --- a/score/mw/com/impl/bindings/lola/skeleton_event.h +++ b/score/mw/com/impl/bindings/lola/skeleton_event.h @@ -44,6 +44,82 @@ namespace score::mw::com::impl::lola { +namespace +{ + +template +void PrepareOfferImpl(EventType& event) +{ + const bool tracing_globally_enabled = ((impl::Runtime::getInstance().GetTracingRuntime() != nullptr) && + (impl::Runtime::getInstance().GetTracingRuntime()->IsTracingEnabled())); + if (!tracing_globally_enabled) + { + // in case tracing is globally disabled, this will never switch back to enable. Thus, we can directly disable + // all trace points for this event. This avoids any further lookups to the tracing runtime during Send() calls. + DisableAllTracePoints(event.GetTracingData()); + } + + const bool tracing_for_skeleton_event_enabled = + event.GetTracingData().enable_send || event.GetTracingData().enable_send_with_allocate; + // LCOV_EXCL_BR_START (Tool incorrectly marks the decision as "Decision couldn't be analyzed" despite all lines in + // both branches (true / false) being covered. "Decision couldn't be analyzed" only appeared after changing the code + // within the if statement (without changing the condition / tests). Suppression can be removed when bug is fixed in + // Ticket-188259). + if (tracing_for_skeleton_event_enabled) + { + // LCOV_EXCL_BR_STOP + event.EmplaceTransactionLogRegistrationGuard(); + event.EmplaceTypeErasedSamplePtrsGuard(); + } + + event.UpdateCurrentTimestamp(); + + // Register callbacks to be notified when event notification existence changes. + // This allows us to optimise the Send() path by skipping NotifyEvent() when no handlers are registered. + // Separate callbacks for QM and ASIL-B update their respective atomic flags for lock-free access. + if (event.GetParent().GetInstanceQualityType() == QualityType::kASIL_QM) + { + GetBindingRuntime(BindingType::kLoLa) + .GetLolaMessaging() + .RegisterEventNotificationExistenceChangedCallback( + QualityType::kASIL_QM, event.GetElementFQId(), [&event](const bool has_handlers) noexcept { + event.SetQmNotificationsRegistered(has_handlers); + }); + } + if (event.GetParent().GetInstanceQualityType() == QualityType::kASIL_B) + { + GetBindingRuntime(BindingType::kLoLa) + .GetLolaMessaging() + .RegisterEventNotificationExistenceChangedCallback( + QualityType::kASIL_B, event.GetElementFQId(), [&event](const bool has_handlers) noexcept { + event.SetAsilBNotificationsRegistered(has_handlers); + }); + } +} + +template +void PrepareStopOfferImpl(EventType& event) noexcept +{ + // Unregister event notification existence changed callbacks + GetBindingRuntime(BindingType::kLoLa) + .GetLolaMessaging() + .UnregisterEventNotificationExistenceChangedCallback(QualityType::kASIL_QM, event.GetElementFQId()); + + if (event.GetParent().GetInstanceQualityType() == QualityType::kASIL_B) + { + GetBindingRuntime(BindingType::kLoLa) + .GetLolaMessaging() + .UnregisterEventNotificationExistenceChangedCallback(QualityType::kASIL_B, event.GetElementFQId()); + } + + // Reset the flags to indicate no handlers are registered + event.SetQmNotificationsRegistered(false); + event.SetAsilBNotificationsRegistered(false); + + event.ResetGuards(); +} + +} // namespace /// \brief Represents a binding specific instance (LoLa) of an event within a skeleton. It can be used to send events /// via Shared Memory. It will be created via a Factory Method, that will instantiate this class based on deployment @@ -110,6 +186,52 @@ class SkeletonEvent final : public SkeletonEventBinding return event_fqn_; }; + // The following methods are public but intended for use by PrepareOfferImpl helper + impl::tracing::SkeletonEventTracingData& GetTracingData() + { + return skeleton_event_tracing_data_; + } + + void EmplaceTransactionLogRegistrationGuard() + { + score::cpp::ignore = transaction_log_registration_guard_.emplace( + TransactionLogRegistrationGuard::Create(event_data_control_composite_->GetQmEventDataControl())); + } + + void EmplaceTypeErasedSamplePtrsGuard() + { + score::cpp::ignore = type_erased_sample_ptrs_guard_.emplace(skeleton_event_tracing_data_.service_element_tracing_data); + } + + void UpdateCurrentTimestamp() + { + current_timestamp_ = event_data_control_composite_.value().GetLatestTimestamp(); + } + + Skeleton& GetParent() + { + return parent_; + } + + void SetQmNotificationsRegistered(bool value) + { + qm_event_update_notifications_registered_.store(value); + } + + void SetAsilBNotificationsRegistered(bool value) + { + asil_b_event_update_notifications_registered_.store(value); + } + + void ResetGuards() noexcept + { + type_erased_sample_ptrs_guard_.reset(); + if (event_data_control_composite_.has_value()) + { + transaction_log_registration_guard_.reset(); + } + } + private: Skeleton& parent_; const ElementFqId event_fqn_; @@ -285,51 +407,8 @@ ResultBlank SkeletonEvent::PrepareOffer() noexcept SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD_MESSAGE(event_data_control_composite_.has_value(), "Defensive programming as event_data_control_composite_ is set by Register above."); - current_timestamp_ = event_data_control_composite_.value().GetLatestTimestamp(); - const bool tracing_globally_enabled = ((impl::Runtime::getInstance().GetTracingRuntime() != nullptr) && - (impl::Runtime::getInstance().GetTracingRuntime()->IsTracingEnabled())); - if (!tracing_globally_enabled) - { - // in case tracing is globally disabled, this will never switch back to enable. Thus, we can directly disable - // all trace points for this event. This avoids any further lookups to the tracing runtime during Send() calls. - DisableAllTracePoints(skeleton_event_tracing_data_); - } - const bool tracing_for_skeleton_event_enabled = - skeleton_event_tracing_data_.enable_send || skeleton_event_tracing_data_.enable_send_with_allocate; - // LCOV_EXCL_BR_START (Tool incorrectly marks the decision as "Decision couldn't be analyzed" despite all lines in - // both branches (true / false) being covered. "Decision couldn't be analyzed" only appeared after changing the code - // within the if statement (without changing the condition / tests). Suppression can be removed when bug is fixed in - // Ticket-188259). - if (tracing_for_skeleton_event_enabled) - { - // LCOV_EXCL_BR_STOP - score::cpp::ignore = transaction_log_registration_guard_.emplace( - TransactionLogRegistrationGuard::Create(event_data_control_composite_->GetQmEventDataControl())); - score::cpp::ignore = type_erased_sample_ptrs_guard_.emplace(skeleton_event_tracing_data_.service_element_tracing_data); - } - - // Register callbacks to be notified when event notification existence changes. - // This allows us to optimise the Send() path by skipping NotifyEvent() when no handlers are registered. - // Separate callbacks for QM and ASIL-B update their respective atomic flags for lock-free access. - if (parent_.GetInstanceQualityType() == QualityType::kASIL_QM) - { - GetBindingRuntime(BindingType::kLoLa) - .GetLolaMessaging() - .RegisterEventNotificationExistenceChangedCallback( - QualityType::kASIL_QM, event_fqn_, [this](const bool has_handlers) noexcept { - qm_event_update_notifications_registered_.store(has_handlers); - }); - } - if (parent_.GetInstanceQualityType() == QualityType::kASIL_B) - { - GetBindingRuntime(BindingType::kLoLa) - .GetLolaMessaging() - .RegisterEventNotificationExistenceChangedCallback( - QualityType::kASIL_B, event_fqn_, [this](const bool has_handlers) noexcept { - asil_b_event_update_notifications_registered_.store(has_handlers); - }); - } + PrepareOfferImpl(*this); return {}; } @@ -343,27 +422,7 @@ template // coverity[autosar_cpp14_a15_5_3_violation : FALSE] void SkeletonEvent::PrepareStopOffer() noexcept { - // Unregister event notification existence changed callbacks - GetBindingRuntime(BindingType::kLoLa) - .GetLolaMessaging() - .UnregisterEventNotificationExistenceChangedCallback(QualityType::kASIL_QM, event_fqn_); - - if (parent_.GetInstanceQualityType() == QualityType::kASIL_B) - { - GetBindingRuntime(BindingType::kLoLa) - .GetLolaMessaging() - .UnregisterEventNotificationExistenceChangedCallback(QualityType::kASIL_B, event_fqn_); - } - - // Reset the flags to indicate no handlers are registered - qm_event_update_notifications_registered_.store(false); - asil_b_event_update_notifications_registered_.store(false); - - type_erased_sample_ptrs_guard_.reset(); - if (event_data_control_composite_.has_value()) - { - transaction_log_registration_guard_.reset(); - } + PrepareStopOfferImpl(*this); } } // namespace score::mw::com::impl::lola diff --git a/score/mw/com/impl/bindings/mock_binding/skeleton.h b/score/mw/com/impl/bindings/mock_binding/skeleton.h index 613f00ff..bc75857b 100644 --- a/score/mw/com/impl/bindings/mock_binding/skeleton.h +++ b/score/mw/com/impl/bindings/mock_binding/skeleton.h @@ -34,10 +34,6 @@ class Skeleton : public SkeletonBinding (noexcept, override, final)); MOCK_METHOD(void, PrepareStopOffer, (std::optional), (noexcept, override, final)); MOCK_METHOD(BindingType, GetBindingType, (), (const, noexcept, override, final)); - MOCK_METHOD(Result>, - CreateGenericEventBinding, - (std::string_view event_name, size_t size, size_t alignment), - (noexcept, override, final)); }; class SkeletonFacade : public SkeletonBinding @@ -63,12 +59,6 @@ class SkeletonFacade : public SkeletonBinding return skeleton_.GetBindingType(); } - Result> - CreateGenericEventBinding(std::string_view event_name, size_t size, size_t alignment) noexcept override final - { - return skeleton_.CreateGenericEventBinding(event_name, size, alignment); - } - private: Skeleton& skeleton_; }; diff --git a/score/mw/com/impl/com_error.h b/score/mw/com/impl/com_error.h index 13f3b0e0..6195c068 100644 --- a/score/mw/com/impl/com_error.h +++ b/score/mw/com/impl/com_error.h @@ -59,6 +59,7 @@ enum class ComErrc : score::result::ErrorCode kFindServiceHandlerFailure, kInvalidHandle, kCallQueueFull, + kServiceElementAlreadyExists, }; /** @@ -203,6 +204,9 @@ class ComErrorDomain final : public score::result::ErrorDomain case static_cast(ComErrc::kCallQueueFull): return "Call queue of service method is already full."; // coverity[autosar_cpp14_m6_4_5_violation] + case static_cast(ComErrc::kServiceElementAlreadyExists): + return "A service element (event, field, method) with the same name already exists."; + // coverity[autosar_cpp14_m6_4_5_violation] default: return "unknown future error"; } diff --git a/score/mw/com/impl/generic_skeleton.cpp b/score/mw/com/impl/generic_skeleton.cpp index 40828bb0..b1571fd7 100644 --- a/score/mw/com/impl/generic_skeleton.cpp +++ b/score/mw/com/impl/generic_skeleton.cpp @@ -4,6 +4,7 @@ * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. * + * * This program and the accompanying materials are made available under the * terms of the Apache License Version 2.0 which is available at * https://www.apache.org/licenses/LICENSE-2.0 @@ -13,6 +14,7 @@ #include "score/mw/com/impl/generic_skeleton.h" #include "score/mw/com/impl/com_error.h" +#include "score/mw/com/impl/plumbing/generic_skeleton_event_binding_factory.h" #include "score/mw/com/impl/plumbing/skeleton_binding_factory.h" #include "score/mw/com/impl/runtime.h" #include "score/mw/com/impl/size_info.h" @@ -21,23 +23,24 @@ namespace score::mw::com::impl { -Result GenericSkeleton::Create(const InstanceSpecifier& specifier) noexcept +Result GenericSkeleton::Create( + const InstanceSpecifier& specifier, + MethodCallProcessingMode mode) noexcept { - const auto identifier_result = Runtime::getInstance().resolve(specifier); - if (identifier_result.empty()) - { - return MakeUnexpected(ComErrc::kInstanceIDCouldNotBeResolved); - } + const auto instance_identifier_result = GetInstanceIdentifier(specifier); - if (identifier_result.size() > 1) + if (!instance_identifier_result.has_value()) { - score::mw::log::LogWarn("com") << "InstanceSpecifier resolved to more than one InstanceIdentifier. Using the first one."; + score::mw::log::LogFatal("lola") << "Failed to resolve instance identifier from instance specifier"; + std::terminate(); } - return Create(identifier_result[0]); + return Create(instance_identifier_result.value(),mode); } -Result GenericSkeleton::Create(const InstanceIdentifier& identifier) noexcept +Result GenericSkeleton::Create( + const InstanceIdentifier& identifier, + MethodCallProcessingMode mode) noexcept { auto binding = SkeletonBindingFactory::Create(identifier); if (!binding) @@ -45,33 +48,33 @@ Result GenericSkeleton::Create(const InstanceIdentifier& identi return MakeUnexpected(ComErrc::kBindingFailure); } - return GenericSkeleton(identifier, std::move(binding)); + return GenericSkeleton(identifier, std::move(binding),mode); } Result GenericSkeleton::AddEvent(std::string_view name, const SizeInfo& size_info) noexcept { - if (SkeletonBaseView{*this}.IsOffered()) + auto skeleton_view = SkeletonBaseView{*this}; + if (skeleton_view.IsOffered()) { - return MakeUnexpected(ComErrc::kNotOffered); + // It is not allowed to add events after the service has been offered. + return MakeUnexpected(ComErrc::kServiceInstanceAlreadyOffered); } - auto event_binding_result = - SkeletonBaseView{*this}.GetBinding()->CreateGenericEventBinding(name, size_info.size, size_info.alignment); + auto event_binding_result = GenericSkeletonEventBindingFactory::Create(*this, name, size_info); if (!event_binding_result.has_value()) { - return MakeUnexpected(ComErrc::kNotOffered); + return MakeUnexpected(ComErrc::kBindingFailure); } - auto emplace_result = events_.emplace( + auto emplace_result = owned_events_.emplace( name, std::make_unique(*this, name, std::move(event_binding_result).value())); if (!emplace_result.second) { - return MakeUnexpected(ComErrc::kEventNotExisting); + // An event with this name has already been added. + return MakeUnexpected(ComErrc::kServiceElementAlreadyExists); } - auto& event = *emplace_result.first->second; - SkeletonBaseView{*this}.RegisterEvent(name, event); - return &event; + return emplace_result.first->second.get(); } Result GenericSkeleton::OfferService() noexcept @@ -84,8 +87,10 @@ void GenericSkeleton::StopOfferService() noexcept SkeletonBase::StopOfferService(); } -GenericSkeleton::GenericSkeleton(const InstanceIdentifier& identifier, std::unique_ptr binding) - : SkeletonBase(std::move(binding), identifier) +GenericSkeleton::GenericSkeleton(const InstanceIdentifier& identifier, + std::unique_ptr binding, + MethodCallProcessingMode mode) + : SkeletonBase(std::move(binding), identifier,mode) { } diff --git a/score/mw/com/impl/generic_skeleton.h b/score/mw/com/impl/generic_skeleton.h index f696418a..4913b83b 100644 --- a/score/mw/com/impl/generic_skeleton.h +++ b/score/mw/com/impl/generic_skeleton.h @@ -35,12 +35,14 @@ class GenericSkeleton : public SkeletonBase /// @brief Creates a GenericSkeleton for a given instance specifier. /// @param specifier The instance specifier. /// @return A GenericSkeleton or an error. - static Result Create(const InstanceSpecifier& specifier) noexcept; + static Result Create(const InstanceSpecifier& specifier, + MethodCallProcessingMode mode = MethodCallProcessingMode::kEvent) noexcept; /// @brief Creates a GenericSkeleton for a given instance identifier. /// @param identifier The instance identifier. /// @return A GenericSkeleton or an error. - static Result Create(const InstanceIdentifier& identifier) noexcept; + static Result Create(const InstanceIdentifier& identifier, + MethodCallProcessingMode mode = MethodCallProcessingMode::kEvent) noexcept; /// @brief Adds a type-erased event to the skeleton. /// @@ -59,9 +61,11 @@ class GenericSkeleton : public SkeletonBase void StopOfferService() noexcept; private: - GenericSkeleton(const InstanceIdentifier& identifier, std::unique_ptr binding); + GenericSkeleton(const InstanceIdentifier& identifier, + std::unique_ptr binding, + MethodCallProcessingMode mode = MethodCallProcessingMode::kEvent); - std::map> events_; + std::map> owned_events_; }; } // namespace score::mw::com::impl \ No newline at end of file diff --git a/score/mw/com/impl/generic_skeleton_event.cpp b/score/mw/com/impl/generic_skeleton_event.cpp index 92c730d1..4fb1156d 100644 --- a/score/mw/com/impl/generic_skeleton_event.cpp +++ b/score/mw/com/impl/generic_skeleton_event.cpp @@ -12,6 +12,9 @@ ********************************************************************************/ #include "score/mw/com/impl/generic_skeleton_event.h" #include "score/mw/com/impl/generic_skeleton_event_binding.h" +#include "score/mw/com/impl/bindings/lola/generic_skeleton_event.h" +#include "score/mw/com/impl/tracing/skeleton_event_tracing.h" +#include "score/mw/com/impl/skeleton_base.h" #include @@ -23,35 +26,62 @@ GenericSkeletonEvent::GenericSkeletonEvent(SkeletonBase& skeleton_base, std::unique_ptr binding) : SkeletonEventBase(skeleton_base, event_name, std::move(binding)) { + SkeletonBaseView{skeleton_base}.RegisterEvent(event_name, *this); + + if (binding_ != nullptr) + { + const SkeletonBaseView skeleton_base_view{skeleton_base}; + const auto& instance_identifier = skeleton_base_view.GetAssociatedInstanceIdentifier(); + const auto binding_type = binding_->GetBindingType(); + auto tracing_data = + tracing::GenerateSkeletonTracingStructFromEventConfig(instance_identifier, binding_type, event_name); + binding_->SetSkeletonEventTracingData(tracing_data); + } } Result GenericSkeletonEvent::Send(SampleAllocateePtr sample) noexcept { + if (!service_offered_flag_.IsSet()) + { + score::mw::log::LogError("lola") + << "GenericSkeletonEvent::Send failed as Event has not yet been offered or has been stop offered"; + return MakeUnexpected(ComErrc::kNotOffered); + } + + auto* const lola_sample_ptr = SampleAllocateePtrView{sample}.As>(); + SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD(lola_sample_ptr != nullptr); + + SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD_MESSAGE(binding_ != nullptr, "Binding is not initialized!"); auto* const binding = static_cast(binding_.get()); - std::cout<<"auto* const binding = static_cast(binding_.get());"<{sample}.As(); - std::cout<<"auto* const generic_sample_ptr = SampleAllocateePtrView{sample}.As();"<Send(generic_sample_ptr->GetControlSlotIndicator()); - return result; + + const auto send_result = binding->Send(lola_sample_ptr->GetReferencedSlot()); + if (!send_result.has_value()) + { + score::mw::log::LogError("lola") << "GenericSkeletonEvent::Send failed: " << send_result.error().Message() + << ": " << send_result.error().UserMessage(); + return MakeUnexpected(ComErrc::kBindingFailure); + } + return send_result; } Result> GenericSkeletonEvent::Allocate() noexcept { - auto* binding = static_cast(binding_.get()); - auto result = binding->Allocate(); + if (!service_offered_flag_.IsSet()) + { + score::mw::log::LogError("lola") + << "GenericSkeletonEvent::Allocate failed as Event has not yet been offered or has been stop offered"; + return MakeUnexpected(ComErrc::kNotOffered); + } + auto* const binding = static_cast(binding_.get()); + auto result = binding->Allocate(); // This now returns a Result> if (!result.has_value()) { + score::mw::log::LogError("lola") << "SkeletonEvent::Allocate failed: " << result.error().Message() + << ": " << result.error().UserMessage(); + return MakeUnexpected>(ComErrc::kSampleAllocationFailure); } - - auto deallocator = [binding_ptr = binding_.get()](lola::ControlSlotCompositeIndicator indicator) { - auto* const generic_binding = static_cast(binding_ptr); - generic_binding->Deallocate(indicator); - }; - - return MakeSampleAllocateePtr( - GenericEventSamplePtr(result.value().first, result.value().second, std::move(deallocator))); + return MakeSampleAllocateePtr(std::move(result.value())); } SizeInfo GenericSkeletonEvent::GetSizeInfo() const noexcept diff --git a/score/mw/com/impl/generic_skeleton_event_binding.h b/score/mw/com/impl/generic_skeleton_event_binding.h index c3a55361..8d6c381d 100644 --- a/score/mw/com/impl/generic_skeleton_event_binding.h +++ b/score/mw/com/impl/generic_skeleton_event_binding.h @@ -13,6 +13,7 @@ #pragma once #include "score/mw/com/impl/skeleton_event_binding.h" +#include "score/mw/com/impl/bindings/lola/sample_allocatee_ptr.h" #include "score/mw/com/impl/bindings/lola/control_slot_composite_indicator.h" #include "score/result/result.h" @@ -28,9 +29,7 @@ class GenericSkeletonEventBinding : public SkeletonEventBindingBase virtual Result Send(lola::ControlSlotCompositeIndicator control_slot_indicator) noexcept = 0; - virtual Result> Allocate() noexcept = 0; - - virtual void Deallocate(lola::ControlSlotCompositeIndicator control_slot_indicator) noexcept = 0; + virtual Result> Allocate() noexcept = 0; virtual std::pair GetSizeInfo() const noexcept = 0; }; diff --git a/score/mw/com/impl/plumbing/BUILD b/score/mw/com/impl/plumbing/BUILD index 67128aa3..4bd33d44 100644 --- a/score/mw/com/impl/plumbing/BUILD +++ b/score/mw/com/impl/plumbing/BUILD @@ -443,6 +443,21 @@ cc_library( ], ) +cc_library( + name = "generic_skeleton_event_binding_factory", + hdrs = ["generic_skeleton_event_binding_factory.h"], + features = COMPILER_WARNING_FEATURES, + tags = ["FFI"], + visibility = [ + "//score/mw/com/impl:__subpackages__", + ], + deps = [ + "//score/mw/com/impl:generic_skeleton_event_binding", + "//score/mw/com/impl:skeleton_base", + "//score/mw/com/impl/plumbing:skeleton_service_element_binding_factory_impl", + ], +) + cc_library( name = "runtime", srcs = [ diff --git a/score/mw/com/impl/plumbing/generic_skeleton_event_binding_factory.h b/score/mw/com/impl/plumbing/generic_skeleton_event_binding_factory.h new file mode 100644 index 00000000..438e08bc --- /dev/null +++ b/score/mw/com/impl/plumbing/generic_skeleton_event_binding_factory.h @@ -0,0 +1,48 @@ +/******************************************************************************** + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#ifndef SCORE_MW_COM_IMPL_PLUMBING_GENERIC_SKELETON_EVENT_BINDING_FACTORY_H +#define SCORE_MW_COM_IMPL_PLUMBING_GENERIC_SKELETON_EVENT_BINDING_FACTORY_H + +#include "score/mw/com/impl/generic_skeleton_event_binding.h" +#include "score/mw/com/impl/skeleton_base.h" +#include "score/mw/com/impl/size_info.h" +#include "score/mw/com/impl/service_element_type.h" // Added for ServiceElementType +#include "score/mw/com/impl/plumbing/skeleton_service_element_binding_factory_impl.h" + +#include "score/result/result.h" + +#include +#include + +namespace score::mw::com::impl +{ + +class GenericSkeletonEventBindingFactory +{ + public: + static Result> Create(SkeletonBase& skeleton_base, + std::string_view event_name, + const SizeInfo& size_info) noexcept + { + const auto& instance_identifier = SkeletonBaseView{skeleton_base}.GetAssociatedInstanceIdentifier(); + return CreateSkeletonServiceElement( + instance_identifier, + skeleton_base, + event_name, + size_info); + } +}; + +} // namespace score::mw::com::impl + +#endif // SCORE_MW_COM_IMPL_PLUMBING_GENERIC_SKELETON_EVENT_BINDING_FACTORY_H \ No newline at end of file diff --git a/score/mw/com/impl/plumbing/sample_allocatee_ptr.h b/score/mw/com/impl/plumbing/sample_allocatee_ptr.h index 6878847d..8205b0de 100644 --- a/score/mw/com/impl/plumbing/sample_allocatee_ptr.h +++ b/score/mw/com/impl/plumbing/sample_allocatee_ptr.h @@ -27,56 +27,6 @@ namespace score::mw::com::impl { -/// @brief An RAII wrapper for a type-erased sample allocated by a GenericSkeletonEvent. -class GenericEventSamplePtr -{ - public: - using element_type = void; - - GenericEventSamplePtr(void* data, - lola::ControlSlotCompositeIndicator control_slot_indicator, - std::function deallocator) - : data_(data), control_slot_indicator_(control_slot_indicator), deallocator_(std::move(deallocator)) - { - } - - ~GenericEventSamplePtr() - { - if (data_ != nullptr) - { - deallocator_(control_slot_indicator_); - } - } - - GenericEventSamplePtr(const GenericEventSamplePtr&) = delete; - GenericEventSamplePtr& operator=(const GenericEventSamplePtr&) = delete; - - GenericEventSamplePtr(GenericEventSamplePtr&& other) noexcept - : data_(other.data_), - control_slot_indicator_(other.control_slot_indicator_), - deallocator_(std::move(other.deallocator_)) - { - other.data_ = nullptr; - } - - GenericEventSamplePtr& operator=(GenericEventSamplePtr&&) = default; - - void* get() const - { - return data_; - } - - lola::ControlSlotCompositeIndicator GetControlSlotIndicator() const - { - return control_slot_indicator_; - } - - private: - void* data_; - lola::ControlSlotCompositeIndicator control_slot_indicator_; - std::function deallocator_; -}; - /// \brief Pointer to a data sample allocated by the Communication Management implementation (mimics std::unique_ptr) /// /// \details We try to implement certain functionality that facades an std::unique_ptr, but some functionalities (e.g. @@ -426,9 +376,6 @@ class SampleAllocateePtr { auto visitor = score::cpp::overload( [](lola::SampleAllocateePtr& internal_ptr) noexcept -> void { internal_ptr.reset(); }, - [](GenericEventSamplePtr& internal_ptr) noexcept -> void { - GenericEventSamplePtr temp = std::move(internal_ptr); - }, [](const score::cpp::blank&) noexcept -> void {}); std::visit(visitor, internal_); } @@ -443,7 +390,6 @@ class SampleAllocateePtr { auto visitor = score::cpp::overload( [](const lola::SampleAllocateePtr& internal_ptr) noexcept -> pointer { return internal_ptr.get(); }, - [](const GenericEventSamplePtr& internal_ptr) noexcept -> pointer { return internal_ptr.get(); }, [](const score::cpp::blank&) noexcept -> pointer { return nullptr; }); return std::visit(visitor, internal_); } @@ -454,7 +400,6 @@ class SampleAllocateePtr [](const lola::SampleAllocateePtr& internal_ptr) noexcept -> bool { return static_cast(internal_ptr); }, - [](const GenericEventSamplePtr& internal_ptr) noexcept -> bool { return internal_ptr.get() != nullptr; }, [](const score::cpp::blank&) noexcept -> bool { return false; }); return std::visit(visitor, internal_); } @@ -465,7 +410,6 @@ class SampleAllocateePtr { auto visitor = score::cpp::overload( [](const lola::SampleAllocateePtr& internal_ptr) noexcept -> pointer { return internal_ptr.get(); }, - [](const GenericEventSamplePtr& internal_ptr) noexcept -> pointer { return internal_ptr.get(); }, [](const score::cpp::blank&) noexcept -> pointer { std::terminate(); return nullptr; @@ -488,7 +432,7 @@ class SampleAllocateePtr template friend class SampleAllocateePtrMutableView; - std::variant, GenericEventSamplePtr> internal_; + std::variant> internal_; }; template <> diff --git a/score/mw/com/impl/plumbing/skeleton_service_element_binding_factory_impl.h b/score/mw/com/impl/plumbing/skeleton_service_element_binding_factory_impl.h index 060852bd..28dd05f9 100644 --- a/score/mw/com/impl/plumbing/skeleton_service_element_binding_factory_impl.h +++ b/score/mw/com/impl/plumbing/skeleton_service_element_binding_factory_impl.h @@ -15,6 +15,7 @@ #include "score/mw/com/impl/bindings/lola/element_fq_id.h" #include "score/mw/com/impl/bindings/lola/skeleton.h" +#include "score/mw/com/impl/bindings/lola/generic_skeleton_event.h" #include "score/mw/com/impl/bindings/lola/skeleton_event_properties.h" #include "score/mw/com/impl/configuration/binding_service_type_deployment.h" #include "score/mw/com/impl/configuration/lola_service_instance_deployment.h" @@ -29,6 +30,7 @@ #include #include +#include #include #include #include @@ -65,9 +67,6 @@ lola::SkeletonEventProperties GetSkeletonEventProperties( lola_service_element_instance_deployment.max_subscribers_.value(), lola_service_element_instance_deployment.enforce_max_samples_}; } - -} // namespace detail - template // Suppress "AUTOSAR C++14 A15-5-3" rule finding. This rule states: "The std::terminate() function shall // not be called implicitly.". std::visit Throws std::bad_variant_access if @@ -77,9 +76,10 @@ template >& size_info) noexcept -> std::unique_ptr { static_assert(element_type != ServiceElementType::INVALID); @@ -88,7 +88,7 @@ auto CreateSkeletonServiceElement(const InstanceIdentifier& identifier, using ReturnType = std::unique_ptr; auto visitor = score::cpp::overload( - [identifier_view, &parent, &service_element_name]( + [identifier_view, &parent, &service_element_name, &size_info]( const LolaServiceTypeDeployment& lola_service_type_deployment) -> ReturnType { auto* const lola_parent = dynamic_cast(SkeletonBaseView{parent}.GetBinding()); if (lola_parent == nullptr) @@ -114,8 +114,18 @@ auto CreateSkeletonServiceElement(const InstanceIdentifier& identifier, lola_service_instance_deployment.instance_id_.value().GetId(), element_type}; - return std::make_unique( - *lola_parent, element_fq_id, service_element_name, skeleton_event_properties); + if constexpr (std::is_same_v) + { + SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD(size_info.has_value()); + return std::make_unique( + *lola_parent, skeleton_event_properties, element_fq_id, size_info.value().get()); + } + else + { + SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD(!size_info.has_value()); + return std::make_unique( + *lola_parent, element_fq_id, service_element_name, skeleton_event_properties); + } }, [](const SomeIpServiceInstanceDeployment&) noexcept -> ReturnType { return nullptr; @@ -132,6 +142,39 @@ auto CreateSkeletonServiceElement(const InstanceIdentifier& identifier, return std::visit(visitor, identifier_view.GetServiceTypeDeployment().binding_info_); } +} // namespace detail + + +/// @brief Overload for typed skeletons (which do not have a SizeInfo). +template +auto CreateSkeletonServiceElement(const InstanceIdentifier& identifier, + SkeletonBase& parent, + const std::string_view service_element_name) noexcept + -> std::unique_ptr +{ + SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD_MESSAGE((!std::is_same_v), + "This overload is for typed skeletons only. Generic skeletons must provide a SizeInfo."); + return detail::CreateSkeletonServiceElementImpl( + identifier, parent, service_element_name, score::cpp::nullopt); +} + +/// @brief Overload for generic skeletons (which require a SizeInfo). +template +auto CreateSkeletonServiceElement(const InstanceIdentifier& identifier, + SkeletonBase& parent, + const std::string_view service_element_name, + const SizeInfo& size_info) noexcept + -> std::unique_ptr +{ + SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD_MESSAGE((std::is_same_v), + "This overload is for generic skeletons only. Typed skeletons must not provide a SizeInfo."); + return detail::CreateSkeletonServiceElementImpl( + identifier, + parent, + service_element_name, + std::cref(size_info)); +} + } // namespace score::mw::com::impl #endif // SCORE_MW_COM_IMPL_PLUMBING_SKELETON_SERVICE_ELEMENT_BINDING_FACTORY_IMPL_H diff --git a/score/mw/com/impl/skeleton_binding.h b/score/mw/com/impl/skeleton_binding.h index 67f867ea..f4e42079 100644 --- a/score/mw/com/impl/skeleton_binding.h +++ b/score/mw/com/impl/skeleton_binding.h @@ -99,13 +99,6 @@ class SkeletonBinding /// \brief Gets the binding type of the binding virtual BindingType GetBindingType() const noexcept = 0; - /// @brief Creates a binding for a generic event. - /// @param event_name The name of the event. - /// @param size The size for the event's data. - /// @param alignment The alignment for the event's data. - /// @return A unique pointer to the created binding or an error. - virtual Result> - CreateGenericEventBinding(std::string_view event_name, size_t size, size_t alignment) noexcept = 0; }; } // namespace score::mw::com::impl From 809cecd28f5942ba796d76f051358532a4854aca Mon Sep 17 00:00:00 2001 From: Abhishek GOYAL Date: Thu, 15 Jan 2026 11:14:46 +0100 Subject: [PATCH 3/3] Remove Send Api for Generic Skeleton with void * --- score/mw/com/impl/bindings/lola/generic_skeleton_event.cpp | 5 ----- score/mw/com/impl/bindings/lola/generic_skeleton_event.h | 2 -- score/mw/com/impl/generic_skeleton_event_binding.h | 2 -- 3 files changed, 9 deletions(-) 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 ad701b67..0d22bde6 100644 --- a/score/mw/com/impl/bindings/lola/generic_skeleton_event.cpp +++ b/score/mw/com/impl/bindings/lola/generic_skeleton_event.cpp @@ -37,11 +37,6 @@ ResultBlank GenericSkeletonEvent::PrepareOffer() noexcept return {}; } -Result GenericSkeletonEvent::Send(const void* /*data*/) noexcept -{ - return MakeUnexpected(ComErrc::kIllegalUseOfAllocate); -} - Result GenericSkeletonEvent::Send(lola::ControlSlotCompositeIndicator control_slot_indicator) noexcept { SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD(control_.has_value()); 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 15c9abde..d5b9c33a 100644 --- a/score/mw/com/impl/bindings/lola/generic_skeleton_event.h +++ b/score/mw/com/impl/bindings/lola/generic_skeleton_event.h @@ -39,8 +39,6 @@ class GenericSkeletonEvent : public GenericSkeletonEventBinding const ElementFqId& event_fqn, const SizeInfo& size_info); - Result Send(const void* data) noexcept override; - Result Send(lola::ControlSlotCompositeIndicator control_slot_indicator) noexcept override; Result> Allocate() noexcept override; diff --git a/score/mw/com/impl/generic_skeleton_event_binding.h b/score/mw/com/impl/generic_skeleton_event_binding.h index 8d6c381d..7372f91e 100644 --- a/score/mw/com/impl/generic_skeleton_event_binding.h +++ b/score/mw/com/impl/generic_skeleton_event_binding.h @@ -25,8 +25,6 @@ namespace score::mw::com::impl class GenericSkeletonEventBinding : public SkeletonEventBindingBase { public: - virtual Result Send(const void* data) noexcept = 0; - virtual Result Send(lola::ControlSlotCompositeIndicator control_slot_indicator) noexcept = 0; virtual Result> Allocate() noexcept = 0;