diff --git a/score/launch_manager/src/daemon/BUILD b/score/launch_manager/src/daemon/BUILD index 34705a594..f0f06f284 100644 --- a/score/launch_manager/src/daemon/BUILD +++ b/score/launch_manager/src/daemon/BUILD @@ -27,9 +27,7 @@ cc_binary( "//score/launch_manager/src/daemon/src/configuration:flatbuffer_config_loader", "//score/launch_manager/src/daemon/src/osal:ipc_comms", "//score/launch_manager/src/daemon/src/process_group_manager", - "//score/launch_manager/src/daemon/src/process_group_manager:alive_monitor_thread", "//score/launch_manager/src/daemon/src/recovery_client", - "//score/launch_manager/src/daemon/src/supervision_control_client:supervision_control_notifier", "//score/launch_manager/src/daemon/src/watchdog:watchdog_factory", "@score_baselibs//score/language/futurecpp", ], diff --git a/score/launch_manager/src/daemon/src/alive_monitor/BUILD b/score/launch_manager/src/daemon/src/alive_monitor/BUILD index 8bea5e726..afa8bc526 100644 --- a/score/launch_manager/src/daemon/src/alive_monitor/BUILD +++ b/score/launch_manager/src/daemon/src/alive_monitor/BUILD @@ -19,3 +19,74 @@ cc_library( "//score/launch_manager/src/daemon/src/alive_monitor/details/daemon:health_monitor_impl", ], ) + +cc_library( + name = "i_alive_monitor", + hdrs = ["IAliveMonitor.hpp"], + include_prefix = "score/mw/launch_manager/alive_monitor", + strip_include_prefix = "/score/launch_manager/src/daemon/src/alive_monitor", + visibility = ["//score:__subpackages__"], + deps = [ + "//score/launch_manager/src/daemon/src/alive_monitor:isupervision_factory", + ], +) + +cc_library( + name = "mock_alive_monitor", + testonly = True, + hdrs = ["mock_alive_monitor.hpp"], + include_prefix = "score/mw/launch_manager/alive_monitor", + strip_include_prefix = "/score/launch_manager/src/daemon/src/alive_monitor", + visibility = ["//score:__subpackages__"], + deps = [ + ":i_alive_monitor", + "@googletest//:gtest_main", + ], +) + +cc_library( + name = "isupervision_event_publisher", + hdrs = ["isupervision_event_publisher.hpp"], + include_prefix = "score/mw/launch_manager/alive_monitor", + strip_include_prefix = "/score/launch_manager/src/daemon/src/alive_monitor", + visibility = ["//score:__subpackages__"], +) + +cc_library( + name = "isupervision_factory", + hdrs = ["isupervision_factory.hpp"], + include_prefix = "score/mw/launch_manager/alive_monitor", + strip_include_prefix = "/score/launch_manager/src/daemon/src/alive_monitor", + visibility = ["//score:__subpackages__"], + deps = [ + "//score/launch_manager/src/daemon/src/alive_monitor/details/ifexm:supervision_handle", + "//score/launch_manager/src/daemon/src/common:identifier_hash", + "//score/launch_manager/src/daemon/src/configuration:component_config", + ], +) + +cc_library( + name = "mock_supervision_event_publisher", + testonly = True, + hdrs = ["mock_supervision_event_publisher.hpp"], + include_prefix = "score/mw/launch_manager/alive_monitor", + strip_include_prefix = "/score/launch_manager/src/daemon/src/alive_monitor", + visibility = ["//score:__subpackages__"], + deps = [ + ":isupervision_event_publisher", + "@googletest//:gtest_main", + ], +) + +cc_library( + name = "mock_supervision_factory", + testonly = True, + hdrs = ["mock_supervision_factory.hpp"], + include_prefix = "score/mw/launch_manager/alive_monitor", + strip_include_prefix = "/score/launch_manager/src/daemon/src/alive_monitor", + visibility = ["//score:__subpackages__"], + deps = [ + ":isupervision_factory", + "@googletest//:gtest_main", + ], +) diff --git a/score/launch_manager/src/daemon/src/alive_monitor/details/daemon/IAliveMonitor.hpp b/score/launch_manager/src/daemon/src/alive_monitor/IAliveMonitor.hpp similarity index 65% rename from score/launch_manager/src/daemon/src/alive_monitor/details/daemon/IAliveMonitor.hpp rename to score/launch_manager/src/daemon/src/alive_monitor/IAliveMonitor.hpp index ac4a8b1b4..9ee72a01b 100644 --- a/score/launch_manager/src/daemon/src/alive_monitor/details/daemon/IAliveMonitor.hpp +++ b/score/launch_manager/src/daemon/src/alive_monitor/IAliveMonitor.hpp @@ -13,9 +13,7 @@ #ifndef SAF_DAEMON_ALIVE_MONITOR_HPP_INCLUDED #define SAF_DAEMON_ALIVE_MONITOR_HPP_INCLUDED -#include - -#include "score/mw/launch_manager/alive_monitor/details/daemon/PhmDaemon.hpp" +#include "score/mw/launch_manager/alive_monitor/isupervision_factory.hpp" namespace score { @@ -32,13 +30,15 @@ class IAliveMonitor public: virtual ~IAliveMonitor() = default; - /// @brief Initialize the AliveMonitor functionality - /// @return kNoError if initialization was successful, otherwise an appropriate error code. - virtual EInitCode init() noexcept = 0; + /// @brief Start the monitor thread + /// @returns False if monitoring failed to start, true otherwise + virtual bool startMonitoring() = 0; + + /// @brief Stop the monitor thread + virtual void stopMonitoring() = 0; - /// @brief Run the AliveMonitor functionality in a cyclic manner until cancellation is requested. - /// @param cancel_thread Atomic boolean flag to signal thread cancellation. - virtual bool run(std::atomic_bool& cancel_thread) noexcept = 0; + /// @brief Returns an interface for components to register their alive supervision + virtual ISupervisionFactory& getSupervisionFactory() const = 0; }; } // namespace daemon diff --git a/score/launch_manager/src/daemon/src/alive_monitor/details/common/BUILD b/score/launch_manager/src/daemon/src/alive_monitor/details/common/BUILD index 2a229864b..99b2cc282 100644 --- a/score/launch_manager/src/daemon/src/alive_monitor/details/common/BUILD +++ b/score/launch_manager/src/daemon/src/alive_monitor/details/common/BUILD @@ -30,6 +30,14 @@ cc_library( visibility = ["//score/launch_manager/src/daemon/src/alive_monitor:__subpackages__"], ) +cc_library( + name = "einitcode", + hdrs = ["EInitCode.hpp"], + include_prefix = "score/mw/launch_manager/alive_monitor/details/common", + strip_include_prefix = "/score/launch_manager/src/daemon/src/alive_monitor/details/common", + visibility = ["//score/launch_manager/src/daemon/src/alive_monitor:__subpackages__"], +) + cc_library( name = "locked_vector", hdrs = ["LockedVector.hpp"], diff --git a/score/launch_manager/src/daemon/src/alive_monitor/details/common/EInitCode.hpp b/score/launch_manager/src/daemon/src/alive_monitor/details/common/EInitCode.hpp new file mode 100644 index 000000000..d93b50fc0 --- /dev/null +++ b/score/launch_manager/src/daemon/src/alive_monitor/details/common/EInitCode.hpp @@ -0,0 +1,34 @@ +/******************************************************************************** + * Copyright (c) 2026 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 E_INIT_CODE_HPP_INCLUDED +#define E_INIT_CODE_HPP_INCLUDED + +#include + +namespace score::mw::lifecycle::internal::saf::daemon +{ + +/// @brief Return codes for PhmDaemon Initialization +enum class EInitCode : std::int8_t +{ + kNoError, ///< Init Successful (no error occurred) + kNotInitialized, ///< Init was not performed + kCycleTimeInitFailed, ///< Cyclic Timer initialization failed + kConstructFlatCfgFactoryFailed, ///< FlatCfgFactory failed loading SWCL configurations + kGeneralError ///< General error +}; + +} // namespace score::mw::lifecycle::internal::saf::daemon + +#endif diff --git a/score/launch_manager/src/daemon/src/alive_monitor/details/daemon/AliveMonitorImpl.cpp b/score/launch_manager/src/daemon/src/alive_monitor/details/daemon/AliveMonitorImpl.cpp index 958479054..d673215e4 100644 --- a/score/launch_manager/src/daemon/src/alive_monitor/details/daemon/AliveMonitorImpl.cpp +++ b/score/launch_manager/src/daemon/src/alive_monitor/details/daemon/AliveMonitorImpl.cpp @@ -11,34 +11,33 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ #include - #include #include #include "score/mw/launch_manager/alive_monitor/details/daemon/AliveMonitorImpl.hpp" +#include "score/mw/launch_manager/alive_monitor/details/daemon/PhmDaemon.hpp" namespace score::mw::lifecycle::internal::saf::daemon { AliveMonitorImpl::AliveMonitorImpl( SptrIRecoveryClient recovery_client, - UptrISupervisionControlReceiver observable_event_receiver, - const Config& config) - : m_recovery_client(recovery_client), - m_observable_event_receiver(std::move(observable_event_receiver)), - m_config(config) + const AliveSupervisionConfig& config, + const std::size_t supervised_components) + : m_recovery_client(recovery_client), m_config(config) { + initResult = init(supervised_components); } -EInitCode AliveMonitorImpl::init() noexcept +EInitCode AliveMonitorImpl::init(const std::size_t supervised_components) noexcept { EInitCode initResult{EInitCode::kGeneralError}; try { m_osClock.startMeasurement(); - m_daemon = std::make_unique(m_osClock, std::move(m_observable_event_receiver)); + m_daemon = std::make_unique(m_osClock, supervised_components); initResult = m_daemon->init(m_recovery_client, m_config); if (initResult == EInitCode::kNoError) @@ -65,11 +64,39 @@ EInitCode AliveMonitorImpl::init() noexcept return initResult; } -bool AliveMonitorImpl::run(std::atomic_bool& cancel_thread) noexcept +bool AliveMonitorImpl::startMonitoring() noexcept +{ + if (initResult != EInitCode::kNoError) + { + return false; + } + + alive_monitor_thread_ = std::thread([this]() { + threadFn(stop_thread_); + }); + + return true; +} + +void AliveMonitorImpl::stopMonitoring() noexcept +{ + stop_thread_.store(true); + if (alive_monitor_thread_.joinable()) + { + alive_monitor_thread_.join(); + } +} + +bool AliveMonitorImpl::threadFn(std::atomic_bool& cancel_thread) noexcept { SCORE_LANGUAGE_FUTURECPP_PRECONDITION_PRD_MESSAGE( m_daemon != nullptr, "HealthMonitor: Instance is not initialized!"); return m_daemon->startCyclicExec(cancel_thread); } +ISupervisionFactory& AliveMonitorImpl::getSupervisionFactory() const noexcept +{ + return *m_daemon; +} + } // namespace score::mw::lifecycle::internal::saf::daemon diff --git a/score/launch_manager/src/daemon/src/alive_monitor/details/daemon/AliveMonitorImpl.hpp b/score/launch_manager/src/daemon/src/alive_monitor/details/daemon/AliveMonitorImpl.hpp index 3cab63bb4..aceb34eae 100644 --- a/score/launch_manager/src/daemon/src/alive_monitor/details/daemon/AliveMonitorImpl.hpp +++ b/score/launch_manager/src/daemon/src/alive_monitor/details/daemon/AliveMonitorImpl.hpp @@ -15,8 +15,10 @@ #include #include +#include -#include "score/mw/launch_manager/alive_monitor/details/daemon/IAliveMonitor.hpp" +#include "score/mw/launch_manager/alive_monitor/IAliveMonitor.hpp" +#include "score/mw/launch_manager/alive_monitor/details/daemon/PhmDaemon.hpp" #include "score/mw/launch_manager/configuration/config.hpp" namespace score @@ -36,30 +38,44 @@ namespace daemon { using SptrIRecoveryClient = std::shared_ptr; -using UptrISupervisionControlReceiver = std::unique_ptr; using UptrPhmDaemon = std::unique_ptr; using OsClock = score::mw::lifecycle::internal::saf::timers::OsClockInterface; -using Config = score::mw::lifecycle::internal::configuration::Config; -using score::mw::lifecycle::internal::configuration::AliveSupervisionConfig; +using configuration::AliveSupervisionConfig; class AliveMonitorImpl : public IAliveMonitor { public: AliveMonitorImpl( SptrIRecoveryClient recovery_client, - UptrISupervisionControlReceiver observable_event_receiver, - const Config& config); + const AliveSupervisionConfig& config, + const std::size_t supervised_components); - EInitCode init() noexcept override; + /// @brief @see IAliveMonitor definition + bool startMonitoring() noexcept override; - bool run(std::atomic_bool& cancel_thread) noexcept override; + /// @brief @see IAliveMonitor definition + void stopMonitoring() noexcept override; + + /// @brief @see IAliveMonitor definition + ISupervisionFactory& getSupervisionFactory() const noexcept override; private: + /// @brief Initialize the AliveMonitor functionality + /// @param supervised_components Number of components we expect to register alive supervision + /// @return kNoError if initialization was successful, otherwise an appropriate error code. + EInitCode init(const std::size_t supervised_components) noexcept; + + /// @brief Run the AliveMonitor functionality in a cyclic manner until cancellation is requested. + /// @param cancel_thread Atomic boolean flag to signal thread cancellation. + bool threadFn(std::atomic_bool& cancel_thread) noexcept; + SptrIRecoveryClient m_recovery_client{nullptr}; UptrPhmDaemon m_daemon{nullptr}; OsClock m_osClock{}; - UptrISupervisionControlReceiver m_observable_event_receiver; - const Config& m_config; + const AliveSupervisionConfig& m_config; + std::thread alive_monitor_thread_{}; + std::atomic_bool stop_thread_{false}; + saf::daemon::EInitCode initResult{saf::daemon::EInitCode::kNotInitialized}; }; } // namespace daemon diff --git a/score/launch_manager/src/daemon/src/alive_monitor/details/daemon/BUILD b/score/launch_manager/src/daemon/src/alive_monitor/details/daemon/BUILD index 4cc7d1804..2014d0723 100644 --- a/score/launch_manager/src/daemon/src/alive_monitor/details/daemon/BUILD +++ b/score/launch_manager/src/daemon/src/alive_monitor/details/daemon/BUILD @@ -51,6 +51,8 @@ cc_library( deps = [ ":phm_daemon_config", ":sw_cluster_handler", + "//score/launch_manager/src/daemon/src/alive_monitor:isupervision_factory", + "//score/launch_manager/src/daemon/src/alive_monitor/details/common:einitcode", "//score/launch_manager/src/daemon/src/alive_monitor/details/factory:flat_cfg_factory", "//score/launch_manager/src/daemon/src/alive_monitor/details/ifappl:monitor_if_daemon", "//score/launch_manager/src/daemon/src/alive_monitor/details/ifexm:observable_event_reader", @@ -62,15 +64,6 @@ cc_library( ], ) -cc_library( - name = "i_health_monitor", - hdrs = ["IAliveMonitor.hpp"], - include_prefix = "score/mw/launch_manager/alive_monitor/details/daemon", - strip_include_prefix = "/score/launch_manager/src/daemon/src/alive_monitor/details/daemon", - visibility = ["//score/launch_manager/src/daemon:__subpackages__"], - deps = [":phm_daemon"], -) - cc_library( name = "health_monitor_impl", srcs = ["AliveMonitorImpl.cpp"], @@ -79,7 +72,8 @@ cc_library( strip_include_prefix = "/score/launch_manager/src/daemon/src/alive_monitor/details/daemon", visibility = ["//score/launch_manager/src/daemon:__subpackages__"], deps = [ - ":i_health_monitor", + ":phm_daemon", + "//score/launch_manager/src/daemon/src/alive_monitor:i_alive_monitor", "//score/launch_manager/src/daemon/src/configuration:config", "@score_baselibs//score/language/futurecpp", ], diff --git a/score/launch_manager/src/daemon/src/alive_monitor/details/daemon/PhmDaemon.cpp b/score/launch_manager/src/daemon/src/alive_monitor/details/daemon/PhmDaemon.cpp index f9d2862ba..6b03a020f 100644 --- a/score/launch_manager/src/daemon/src/alive_monitor/details/daemon/PhmDaemon.cpp +++ b/score/launch_manager/src/daemon/src/alive_monitor/details/daemon/PhmDaemon.cpp @@ -27,13 +27,15 @@ namespace score::mw::lifecycle::internal::saf::daemon true_no_defect) */ /* RULECHECKER_comment(0, 4, check_incomplete_data_member_construction, "Default constructor is used for\ processStateReader.", true_no_defect) */ -PhmDaemon::PhmDaemon(OsClock& f_osClock, std::unique_ptr f_observable_event_receiver) +PhmDaemon::PhmDaemon(OsClock& f_osClock, std::size_t supervised_components) : osClock{f_osClock}, cycleTimer{&osClock}, + buffer_(std::make_shared()), supervisionManager{std::make_unique()}, - processStateReader{std::move(f_observable_event_receiver)} + processStateReader{buffer_} { - static_cast(f_osClock); + buffer_->initialize(); + supervisionManager.reserve(supervised_components); } void PhmDaemon::performCyclicTriggers(void) @@ -57,38 +59,4 @@ void PhmDaemon::performCyclicTriggers(void) } } -bool PhmDaemon::construct(const std::vector& config) noexcept(false) -{ - const std::size_t supervised_components = - std::count_if(config.begin(), config.end(), [](const configuration::ComponentConfig& component) { - return component.component_properties.application_profile.alive_supervision.has_value(); - }); - - supervisionManager.reserve(supervised_components); - - // In a later refactoring step, components will register their own alive supervision and provide their identifier. - // For now, we iterate through them all here. - - LM_LOG_DEBUG() << "Supervision manager starts constructing workers"; - - for (const auto& comp : config) - { - if (!comp.component_properties.application_profile.alive_supervision.has_value()) - { - continue; - } - const auto& alive = comp.component_properties.application_profile.alive_supervision.value(); - const IdentifierHash name{comp.name}; - const auto uid = comp.deployment_config.sandbox.uid; - if (!supervisionManager.constructWorker(name, alive, uid, recoveryClient, processStateReader)) - { - - LM_LOG_ERROR() << "Supervision manager is unable to construct the required worker objects."; - return false; - } - } - - return true; -} - } // namespace score::mw::lifecycle::internal::saf::daemon diff --git a/score/launch_manager/src/daemon/src/alive_monitor/details/daemon/PhmDaemon.hpp b/score/launch_manager/src/daemon/src/alive_monitor/details/daemon/PhmDaemon.hpp index d23b180b9..bd823268c 100644 --- a/score/launch_manager/src/daemon/src/alive_monitor/details/daemon/PhmDaemon.hpp +++ b/score/launch_manager/src/daemon/src/alive_monitor/details/daemon/PhmDaemon.hpp @@ -19,12 +19,14 @@ #include #include "score/launch_manager/src/daemon/src/common/log.hpp" +#include "score/mw/launch_manager/alive_monitor/details/common/EInitCode.hpp" #include "score/mw/launch_manager/alive_monitor/details/daemon/PhmDaemonConfig.hpp" #include "score/mw/launch_manager/alive_monitor/details/daemon/SupervisionManager.hpp" #include "score/mw/launch_manager/alive_monitor/details/ifexm/ObservableEventReader.hpp" #include "score/mw/launch_manager/alive_monitor/details/timers/CycleTimeValidator.hpp" #include "score/mw/launch_manager/alive_monitor/details/timers/CycleTimer.hpp" #include "score/mw/launch_manager/alive_monitor/details/timers/TimeConversion.hpp" +#include "score/mw/launch_manager/alive_monitor/isupervision_factory.hpp" #include "score/mw/launch_manager/configuration/config.hpp" namespace score @@ -36,24 +38,13 @@ namespace saf namespace daemon { -/// @brief Return codes for PhmDaemon Initialization -enum class EInitCode : std::int8_t -{ - kNoError, ///< Init Successful (no error occurred) - kNotInitialized, ///< Init was not performed - kCycleTimeInitFailed, ///< Cyclic Timer initialization failed - kConstructFlatCfgFactoryFailed, ///< FlatCfgFactory failed loading SWCL configurations - kGeneralError ///< General error -}; - /// @brief PHM daemon main class wraps the functionality for initialization and cyclic execution. /// @details This is the main class responsible to execute the main functionalities of PHM daemon, /// by using the necessary classes from this software component. -class PhmDaemon +class PhmDaemon final : public ISupervisionFactory { public: using OsClock = score::mw::lifecycle::internal::saf::timers::OsClockInterface; - using SupervisionControlReceiver = score::mw::lifecycle::ISupervisionControlReceiver; using RecoveryClient = score::mw::lifecycle::IRecoveryClient; using CycleTimer = score::mw::lifecycle::internal::saf::timers::CycleTimer; using CycleTimeValidator = score::mw::lifecycle::internal::saf::timers::CycleTimeValidator; @@ -61,20 +52,16 @@ class PhmDaemon using ObservableEventReader = score::mw::lifecycle::internal::saf::ifexm::ObservableEventReader; using Config = score::mw::lifecycle::internal::configuration::Config; - /* RULECHECKER_comment(0, 4, check_expensive_to_copy_in_parameter, "f_supervisionErrorInfo name is passed by value\ - as same as generated function", true_no_defect) */ /// @brief Set the OS clock interface /// @param[in] f_osClock Access to the system clock (dependency injection possible in tests) - /// @param[in] f_observable_event_receiver observable event receiver implementation (dependency injection possible + /// @param[in] supervised_components Number of components that will register alive supervision /// in tests) /* RULECHECKER_comment(3,1, check_expensive_to_copy_in_parameter, "Move only types cannot be passed by const ref", true_no_defect) */ - PhmDaemon(OsClock& f_osClock, std::unique_ptr f_observable_event_receiver); + explicit PhmDaemon(OsClock& f_osClock, std::size_t supervised_components); - /* RULECHECKER_comment(0, 4, check_min_instructions, "Default destructor is not provided\ - a function body", true_no_defect) */ /// @brief Destroys the workers - virtual ~PhmDaemon() = default; + ~PhmDaemon() override = default; /// @brief No Copy Constructor PhmDaemon(const PhmDaemon&) = delete; @@ -90,17 +77,14 @@ class PhmDaemon /// @param[in] recovery_client Shared pointer to recovery client /// @param[in] config Config holding alive monitor and component configuration /// @return See EInitCode definition - EInitCode init(std::shared_ptr recovery_client, const Config& config) noexcept(false) + EInitCode init( + std::shared_ptr recovery_client, + const configuration::AliveSupervisionConfig& config) noexcept(false) { recoveryClient = recovery_client; - if (!construct(config.components())) - { - return EInitCode::kConstructFlatCfgFactoryFailed; - } - - int64_t cycleTimeModified{static_cast( - timers::TimeConversion::convertMilliSecToNanoSec(config.aliveSupervision().evaluation_cycle_ms))}; + int64_t cycleTimeModified{ + static_cast(timers::TimeConversion::convertMilliSecToNanoSec(config.evaluation_cycle_ms))}; cycleTimeModified = CycleTimeValidator::adjustCycleTimeOnClockAccuracy(cycleTimeModified, osClock); @@ -193,13 +177,19 @@ class PhmDaemon return true; } - private: - /// @brief Create SwCluster objects & Invoke construction of worker objects - /// @details Create the SwclusterHandler objects and the workers for the SwclusterHandler - /// @param[in] config Config for all components - /// @return bool true if workers creation succeeded, false otherwise - bool construct(const std::vector& config) noexcept(false); + /// @brief @see ISupervisonFactory::constructSupervision + std::unique_ptr constructSupervision( + const IdentifierHash id, + const uid_t uid, + const configuration::ComponentAliveSupervision& config) override + { + SCORE_LANGUAGE_FUTURECPP_ASSERT_DBG_MESSAGE( + !supervisionManager.full(), "More alive supervisions than expected were constructed"); + supervisionManager.constructWorker(id, config, uid, recoveryClient, processStateReader); + return std::make_unique(id, buffer_); + } + private: /// @brief Perform cyclic execution of Phm daemon /// @details Perform cyclic execution of Phm daemon functionalities, for e.g., evaluation of supervisions. void performCyclicTriggers(void); @@ -210,6 +200,8 @@ class PhmDaemon /// @brief For fixed time-step execution during the cyclic execution CycleTimer cycleTimer; + std::shared_ptr buffer_; + /// @brief Recovery interface to Launch Manager std::shared_ptr recoveryClient; diff --git a/score/launch_manager/src/daemon/src/alive_monitor/details/daemon/SupervisionManager.cpp b/score/launch_manager/src/daemon/src/alive_monitor/details/daemon/SupervisionManager.cpp index f84851cf9..556c761cb 100644 --- a/score/launch_manager/src/daemon/src/alive_monitor/details/daemon/SupervisionManager.cpp +++ b/score/launch_manager/src/daemon/src/alive_monitor/details/daemon/SupervisionManager.cpp @@ -32,8 +32,14 @@ SupervisionManager::SupervisionManager(std::unique_ptr fac SupervisionManager::~SupervisionManager() = default; +bool SupervisionManager::full() +{ + return aliveSupervisions.size() == capacity; +} + void SupervisionManager::reserve(std::size_t size) { + capacity = size; processStates.reserve(size); aliveIfIpcs.reserve(size); aliveInterfaces.reserve(size); diff --git a/score/launch_manager/src/daemon/src/alive_monitor/details/daemon/SupervisionManager.hpp b/score/launch_manager/src/daemon/src/alive_monitor/details/daemon/SupervisionManager.hpp index 6d793f7fb..e54323ded 100644 --- a/score/launch_manager/src/daemon/src/alive_monitor/details/daemon/SupervisionManager.hpp +++ b/score/launch_manager/src/daemon/src/alive_monitor/details/daemon/SupervisionManager.hpp @@ -91,6 +91,9 @@ class SupervisionManager /// @param[in] size Number of supervised components void reserve(std::size_t size); + /// @brief Returns true if the number of alive supervisions constructed equals the reserved size + bool full(); + /// @brief Construct required worker objects for provided component /// @details Construct the interfaces, checkpoints, supervisions and recovery notifications /// @param [in] id Identifier of the component @@ -142,6 +145,9 @@ class SupervisionManager std::vector aliveSupervisions; std::unique_ptr flatCfgFactory; + + /// @brief The number of alive supervisions we expect to successfully construct + std::size_t capacity{0}; }; } // namespace daemon diff --git a/score/launch_manager/src/daemon/src/alive_monitor/details/ifappl/BUILD b/score/launch_manager/src/daemon/src/alive_monitor/details/ifappl/BUILD index 6417eadda..73fd72d0e 100644 --- a/score/launch_manager/src/daemon/src/alive_monitor/details/ifappl/BUILD +++ b/score/launch_manager/src/daemon/src/alive_monitor/details/ifappl/BUILD @@ -54,6 +54,7 @@ cc_library( ":data_structures", "//score/launch_manager/src/daemon/src/alive_monitor/details/ifexm:observable_event", "//score/launch_manager/src/daemon/src/alive_monitor/details/timers:timers_os_clock", + "//score/launch_manager/src/daemon/src/common:log", ], ) diff --git a/score/launch_manager/src/daemon/src/alive_monitor/details/ifappl/MonitorIfDaemon.cpp b/score/launch_manager/src/daemon/src/alive_monitor/details/ifappl/MonitorIfDaemon.cpp index f8caa9f51..59e4e55e5 100644 --- a/score/launch_manager/src/daemon/src/alive_monitor/details/ifappl/MonitorIfDaemon.cpp +++ b/score/launch_manager/src/daemon/src/alive_monitor/details/ifappl/MonitorIfDaemon.cpp @@ -15,8 +15,8 @@ #include -#include "score/launch_manager/src/daemon/src/common/log.hpp" #include "score/mw/launch_manager/alive_monitor/details/ifexm/ObservableEvent.hpp" +#include "score/mw/launch_manager/common/log.hpp" namespace score::mw::lifecycle::internal::saf::ifappl { diff --git a/score/launch_manager/src/daemon/src/alive_monitor/details/ifexm/BUILD b/score/launch_manager/src/daemon/src/alive_monitor/details/ifexm/BUILD index e8cdc0c30..a30d5fa03 100644 --- a/score/launch_manager/src/daemon/src/alive_monitor/details/ifexm/BUILD +++ b/score/launch_manager/src/daemon/src/alive_monitor/details/ifexm/BUILD @@ -11,6 +11,7 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* load("@rules_cc//cc:defs.bzl", "cc_library") +load("//tests/utils/bazel:unit_test.bzl", "lm_cc_test") cc_library( name = "observable_event", @@ -20,9 +21,9 @@ cc_library( strip_include_prefix = "/score/launch_manager/src/daemon/src/alive_monitor/details/ifexm", visibility = ["//score/launch_manager/src/daemon/src/alive_monitor:__subpackages__"], deps = [ + ":supervision_event", "//score/launch_manager/src/daemon/src/alive_monitor/details/common:observer", "//score/launch_manager/src/daemon/src/alive_monitor/details/timers:timers_os_clock", - "//score/launch_manager/src/daemon/src/supervision_control_client", ], ) @@ -35,9 +36,46 @@ cc_library( visibility = ["//score/launch_manager/src/daemon/src/alive_monitor:__subpackages__"], deps = [ ":observable_event", + ":supervision_event", + "//score/launch_manager:error_event", "//score/launch_manager/src/daemon/src/alive_monitor/details/timers:time_conversion", "//score/launch_manager/src/daemon/src/alive_monitor/details/timers:timers_os_clock", "//score/launch_manager/src/daemon/src/common:log", - "//score/launch_manager/src/daemon/src/supervision_control_client", + ], +) + +cc_library( + name = "supervision_event", + hdrs = ["supervision_event.hpp"], + include_prefix = "score/mw/launch_manager/alive_monitor/details/ifexm", + strip_include_prefix = "/score/launch_manager/src/daemon/src/alive_monitor/details/ifexm", + visibility = ["//score:__subpackages__"], + deps = [ + "//externals/ipc_dropin", + "//score/launch_manager/src/daemon/src/common:identifier_hash", + ], +) + +cc_library( + name = "supervision_handle", + hdrs = ["supervision_handle.hpp"], + include_prefix = "score/mw/launch_manager/alive_monitor/details/ifexm", + strip_include_prefix = "/score/launch_manager/src/daemon/src/alive_monitor/details/ifexm", + visibility = ["//score/launch_manager/src/daemon/src/alive_monitor:__subpackages__"], + deps = [ + ":supervision_event", + "//score/launch_manager/src/daemon/src/alive_monitor:isupervision_event_publisher", + "//score/launch_manager/src/daemon/src/common:alive_interface_path", + "//score/launch_manager/src/daemon/src/common:identifier_hash", + "//score/launch_manager/src/daemon/src/common:log", + ], +) + +lm_cc_test( + name = "supervision_control_client_ut", + srcs = ["supervision_control_client_ut.cpp"], + deps = [ + ":supervision_handle", + "@googletest//:gtest_main", ], ) diff --git a/score/launch_manager/src/daemon/src/alive_monitor/details/ifexm/ObservableEvent.hpp b/score/launch_manager/src/daemon/src/alive_monitor/details/ifexm/ObservableEvent.hpp index f43cd5aaf..1575bb97e 100644 --- a/score/launch_manager/src/daemon/src/alive_monitor/details/ifexm/ObservableEvent.hpp +++ b/score/launch_manager/src/daemon/src/alive_monitor/details/ifexm/ObservableEvent.hpp @@ -18,7 +18,7 @@ #include "score/mw/launch_manager/alive_monitor/details/timers/Timers_OsClock.hpp" #include -#include "score/mw/launch_manager/supervision_control_client/supervision_event.hpp" +#include "score/mw/launch_manager/alive_monitor/details/ifexm/supervision_event.hpp" namespace score { diff --git a/score/launch_manager/src/daemon/src/alive_monitor/details/ifexm/ObservableEventReader.cpp b/score/launch_manager/src/daemon/src/alive_monitor/details/ifexm/ObservableEventReader.cpp index 448bbe4b2..52734a7d2 100644 --- a/score/launch_manager/src/daemon/src/alive_monitor/details/ifexm/ObservableEventReader.cpp +++ b/score/launch_manager/src/daemon/src/alive_monitor/details/ifexm/ObservableEventReader.cpp @@ -14,12 +14,13 @@ #include "score/mw/launch_manager/alive_monitor/details/ifexm/ObservableEventReader.hpp" #include "score/launch_manager/src/daemon/src/common/log.hpp" #include "score/mw/launch_manager/alive_monitor/details/timers/TimeConversion.hpp" +#include "score/mw/lifecycle/execution_error.h" namespace score::mw::lifecycle::internal::saf::ifexm { -ObservableEventReader::ObservableEventReader(std::unique_ptr f_observable_event_receiver) - : processStateReceiverHM(std::move(f_observable_event_receiver)) +ObservableEventReader::ObservableEventReader(std::shared_ptr f_observable_event_receiver) + : buffer_(f_observable_event_receiver) { } @@ -64,8 +65,7 @@ bool ObservableEventReader::distributeChanges(const timers::NanoSecondType f_syn bool flagContinue{true}; do { - score::Result> resultEvent{ - processStateReceiverHM->getNextSupervisionEvent()}; + score::Result> resultEvent{getNextSupervisionEvent()}; if (resultEvent) { @@ -93,8 +93,36 @@ bool ObservableEventReader::distributeChanges(const timers::NanoSecondType f_syn return flagSuccess; } +score::Result> ObservableEventReader::getNextSupervisionEvent() noexcept +{ + score::mw::lifecycle::SupervisionEvent event; + if (buffer_->getOverflowFlag()) + { + LM_LOG_ERROR() + << "Supervision event buffer overflow has occurred, this will be reported as a communication error"; + return score::Result>{ + score::MakeUnexpected(score::mw::lifecycle::ExecErrc::kCommunicationError)}; + } + + if (buffer_->empty()) + { + return score::Result>{std::nullopt}; + } + + auto res = buffer_->tryDequeue(event); + if (res) + { + return score::Result>{event}; + } + else + { + return score::Result>{ + score::MakeUnexpected(score::mw::lifecycle::ExecErrc::kGeneralError)}; + } +} + bool ObservableEventReader::pushUpdateTill( - const LcmSupervisionEvent& f_event, + const SupervisionEvent& f_event, const timers::NanoSecondType f_syncTimestamp) noexcept { bool isSyncTimestampReached{false}; diff --git a/score/launch_manager/src/daemon/src/alive_monitor/details/ifexm/ObservableEventReader.hpp b/score/launch_manager/src/daemon/src/alive_monitor/details/ifexm/ObservableEventReader.hpp index 228acf5da..a14b33ae7 100644 --- a/score/launch_manager/src/daemon/src/alive_monitor/details/ifexm/ObservableEventReader.hpp +++ b/score/launch_manager/src/daemon/src/alive_monitor/details/ifexm/ObservableEventReader.hpp @@ -17,9 +17,10 @@ #include #include "score/mw/launch_manager/alive_monitor/details/ifexm/ObservableEvent.hpp" +#include "score/mw/launch_manager/alive_monitor/details/ifexm/supervision_event.hpp" #include "score/mw/launch_manager/alive_monitor/details/timers/Timers_OsClock.hpp" -#include "score/mw/launch_manager/supervision_control_client/isupervision_control_receiver.hpp" -#include "score/mw/launch_manager/supervision_control_client/supervision_event.hpp" + +#include "score/result/result.h" namespace score { @@ -36,12 +37,9 @@ namespace ifexm class ObservableEventReader { public: - using LcmSupervisionEvent = score::mw::lifecycle::SupervisionEvent; - using LcmSupervisionControlReceiver = score::mw::lifecycle::ISupervisionControlReceiver; - /// @brief Constructor - /// @param [in] f_observable_event_receiver Process state receiver implementation - ObservableEventReader(std::unique_ptr f_observable_event_receiver); + /// @param [in] f_observable_event_receiver Shared pointer to the ring buffer used to receive supervision events + explicit ObservableEventReader(std::shared_ptr f_observable_event_receiver); /// @brief No Copy Constructor ObservableEventReader(const ObservableEventReader&) = delete; @@ -76,10 +74,14 @@ class ObservableEventReader /// @param [in] f_event Supervision event for which push update is needed /// @param [in] f_syncTimestamp Timestamp for cyclic synchronization /// @return true (sync timestamp is reached), false (sync timestamp is not yet reached) - bool pushUpdateTill(const LcmSupervisionEvent& f_event, const timers::NanoSecondType f_syncTimestamp) noexcept; + bool pushUpdateTill(const SupervisionEvent& f_event, const timers::NanoSecondType f_syncTimestamp) noexcept; + + /// @brief Returns a queued SupervisionEvent that has not yet been parsed. + /// @returns Result containing SupervisionEvent in case of success, or ExecError in case of failure. + score::Result> getNextSupervisionEvent() noexcept; - /// @brief Process state receiver for HM thread - std::unique_ptr processStateReceiverHM; + /// @brief Ring buffer through which supervision events are received from the Launch Manager + std::shared_ptr buffer_; /// @brief Map for process id and observable event object std::map processStateMap{}; diff --git a/score/launch_manager/src/daemon/src/alive_monitor/details/ifexm/supervision_control_client_ut.cpp b/score/launch_manager/src/daemon/src/alive_monitor/details/ifexm/supervision_control_client_ut.cpp new file mode 100644 index 000000000..c089dabac --- /dev/null +++ b/score/launch_manager/src/daemon/src/alive_monitor/details/ifexm/supervision_control_client_ut.cpp @@ -0,0 +1,110 @@ +/******************************************************************************** + * Copyright (c) 2026 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/launch_manager/alive_monitor/details/ifexm/supervision_handle.hpp" +#include +#include + +using namespace testing; +using namespace score::mw::lifecycle; + +class SupervisionControlClient_UT : public ::testing::Test +{ + protected: + void SetUp() override + { + RecordProperty("TestType", "interface-test"); + RecordProperty("DerivationTechnique", "explorative-testing "); + buffer_ = std::make_shared(); + handle_ = std::make_unique(process_, buffer_); + } + + void TearDown() override + { + handle_.reset(); + buffer_.reset(); + } + + const IdentifierHash process_{"Process"}; + std::shared_ptr buffer_; + std::unique_ptr handle_; +}; + +TEST_F(SupervisionControlClient_UT, SupervisionControlClient_QueueOneEvent_Succeeds) +{ + RecordProperty( + "Description", + "This test verifies that a single SupervisionEvent can be successfully queued using the " + "SupervisionControlNotifier and retrieved using the SupervisionControlReceiver."); + SupervisionEvent event1{.id = process_, .eventType = SupervisionEventType::kActivation, .systemClockTimestamp = {}}; + + clock_gettime(CLOCK_MONOTONIC, &event1.systemClockTimestamp); + + bool queued = handle_->reportActivation(event1.systemClockTimestamp); + ASSERT_TRUE(queued); + + SupervisionEvent result; + ASSERT_TRUE(buffer_->tryDequeue(result)); + + EXPECT_EQ(result.id, event1.id); + EXPECT_EQ(result.eventType, event1.eventType); + EXPECT_EQ(result.systemClockTimestamp.tv_nsec, event1.systemClockTimestamp.tv_nsec); + + bool items_remaining = buffer_->tryDequeue(result); + ASSERT_FALSE(items_remaining); +} + +TEST_F(SupervisionControlClient_UT, SupervisionControlClient_QueueMaxNumberOfEvents_Succeeds) +{ + RecordProperty( + "Description", + "This test verifies that the SupervisionControlNotifier can successfully queue the maximum number of " + "SupervisionEvent " + "instances defined by the buffer size, and that they can be retrieved using the SupervisionControlReceiver."); + + SupervisionEvent event{.id = process_, .eventType = SupervisionEventType::kActivation, .systemClockTimestamp = {}}; + + for (size_t i = 0; i < static_cast(BufferConstants::BUFFER_QUEUE_SIZE); ++i) + { + bool queued = handle_->reportActivation(event.systemClockTimestamp); + ASSERT_TRUE(queued) << "Failed to queue event at index " << i; + } + + SupervisionEvent result; + + for (size_t i = 0; i < static_cast(BufferConstants::BUFFER_QUEUE_SIZE); ++i) + { + ASSERT_TRUE(buffer_->tryDequeue(result)); + EXPECT_EQ(result.id, event.id); + } + + bool items_remaining = buffer_->tryDequeue(result); + ASSERT_FALSE(items_remaining); +} + +TEST_F(SupervisionControlClient_UT, SupervisionControlClient_QueueOneEventTooMany_Fails) +{ + RecordProperty( + "Description", + "This test verifies that attempting to queue a SupervisionEvent when the buffer is already at maximum capacity " + "results in a failure, and that no additional events can be retrieved from the receiver."); + SupervisionEvent event{.id = process_, .eventType = SupervisionEventType::kActivation, .systemClockTimestamp = {}}; + + for (size_t i = 0; i < static_cast(BufferConstants::BUFFER_QUEUE_SIZE); ++i) + { + bool queued = handle_->reportActivation(event.systemClockTimestamp); + ASSERT_TRUE(queued) << "Failed to queue event at index " << i; + } + + bool queued = handle_->reportActivation(event.systemClockTimestamp); + ASSERT_FALSE(queued) << "Expected queuing to fail due to full buffer"; +} diff --git a/score/launch_manager/src/daemon/src/supervision_control_client/supervision_event.hpp b/score/launch_manager/src/daemon/src/alive_monitor/details/ifexm/supervision_event.hpp similarity index 86% rename from score/launch_manager/src/daemon/src/supervision_control_client/supervision_event.hpp rename to score/launch_manager/src/daemon/src/alive_monitor/details/ifexm/supervision_event.hpp index 320acc4d7..031e519d8 100644 --- a/score/launch_manager/src/daemon/src/supervision_control_client/supervision_event.hpp +++ b/score/launch_manager/src/daemon/src/alive_monitor/details/ifexm/supervision_event.hpp @@ -14,9 +14,11 @@ #ifndef SUPERVISION_EVENT_HPP_INCLUDED #define SUPERVISION_EVENT_HPP_INCLUDED +#include "ipc_dropin/ringbuffer.hpp" #include "score/mw/launch_manager/common/identifier_hash.hpp" #include #include +#include namespace score { @@ -55,6 +57,10 @@ constexpr std::size_t BUFFER_QUEUE_SIZE = 4096UL; } // namespace BufferConstants +using SupervisionBufferType = ipc_dropin::RingBuffer< + static_cast(score::mw::lifecycle::BufferConstants::BUFFER_QUEUE_SIZE), + static_cast(score::mw::lifecycle::BufferConstants::BUFFER_MAXPAYLOAD)>; + } // namespace mw::lifecycle } // namespace score diff --git a/score/launch_manager/src/daemon/src/alive_monitor/details/ifexm/supervision_handle.hpp b/score/launch_manager/src/daemon/src/alive_monitor/details/ifexm/supervision_handle.hpp new file mode 100644 index 000000000..9c60dd516 --- /dev/null +++ b/score/launch_manager/src/daemon/src/alive_monitor/details/ifexm/supervision_handle.hpp @@ -0,0 +1,92 @@ +/******************************************************************************** + * Copyright (c) 2026 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 SUPERVISION_HANDLE_HPP_INCLUDED +#define SUPERVISION_HANDLE_HPP_INCLUDED + +#include "score/mw/launch_manager/alive_monitor/details/ifexm/supervision_event.hpp" +#include "score/mw/launch_manager/alive_monitor/isupervision_event_publisher.hpp" +#include "score/mw/launch_manager/common/alive_interface_path.hpp" +#include "score/mw/launch_manager/common/identifier_hash.hpp" +#include "score/mw/launch_manager/common/log.hpp" + +#include + +namespace score +{ + +namespace mw::lifecycle +{ + +/// @brief A supervision handle can be used by a process to manage its own alive supervision. It should be constructed +/// by the alive monitor and provided to a process so that the process need not have access to the supervision buffer. +/// The process can then report its own activation and deactivation. +class SupervisionHandle : public ISupervisionEventPublisher +{ + public: + /// @brief Construct a new supervision handle. + /// @param process_id Identifier of the process being supervised. + /// @param buffer Buffer to push supervision events to. + explicit SupervisionHandle(IdentifierHash process_id, std::shared_ptr buffer) + : process_id_(process_id), buffer_(buffer) + { + ipc_path_ = std::move(internal::aliveInterfacePath(process_id_)); + } + + /// @brief Report that the calling process has reached the active state at @param time + bool reportActivation(timespec time) noexcept override + { + return queueSupervisionEvent({process_id_, SupervisionEventType::kActivation, time}); + } + + /// @brief Report that the calling process has changed from the active state at @param time + bool reportDeactivation(timespec time) noexcept override + { + return queueSupervisionEvent({process_id_, SupervisionEventType::kDeactivation, time}); + } + + /// @brief Get the name of the IPC file alive indications are sent to. + std::string_view getConnectionId() const noexcept override + { + return ipc_path_; + } + + private: + /// @brief Attempts to push a supervision event so that the alive monitor can be informed about it. + /// @param[in] f_event The SupervisionEvent to be queued + /// @returns True on success, false for failure + bool queueSupervisionEvent(const score::mw::lifecycle::SupervisionEvent& f_event) noexcept + { + if (buffer_->tryEnqueue(f_event)) + { + return true; + } + else + { + LM_LOG_ERROR() << "Failed to queue supervision event"; + return false; + } + } + + /// @brief Identifier of the process being supervised. + const IdentifierHash process_id_; + /// @brief Buffer to push supervision events to + std::shared_ptr buffer_; + /// @brief IPC path for alive indications + std::string ipc_path_; +}; + +} // namespace mw::lifecycle + +} // namespace score + +#endif diff --git a/score/launch_manager/src/daemon/src/supervision_control_client/isupervision_event_publisher.hpp b/score/launch_manager/src/daemon/src/alive_monitor/isupervision_event_publisher.hpp similarity index 71% rename from score/launch_manager/src/daemon/src/supervision_control_client/isupervision_event_publisher.hpp rename to score/launch_manager/src/daemon/src/alive_monitor/isupervision_event_publisher.hpp index 6921fb045..ff30af4e4 100644 --- a/score/launch_manager/src/daemon/src/supervision_control_client/isupervision_event_publisher.hpp +++ b/score/launch_manager/src/daemon/src/alive_monitor/isupervision_event_publisher.hpp @@ -13,8 +13,8 @@ #ifndef ISUPERVISION_EVENT_PUBLISHER_HPP_INCLUDED #define ISUPERVISION_EVENT_PUBLISHER_HPP_INCLUDED -#include "score/mw/launch_manager/common/identifier_hash.hpp" #include +#include namespace score { @@ -31,11 +31,14 @@ class ISupervisionEventPublisher /// @brief Destructor. virtual ~ISupervisionEventPublisher() noexcept = default; - /// @brief Report that process with @param id has reached the active state at @param time - virtual bool reportActivation(IdentifierHash id, timespec time) noexcept = 0; + /// @brief Report that the calling process has reached the active state at @param time + virtual bool reportActivation(timespec time) noexcept = 0; - /// @brief Report that process with @param id has changed from the active state at @param time - virtual bool reportDeactivation(IdentifierHash id, timespec time) noexcept = 0; + /// @brief Report that the calling process has changed from the active state at @param time + virtual bool reportDeactivation(timespec time) noexcept = 0; + + /// @brief Get the name of the IPC file alive indications are sent to. + virtual std::string_view getConnectionId() const noexcept = 0; }; } // namespace mw::lifecycle diff --git a/score/launch_manager/src/daemon/src/alive_monitor/isupervision_factory.hpp b/score/launch_manager/src/daemon/src/alive_monitor/isupervision_factory.hpp new file mode 100644 index 000000000..f40ab6de9 --- /dev/null +++ b/score/launch_manager/src/daemon/src/alive_monitor/isupervision_factory.hpp @@ -0,0 +1,51 @@ +/******************************************************************************** + * Copyright (c) 2026 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 ISUPERVISION_FACTORY_HPP_INCLUDED +#define ISUPERVISION_FACTORY_HPP_INCLUDED + +#include +#include + +#include "score/mw/launch_manager/alive_monitor/details/ifexm/supervision_handle.hpp" +#include "score/mw/launch_manager/common/identifier_hash.hpp" +#include "score/mw/launch_manager/configuration/component_config.hpp" + +namespace score +{ + +namespace mw::lifecycle +{ + +class ISupervisionFactory +{ + public: + /// @brief Destructor. + virtual ~ISupervisionFactory() noexcept = default; + + /// @brief Set up alive supervision for the identified process. Alive supervision is not started until the publisher + /// is notified. + /// @param [in] id Identifier of the process. + /// @param [in] uid The configured uid of the process. + /// @param [in] config Alive supervision configuration for the process. + /// @returns Handle for the process to start and stop its own supervision. + virtual std::unique_ptr constructSupervision( + const IdentifierHash id, + const uid_t uid, + const internal::configuration::ComponentAliveSupervision& config) = 0; +}; + +} // namespace mw::lifecycle + +} // namespace score + +#endif diff --git a/score/launch_manager/src/daemon/src/process_group_manager/ialive_monitor_thread.hpp b/score/launch_manager/src/daemon/src/alive_monitor/mock_alive_monitor.hpp similarity index 53% rename from score/launch_manager/src/daemon/src/process_group_manager/ialive_monitor_thread.hpp rename to score/launch_manager/src/daemon/src/alive_monitor/mock_alive_monitor.hpp index 23fbf41f2..915379fc9 100644 --- a/score/launch_manager/src/daemon/src/process_group_manager/ialive_monitor_thread.hpp +++ b/score/launch_manager/src/daemon/src/alive_monitor/mock_alive_monitor.hpp @@ -10,26 +10,23 @@ * * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ +#ifndef MOCK_ALIVE_MONITOR_HPP_INCLUDED +#define MOCK_ALIVE_MONITOR_HPP_INCLUDED -#ifndef SCORE_LCM_IALIVE_MONITOR_THREAD_HPP_INCLUDED -#define SCORE_LCM_IALIVE_MONITOR_THREAD_HPP_INCLUDED +#include "score/mw/launch_manager/alive_monitor/IAliveMonitor.hpp" +#include -namespace score +namespace score::mw::lifecycle::internal::saf::daemon { -namespace mw::lifecycle -{ -namespace internal -{ -class IAliveMonitorThread + +class MockAliveMonitor : public IAliveMonitor { public: - virtual bool start() = 0; - virtual void stop() = 0; - - virtual ~IAliveMonitorThread() = default; + MOCK_METHOD(bool, startMonitoring, (), (override)); + MOCK_METHOD(void, stopMonitoring, (), (override)); + MOCK_METHOD(ISupervisionFactory&, getSupervisionFactory, (), (const, override)); }; -} // namespace internal -} // namespace mw::lifecycle -} // namespace score + +} // namespace score::mw::lifecycle::internal::saf::daemon #endif diff --git a/score/launch_manager/src/daemon/src/supervision_control_client/mock_supervision_event_publisher.hpp b/score/launch_manager/src/daemon/src/alive_monitor/mock_supervision_event_publisher.hpp similarity index 73% rename from score/launch_manager/src/daemon/src/supervision_control_client/mock_supervision_event_publisher.hpp rename to score/launch_manager/src/daemon/src/alive_monitor/mock_supervision_event_publisher.hpp index 645c82cf9..c34b8b370 100644 --- a/score/launch_manager/src/daemon/src/supervision_control_client/mock_supervision_event_publisher.hpp +++ b/score/launch_manager/src/daemon/src/alive_monitor/mock_supervision_event_publisher.hpp @@ -13,7 +13,7 @@ #ifndef MOCK_SUPERVISION_EVENT_PUBLISHER_HPP_INCLUDED #define MOCK_SUPERVISION_EVENT_PUBLISHER_HPP_INCLUDED -#include "score/mw/launch_manager/supervision_control_client/isupervision_event_publisher.hpp" +#include "score/mw/launch_manager/alive_monitor/isupervision_event_publisher.hpp" #include namespace score::mw::lifecycle @@ -22,8 +22,9 @@ namespace score::mw::lifecycle class MockSupervisionEventPublisher : public ISupervisionEventPublisher { public: - MOCK_METHOD(bool, reportActivation, (IdentifierHash id, timespec time), (override, noexcept)); - MOCK_METHOD(bool, reportDeactivation, (IdentifierHash id, timespec time), (override, noexcept)); + MOCK_METHOD(bool, reportActivation, (timespec time), (override, noexcept)); + MOCK_METHOD(bool, reportDeactivation, (timespec time), (override, noexcept)); + MOCK_METHOD(std::string_view, getConnectionId, (), (const, override, noexcept)); }; } // namespace score::mw::lifecycle diff --git a/score/launch_manager/src/daemon/src/supervision_control_client/mock_supervision_control_notifier.hpp b/score/launch_manager/src/daemon/src/alive_monitor/mock_supervision_factory.hpp similarity index 51% rename from score/launch_manager/src/daemon/src/supervision_control_client/mock_supervision_control_notifier.hpp rename to score/launch_manager/src/daemon/src/alive_monitor/mock_supervision_factory.hpp index db237840a..025b49e6e 100644 --- a/score/launch_manager/src/daemon/src/supervision_control_client/mock_supervision_control_notifier.hpp +++ b/score/launch_manager/src/daemon/src/alive_monitor/mock_supervision_factory.hpp @@ -10,23 +10,26 @@ * * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -#ifndef MOCK_SUPERVISION_CONTROL_NOTIFIER_HPP_INCLUDED -#define MOCK_SUPERVISION_CONTROL_NOTIFIER_HPP_INCLUDED -#include "score/mw/launch_manager/supervision_control_client/isupervision_control_notifier.hpp" +#ifndef MOCK_SUPERVISION_FACTORY_HPP_INCLUDED +#define MOCK_SUPERVISION_FACTORY_HPP_INCLUDED + +#include "score/mw/launch_manager/alive_monitor/isupervision_factory.hpp" #include namespace score::mw::lifecycle { -class MockSupervisionControlNotifier : public ISupervisionControlNotifier +class MockSupervisionFactory : public ISupervisionFactory { public: - MOCK_METHOD(bool, reportActivation, (IdentifierHash id, timespec time), (override, noexcept)); - MOCK_METHOD(bool, reportDeactivation, (IdentifierHash id, timespec time), (override, noexcept)); - MOCK_METHOD(std::unique_ptr, constructReceiver, (), (override)); + MOCK_METHOD( + std::unique_ptr, + constructSupervision, + (const IdentifierHash id, const uid_t uid, const internal::configuration::ComponentAliveSupervision& config), + (override)); }; } // namespace score::mw::lifecycle -#endif // MOCK_SUPERVISION_CONTROL_NOTIFIER_HPP_INCLUDED +#endif diff --git a/score/launch_manager/src/daemon/src/main.cpp b/score/launch_manager/src/daemon/src/main.cpp index 9ffa87a35..cf24f7291 100644 --- a/score/launch_manager/src/daemon/src/main.cpp +++ b/score/launch_manager/src/daemon/src/main.cpp @@ -20,10 +20,8 @@ #include "score/mw/launch_manager/alive_monitor/details/daemon/AliveMonitorImpl.hpp" #include "score/mw/launch_manager/common/log.hpp" #include "score/mw/launch_manager/configuration/flatbuffer_config_loader.hpp" -#include "score/mw/launch_manager/process_group_manager/alive_monitor_thread.hpp" #include "score/mw/launch_manager/process_group_manager/process_group_manager.hpp" #include "score/mw/launch_manager/recovery_client/recovery_client.hpp" -#include "score/mw/launch_manager/supervision_control_client/supervision_control_notifier.hpp" #include "score/mw/launch_manager/watchdog/WatchdogFactory.hpp" using namespace std; @@ -161,23 +159,19 @@ int main(int argc, const char* argv[]) std::shared_ptr recoveryClient{std::make_shared()}; - auto supervision_control_notifier = std::make_unique(); + const std::size_t supervised_components = std::count_if( + config_result.value().components().begin(), + config_result.value().components().end(), + [](const configuration::ComponentConfig& component) { + return component.component_properties.application_profile.alive_supervision.has_value(); + }); - // currently this is copying the config. std::unique_ptr healthMonitor{std::make_unique( - recoveryClient, supervision_control_notifier->constructReceiver(), *config_result)}; - static_cast(config_result.value().takeAliveSupervision()); - - std::unique_ptr aliveMonitorThread{ - std::make_unique(std::move(healthMonitor))}; + recoveryClient, config_result.value().takeAliveSupervision(), supervised_components)}; auto watchdog = watchdog::createWatchdog(); auto process_group_manager = std::make_unique( - std::move(config_result).value(), - std::move(aliveMonitorThread), - recoveryClient, - std::move(supervision_control_notifier), - std::move(watchdog)); + std::move(config_result).value(), std::move(healthMonitor), recoveryClient, std::move(watchdog)); if (process_group_manager->initialize()) { diff --git a/score/launch_manager/src/daemon/src/process_group_manager/BUILD b/score/launch_manager/src/daemon/src/process_group_manager/BUILD index 707cb5202..c7fe705f3 100644 --- a/score/launch_manager/src/daemon/src/process_group_manager/BUILD +++ b/score/launch_manager/src/daemon/src/process_group_manager/BUILD @@ -38,40 +38,6 @@ cc_library( ], ) -cc_library( - name = "ialive_monitor_thread", - hdrs = ["ialive_monitor_thread.hpp"], - include_prefix = "score/mw/launch_manager/process_group_manager", - strip_include_prefix = "/score/launch_manager/src/daemon/src/process_group_manager", - visibility = ["//score:__subpackages__"], -) - -cc_library( - name = "mock_ialive_monitor_thread", - testonly = True, - hdrs = ["mock_alive_monitor_thread.hpp"], - include_prefix = "score/mw/launch_manager/process_group_manager", - strip_include_prefix = "/score/launch_manager/src/daemon/src/process_group_manager", - visibility = ["//score:__subpackages__"], - deps = [ - ":ialive_monitor_thread", - "@googletest//:gtest", - ], -) - -cc_library( - name = "alive_monitor_thread", - srcs = ["alive_monitor_thread.cpp"], - hdrs = ["alive_monitor_thread.hpp"], - include_prefix = "score/mw/launch_manager/process_group_manager", - strip_include_prefix = "/score/launch_manager/src/daemon/src/process_group_manager", - visibility = ["//score:__subpackages__"], - deps = [ - ":ialive_monitor_thread", - "//score/launch_manager/src/daemon/src/alive_monitor", - ], -) - cc_library( name = "process_group_manager", srcs = ["process_group_manager.cpp"], @@ -80,8 +46,8 @@ cc_library( strip_include_prefix = "/score/launch_manager/src/daemon/src/process_group_manager", visibility = ["//score:__subpackages__"], deps = [ - ":ialive_monitor_thread", ":iprocess", + "//score/launch_manager/src/daemon/src/alive_monitor:i_alive_monitor", "//score/launch_manager/src/daemon/src/common:identifier_hash", "//score/launch_manager/src/daemon/src/common/concurrency:mpmc_concurrent_queue", "//score/launch_manager/src/daemon/src/common/concurrency:thread_pool", @@ -96,7 +62,6 @@ cc_library( "//score/launch_manager/src/daemon/src/process_group_manager/details:process_monitor", "//score/launch_manager/src/daemon/src/process_group_manager/details:safe_process_map", "//score/launch_manager/src/daemon/src/recovery_client", - "//score/launch_manager/src/daemon/src/supervision_control_client:isupervision_control_notifier", "//score/launch_manager/src/daemon/src/watchdog:i_watchdog_if", "@score_baselibs//score/language/futurecpp", ], diff --git a/score/launch_manager/src/daemon/src/process_group_manager/alive_monitor_thread.cpp b/score/launch_manager/src/daemon/src/process_group_manager/alive_monitor_thread.cpp deleted file mode 100644 index e2c4c56dd..000000000 --- a/score/launch_manager/src/daemon/src/process_group_manager/alive_monitor_thread.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/******************************************************************************** - * 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/launch_manager/process_group_manager/alive_monitor_thread.hpp" - -namespace score::mw::lifecycle::internal -{ - -AliveMonitorThread::AliveMonitorThread(std::unique_ptr health_monitor) - : m_health_monitor(std::move(health_monitor)) -{ - initResult = m_health_monitor->init(); -} - -bool AliveMonitorThread::start() -{ - alive_monitor_thread_ = std::thread([this]() { - if (initResult == saf::daemon::EInitCode::kNoError) - { - m_health_monitor->run(stop_thread_); - } - }); - - return initResult == saf::daemon::EInitCode::kNoError; -} - -void AliveMonitorThread::stop() -{ - stop_thread_.store(true); - if (alive_monitor_thread_.joinable()) - { - alive_monitor_thread_.join(); - } -} - -} // namespace score::mw::lifecycle::internal diff --git a/score/launch_manager/src/daemon/src/process_group_manager/alive_monitor_thread.hpp b/score/launch_manager/src/daemon/src/process_group_manager/alive_monitor_thread.hpp deleted file mode 100644 index 4290470a4..000000000 --- a/score/launch_manager/src/daemon/src/process_group_manager/alive_monitor_thread.hpp +++ /dev/null @@ -1,58 +0,0 @@ -/******************************************************************************** - * 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_LCM_ALIVE_MONITOR_THREAD_HPP_INCLUDED -#define SCORE_LCM_ALIVE_MONITOR_THREAD_HPP_INCLUDED - -#include "score/mw/launch_manager/alive_monitor/details/daemon/IAliveMonitor.hpp" -#include -#include -#include - -#include "score/mw/launch_manager/process_group_manager/ialive_monitor_thread.hpp" - -namespace score -{ -namespace mw::lifecycle -{ -namespace internal -{ - -/// @brief AliveMonitor manages the lifecycle of the alive monitoring daemon in a separate thread. -class AliveMonitorThread final : public IAliveMonitorThread -{ - public: - explicit AliveMonitorThread(std::unique_ptr health_monitor); - - /// @brief Starts the Alive Monitor thread. - /// @return true if the Alive Monitor started successfully, false otherwise. - bool start() override; - - /// @brief Stops the Alive Monitor thread. - void stop() override; - - private: - void notifyInitializationComplete( - score::mw::lifecycle::internal::saf::daemon::EInitCode& f_init_status_r, - const score::mw::lifecycle::internal::saf::daemon::EInitCode f_init_result); - void waitForInitializationCompleted(score::mw::lifecycle::internal::saf::daemon::EInitCode& f_init_status_r); - - std::unique_ptr m_health_monitor{nullptr}; - std::thread alive_monitor_thread_{}; - std::atomic_bool stop_thread_{false}; - saf::daemon::EInitCode initResult{saf::daemon::EInitCode::kNotInitialized}; -}; - -} // namespace internal -} // namespace mw::lifecycle -} // namespace score -#endif diff --git a/score/launch_manager/src/daemon/src/process_group_manager/details/BUILD b/score/launch_manager/src/daemon/src/process_group_manager/details/BUILD index fd0a50f1d..b91d398c9 100644 --- a/score/launch_manager/src/daemon/src/process_group_manager/details/BUILD +++ b/score/launch_manager/src/daemon/src/process_group_manager/details/BUILD @@ -144,8 +144,8 @@ cc_library( visibility = ["//score/launch_manager/src/daemon/src/process_group_manager:__pkg__"], deps = [ ":safe_process_map", + "//score/launch_manager/src/daemon/src/alive_monitor:isupervision_factory", "//score/launch_manager/src/daemon/src/process_group_manager:iprocess", - "//score/launch_manager/src/daemon/src/supervision_control_client:isupervision_event_publisher", ], ) @@ -160,14 +160,13 @@ cc_library( ":icomponent", ":process_handling", ":safe_process_map", - "//score/launch_manager/src/daemon/src/common:alive_interface_path", + "//score/launch_manager/src/daemon/src/alive_monitor:isupervision_event_publisher", "//score/launch_manager/src/daemon/src/configuration:component_config", "//score/launch_manager/src/daemon/src/control:control_client_channel", "//score/launch_manager/src/daemon/src/osal:ipc_comms", "//score/launch_manager/src/daemon/src/osal:semaphore", "//score/launch_manager/src/daemon/src/process_group_manager:iprocess", "//score/launch_manager/src/daemon/src/process_group_manager:process_state", - "//score/launch_manager/src/daemon/src/supervision_control_client:isupervision_event_publisher", "@score_baselibs//score/language/futurecpp", ], ) @@ -178,8 +177,9 @@ lm_cc_test( deps = [ ":process_info_node", ":safe_process_map", + "//score/launch_manager/src/daemon/src/alive_monitor:mock_supervision_event_publisher", + "//score/launch_manager/src/daemon/src/alive_monitor:mock_supervision_factory", "//score/launch_manager/src/daemon/src/process_group_manager:mock_iprocess", - "//score/launch_manager/src/daemon/src/supervision_control_client:mock_supervision_event_publisher", "@googletest//:gtest_main", ], ) @@ -229,9 +229,10 @@ lm_cc_test( srcs = ["graph_UT.cpp"], deps = [ ":graph", + "//score/launch_manager/src/daemon/src/alive_monitor:mock_supervision_event_publisher", + "//score/launch_manager/src/daemon/src/alive_monitor:mock_supervision_factory", "//score/launch_manager/src/daemon/src/configuration:config", "//score/launch_manager/src/daemon/src/process_group_manager:mock_iprocess", - "//score/launch_manager/src/daemon/src/supervision_control_client:mock_supervision_event_publisher", "@googletest//:gtest_main", ], ) @@ -349,12 +350,12 @@ lm_cc_test( name = "process_group_manager_UT", srcs = ["process_group_manager_UT.cpp"], deps = [ + "//score/launch_manager/src/daemon/src/alive_monitor:mock_alive_monitor", + "//score/launch_manager/src/daemon/src/alive_monitor:mock_supervision_event_publisher", + "//score/launch_manager/src/daemon/src/alive_monitor:mock_supervision_factory", "//score/launch_manager/src/daemon/src/configuration:config", "//score/launch_manager/src/daemon/src/process_group_manager", - "//score/launch_manager/src/daemon/src/process_group_manager:mock_ialive_monitor_thread", "//score/launch_manager/src/daemon/src/recovery_client:mock_recovery_client", - "//score/launch_manager/src/daemon/src/supervision_control_client:isupervision_control_notifier", - "//score/launch_manager/src/daemon/src/supervision_control_client:mock_supervision_control_notifier", "//score/launch_manager/src/daemon/src/watchdog:mock_i_watchdog_if", "@googletest//:gtest_main", ], diff --git a/score/launch_manager/src/daemon/src/process_group_manager/details/graph.hpp b/score/launch_manager/src/daemon/src/process_group_manager/details/graph.hpp index 80e7580cc..89ab3ab58 100644 --- a/score/launch_manager/src/daemon/src/process_group_manager/details/graph.hpp +++ b/score/launch_manager/src/daemon/src/process_group_manager/details/graph.hpp @@ -39,7 +39,6 @@ #include "score/mw/launch_manager/process_group_manager/details/run_target.hpp" #include "score/mw/launch_manager/process_group_manager/details/transition.hpp" #include "score/mw/launch_manager/process_group_manager/iprocess.hpp" -#include "score/mw/launch_manager/supervision_control_client/isupervision_event_publisher.hpp" #include namespace score @@ -155,7 +154,10 @@ class Graph final /// @brief Constructor to initialize a Graph object. /// @param max_num_nodes Maximum number of nodes this graph can hold. + /// @param configuration Configuration containing run target and component information. + /// @param job_queue Queue to push component jobs to for multithreaded processing. /// @param process_handling The interfaces used to start, stop and report on the OS processes. + /// @param transition_result_receiver Object to notify when the initial transition is complete. Graph( uint32_t max_num_nodes, configuration::Config& configuration, diff --git a/score/launch_manager/src/daemon/src/process_group_manager/details/graph_UT.cpp b/score/launch_manager/src/daemon/src/process_group_manager/details/graph_UT.cpp index 3843b68eb..bf4cb26da 100644 --- a/score/launch_manager/src/daemon/src/process_group_manager/details/graph_UT.cpp +++ b/score/launch_manager/src/daemon/src/process_group_manager/details/graph_UT.cpp @@ -20,10 +20,11 @@ #include #include +#include "score/mw/launch_manager/alive_monitor/mock_supervision_event_publisher.hpp" +#include "score/mw/launch_manager/alive_monitor/mock_supervision_factory.hpp" #include "score/mw/launch_manager/configuration/config.hpp" #include "score/mw/launch_manager/process_group_manager/details/graph.hpp" #include "score/mw/launch_manager/process_group_manager/mock_iprocess.hpp" -#include "score/mw/launch_manager/supervision_control_client/mock_supervision_event_publisher.hpp" namespace score::mw::lifecycle::internal { @@ -63,7 +64,7 @@ class GraphTest : public ::testing::Test 10U, config_.value(), job_queue_, - ProcessHandling{mock_supervision_event_publisher_, &process_interface_, mock_process_map}, + ProcessHandling{&process_interface_, mock_process_map, mock_factory_}, &mock_transition_result_publisher_); } @@ -185,6 +186,7 @@ class GraphTest : public ::testing::Test std::shared_ptr mock_process_map = std::make_shared(); NiceMock mock_supervision_event_publisher_{}; MockTransitionResultPublisher mock_transition_result_publisher_{}; + MockSupervisionFactory mock_factory_{}; std::unique_ptr graph_{}; static constexpr std::string_view pg_string{"MainPG"}; diff --git a/score/launch_manager/src/daemon/src/process_group_manager/details/process_group_manager_UT.cpp b/score/launch_manager/src/daemon/src/process_group_manager/details/process_group_manager_UT.cpp index b156a56ad..45c02a4fd 100644 --- a/score/launch_manager/src/daemon/src/process_group_manager/details/process_group_manager_UT.cpp +++ b/score/launch_manager/src/daemon/src/process_group_manager/details/process_group_manager_UT.cpp @@ -13,9 +13,10 @@ #include "score/mw/launch_manager/process_group_manager/process_group_manager.hpp" -#include "score/mw/launch_manager/process_group_manager/mock_alive_monitor_thread.hpp" +#include "score/mw/launch_manager/alive_monitor/mock_alive_monitor.hpp" +#include "score/mw/launch_manager/alive_monitor/mock_supervision_event_publisher.hpp" +#include "score/mw/launch_manager/alive_monitor/mock_supervision_factory.hpp" #include "score/mw/launch_manager/recovery_client/mock_irecovery_client.h" -#include "score/mw/launch_manager/supervision_control_client/mock_supervision_control_notifier.hpp" #include "score/mw/launch_manager/watchdog/mock_IWatchdogIf.hpp" #include @@ -36,20 +37,9 @@ namespace score::mw::lifecycle::internal namespace { -using score::mw::lifecycle::MockRecoveryClient; -using score::mw::lifecycle::MockSupervisionControlNotifier; -using score::mw::lifecycle::internal::watchdog::MockWatchdogIf; - -using score::mw::lifecycle::internal::configuration::AliveSupervisionConfig; -using score::mw::lifecycle::internal::configuration::ApplicationType; -using score::mw::lifecycle::internal::configuration::ComponentConfig; -using score::mw::lifecycle::internal::configuration::Config; -using score::mw::lifecycle::internal::configuration::ConfigBuilder; -using score::mw::lifecycle::internal::configuration::FallbackRunTargetConfig; -using score::mw::lifecycle::internal::configuration::ProcessState; -using score::mw::lifecycle::internal::configuration::ReadyCondition; -using score::mw::lifecycle::internal::configuration::RunTargetConfig; -using score::mw::lifecycle::internal::configuration::WatchdogConfig; +using namespace configuration; +using namespace watchdog; +using saf::daemon::MockAliveMonitor; Config makeMinimalConfig() { @@ -117,7 +107,7 @@ class ProcessGroupManagerWatchdogTest : public Test protected: void expectNormalStartup() { - EXPECT_CALL(*alive_monitor_thread_, start()).WillOnce(Return(true)); + EXPECT_CALL(*alive_monitor_, startMonitoring()).WillOnce(Return(true)); EXPECT_CALL(*watchdog_, init(_, _)).WillOnce(Return(true)); EXPECT_CALL(*watchdog_, enable()).WillOnce(Return(true)); } @@ -127,9 +117,10 @@ class ProcessGroupManagerWatchdogTest : public Test RecordProperty("TestType", "unit-test"); RecordProperty("DerivationTechnique", "explorative-testing"); - auto alive_monitor_thread = std::make_unique>(); - alive_monitor_thread_ = alive_monitor_thread.get(); - ON_CALL(*alive_monitor_thread_, start()).WillByDefault(Return(true)); + auto alive_monitor = std::make_unique>(); + alive_monitor_ = alive_monitor.get(); + ON_CALL(*alive_monitor_, startMonitoring()).WillByDefault(Return(true)); + ON_CALL(*alive_monitor_, getSupervisionFactory).WillByDefault(ReturnRef(factory_)); auto recovery_client = std::make_shared>(); recovery_client_ = recovery_client.get(); @@ -138,22 +129,18 @@ class ProcessGroupManagerWatchdogTest : public Test ON_CALL(*recovery_client_, setRecoveryRequestCallback(_)).WillByDefault(SaveArg<0>(&recovery_callback_)); ON_CALL(*recovery_client_, sendRecoveryRequest(_)).WillByDefault(Return(true)); - auto supervision_control_notifier = std::make_unique>(); - supervision_control_notifier_ = supervision_control_notifier.get(); - ON_CALL(*supervision_control_notifier_, constructReceiver()) - .WillByDefault(Return(ByMove(std::unique_ptr{}))); - ON_CALL(*supervision_control_notifier_, reportActivation(_, _)).WillByDefault(Return(true)); - ON_CALL(*supervision_control_notifier_, reportDeactivation(_, _)).WillByDefault(Return(true)); + ON_CALL(factory_, constructSupervision).WillByDefault(InvokeWithoutArgs([]() { + auto publisher = std::make_unique>(); + ON_CALL(*publisher, reportActivation).WillByDefault(Return(true)); + ON_CALL(*publisher, reportDeactivation).WillByDefault(Return(true)); + return publisher; + })); auto watchdog = std::make_unique>(); watchdog_ = watchdog.get(); process_group_manager_ = std::make_unique( - makeMinimalConfig(), - std::move(alive_monitor_thread), - std::move(recovery_client), - std::move(supervision_control_notifier), - std::move(watchdog)); + makeMinimalConfig(), std::move(alive_monitor), std::move(recovery_client), std::move(watchdog)); } void TearDown() override @@ -161,11 +148,11 @@ class ProcessGroupManagerWatchdogTest : public Test process_group_manager_->deinitialize(); } - MockAliveMonitorThread* alive_monitor_thread_{}; + MockAliveMonitor* alive_monitor_{}; MockRecoveryClient* recovery_client_{}; - score::mw::lifecycle::IRecoveryClient::RecoveryRequestCallback recovery_callback_{}; - MockSupervisionControlNotifier* supervision_control_notifier_{}; + IRecoveryClient::RecoveryRequestCallback recovery_callback_{}; MockWatchdogIf* watchdog_{}; + NiceMock factory_{}; std::unique_ptr process_group_manager_; }; @@ -178,7 +165,7 @@ TEST_F(ProcessGroupManagerWatchdogTest, GivenMinimalConfig_ExpectWatchdogMethods InSequence sequence; expectNormalStartup(); EXPECT_CALL(*watchdog_, disable()).Times(1); - EXPECT_CALL(*alive_monitor_thread_, stop()).Times(1); + EXPECT_CALL(*alive_monitor_, stopMonitoring()).Times(1); // When auto initialize_result = process_group_manager_->initialize(); @@ -197,7 +184,7 @@ TEST_F(ProcessGroupManagerWatchdogTest, GivenMinimalConfig_ExpectWatchdogService }); // Called in deinitialize() after run() returns EXPECT_CALL(*watchdog_, disable()).Times(1); - EXPECT_CALL(*alive_monitor_thread_, stop()).Times(1); + EXPECT_CALL(*alive_monitor_, stopMonitoring()).Times(1); // When ASSERT_TRUE(process_group_manager_->initialize()); @@ -221,7 +208,7 @@ TEST_F(ProcessGroupManagerWatchdogTest, GivenMinimalConfig_ExpectWatchdogFired_W process_group_manager_->cancel(); }); EXPECT_CALL(*watchdog_, disable()).Times(1); - EXPECT_CALL(*alive_monitor_thread_, stop()).Times(1); + EXPECT_CALL(*alive_monitor_, stopMonitoring()).Times(1); // When ASSERT_TRUE(process_group_manager_->initialize()); @@ -232,7 +219,7 @@ TEST_F(ProcessGroupManagerWatchdogTest, GivenMinimalConfig_ExpectWatchdogFired_W ASSERT_TRUE(recovery_callback_); for (int i = 0; i < kNumRecoveryRequests; ++i) { - recovery_callback_(score::mw::lifecycle::IdentifierHash{"overflow_probe"}); + recovery_callback_(IdentifierHash{"overflow_probe"}); } auto run_result = process_group_manager_->run(); @@ -248,7 +235,7 @@ TEST_F(ProcessGroupManagerWatchdogTest, GivenMinimalConfig_ExpectWatchdogDisable // We are explicitly calling deinitialize() in this test for readability, // so disable() and stop() are expected to be called twice: once in deinitialize() and once in TearDown(). EXPECT_CALL(*watchdog_, disable()).Times(2); - EXPECT_CALL(*alive_monitor_thread_, stop()).Times(2); + EXPECT_CALL(*alive_monitor_, stopMonitoring()).Times(2); // When ASSERT_TRUE(process_group_manager_->initialize()); diff --git a/score/launch_manager/src/daemon/src/process_group_manager/details/process_handling.hpp b/score/launch_manager/src/daemon/src/process_group_manager/details/process_handling.hpp index 50cfbb37d..0c1794034 100644 --- a/score/launch_manager/src/daemon/src/process_group_manager/details/process_handling.hpp +++ b/score/launch_manager/src/daemon/src/process_group_manager/details/process_handling.hpp @@ -14,25 +14,25 @@ #ifndef _INCLUDED_PROCESSHANDLING_ #define _INCLUDED_PROCESSHANDLING_ +#include "score/mw/launch_manager/alive_monitor/isupervision_factory.hpp" #include "score/mw/launch_manager/process_group_manager/details/safe_process_map.hpp" #include "score/mw/launch_manager/process_group_manager/iprocess.hpp" -#include "score/mw/launch_manager/supervision_control_client/isupervision_event_publisher.hpp" #include namespace score::mw::lifecycle::internal { -/// @brief Collection of interfaces required to control a OS process. +/// @brief Collection of interfaces required to control an OS process. struct ProcessHandling { - /// @brief Interface for reporting component state to health monitor. - ISupervisionEventPublisher& state_publisher_; - /// @brief Handle to manage the underlying posix process. osal::IProcess* process_interface_{nullptr}; /// @brief Map to store the state of the process. std::shared_ptr process_map_; + + /// @brief Factory to construct component supervisions with. + ISupervisionFactory& supervision_factory; }; } // namespace score::mw::lifecycle::internal diff --git a/score/launch_manager/src/daemon/src/process_group_manager/details/process_info_node.cpp b/score/launch_manager/src/daemon/src/process_group_manager/details/process_info_node.cpp index 8822d9f54..21eba6733 100644 --- a/score/launch_manager/src/daemon/src/process_group_manager/details/process_info_node.cpp +++ b/score/launch_manager/src/daemon/src/process_group_manager/details/process_info_node.cpp @@ -13,7 +13,6 @@ #include "process_info_node.hpp" #include "score/launch_manager/src/daemon/src/configuration/component_config.hpp" -#include "score/mw/launch_manager/common/alive_interface_path.hpp" #include "score/mw/launch_manager/common/log.hpp" #include "score/mw/launch_manager/osal/ipc_comms.hpp" #include "score/mw/launch_manager/process_group_manager/details/safe_process_map.hpp" @@ -31,19 +30,28 @@ ProcessInfoNode::ProcessInfoNode(configuration::ComponentConfig&& config, Proces status_(0), config_(std::move(config)), process_handling_(std::move(process_handling)), - identifier_(IdentifierHash{{config_.name}}) + identifier_(config_.name) { - - if (config.component_properties.application_profile.application_type == - configuration::ApplicationType::ReportingAndSupervised) - { - config_.deployment_config.environmental_variables.add( - "LCM_ALIVE_INTERFACE_PATH", aliveInterfacePath(IdentifierHash{config_.name})); - } if (config_.deployment_config.ready_recovery_action.has_value()) { start_tries_ = config_.deployment_config.ready_recovery_action->number_of_attempts + 1; } + + const configuration::ApplicationProfile& app_profile = config_.component_properties.application_profile; + + if (app_profile.application_type == configuration::ApplicationType::ReportingAndSupervised) + { + SCORE_LANGUAGE_FUTURECPP_ASSERT_DBG_MESSAGE( + app_profile.alive_supervision.has_value(), "Supervised process did not have alive supervision config"); + const uid_t uid = config_.deployment_config.sandbox.uid; + + LM_LOG_DEBUG() << "Setting up alive supervision for" << identifier_; + + state_publisher_ = process_handling_.supervision_factory.constructSupervision( + identifier_, uid, app_profile.alive_supervision.value()); + config_.deployment_config.environmental_variables.add( + "LCM_ALIVE_INTERFACE_PATH", state_publisher_->getConnectionId()); + } } IComponent::RequestResult ProcessInfoNode::tryReportCompletion(score::mw::lifecycle::ProcessState new_state) @@ -90,7 +98,7 @@ IComponent::RequestResult ProcessInfoNode::tryReportSuccess() if (auto time = getTimeForReport()) { - process_handling_.state_publisher_.reportActivation(identifier_, time.value()); + state_publisher_->reportActivation(time.value()); } return {RequestState::kSuccess}; @@ -101,14 +109,14 @@ IComponent::RequestResult ProcessInfoNode::tryReportSuccess() std::optional ProcessInfoNode::getTimeForReport() const { if (config_.component_properties.application_profile.application_type == - score::mw::lifecycle::internal::configuration::ApplicationType::Native) + score::mw::lifecycle::internal::configuration::ApplicationType::ReportingAndSupervised) { - return std::nullopt; + timespec timestamp{}; + static_cast(clock_gettime(CLOCK_MONOTONIC, ×tamp)); + return timestamp; } - timespec timestamp{}; - static_cast(clock_gettime(CLOCK_MONOTONIC, ×tamp)); - return timestamp; + return std::nullopt; } IComponent::RequestResult ProcessInfoNode::tryReportError(ComponentError error) @@ -419,7 +427,7 @@ IComponent::RequestResult ProcessInfoNode::deactivate(score::cpp::stop_token sto reached_ready_.store(false); if (auto time = getTimeForReport()) { - process_handling_.state_publisher_.reportDeactivation(identifier_, time.value()); + state_publisher_->reportDeactivation(time.value()); } terminateProcess(stop_token); setState(ProcessState::kIdle); diff --git a/score/launch_manager/src/daemon/src/process_group_manager/details/process_info_node.hpp b/score/launch_manager/src/daemon/src/process_group_manager/details/process_info_node.hpp index 78c6b25b9..cec061c3e 100644 --- a/score/launch_manager/src/daemon/src/process_group_manager/details/process_info_node.hpp +++ b/score/launch_manager/src/daemon/src/process_group_manager/details/process_info_node.hpp @@ -15,13 +15,13 @@ #define _INCLUDED_PROCESSINFONODE_ #include "score/launch_manager/src/daemon/src/configuration/component_config.hpp" +#include "score/mw/launch_manager/alive_monitor/isupervision_event_publisher.hpp" #include "score/mw/launch_manager/configuration/component_config.hpp" #include "score/mw/launch_manager/control/control_client_channel.hpp" #include "score/mw/launch_manager/process_group_manager/details/icomponent.hpp" #include "score/mw/launch_manager/process_group_manager/details/process_handling.hpp" #include "score/mw/launch_manager/process_group_manager/details/safe_process_map.hpp" #include "score/mw/launch_manager/process_group_manager/process_state.hpp" -#include "score/mw/launch_manager/supervision_control_client/isupervision_event_publisher.hpp" #include #include #include @@ -43,8 +43,6 @@ class ProcessInfoNode final : public IComponent public: /// @brief Constructs a ProcessInfoNode. /// @param config Configuration for the OS process. - /// @param index The process index within its process group. - /// @param ready_condition Whether this process is considered ready when running or when terminated. /// @param process_handling The interfaces used to start, stop and report on the OS process. ProcessInfoNode(configuration::ComponentConfig&& config, ProcessHandling process_handling); @@ -60,6 +58,7 @@ class ProcessInfoNode final : public IComponent control_client_channel_(std::move(other.control_client_channel_)), sync_(std::move(other.sync_)), process_handling_(std::move(other.process_handling_)), + state_publisher_(std::move(other.state_publisher_)), identifier_(other.identifier_) { } @@ -187,6 +186,9 @@ class ProcessInfoNode final : public IComponent /// @brief The interfaces used to control a OS process. ProcessHandling process_handling_; + /// @brief Interface for reporting component state to health monitor. + std::unique_ptr state_publisher_; + /// @brief Number ot times to try run the process. std::uint8_t start_tries_{1U}; diff --git a/score/launch_manager/src/daemon/src/process_group_manager/details/process_info_node_UT.cpp b/score/launch_manager/src/daemon/src/process_group_manager/details/process_info_node_UT.cpp index c8f3c8316..1cb4b24ba 100644 --- a/score/launch_manager/src/daemon/src/process_group_manager/details/process_info_node_UT.cpp +++ b/score/launch_manager/src/daemon/src/process_group_manager/details/process_info_node_UT.cpp @@ -11,10 +11,11 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ +#include "score/mw/launch_manager/alive_monitor/mock_supervision_event_publisher.hpp" +#include "score/mw/launch_manager/alive_monitor/mock_supervision_factory.hpp" #include "score/mw/launch_manager/process_group_manager/details/process_info_node.hpp" #include "score/mw/launch_manager/process_group_manager/details/safe_process_map.hpp" #include "score/mw/launch_manager/process_group_manager/mock_iprocess.hpp" -#include "score/mw/launch_manager/supervision_control_client/mock_supervision_event_publisher.hpp" #include #include #include @@ -24,8 +25,9 @@ #include using namespace testing; -using namespace score::mw::lifecycle::internal; -using namespace score::mw::lifecycle; + +namespace score::mw::lifecycle::internal +{ // Default process name for testing constexpr std::string_view kProcessName{"test_process"}; @@ -45,8 +47,35 @@ class ProcessInfoNodeFixture : public ::testing::Test RecordProperty("TestType", "interface-test"); RecordProperty("DerivationTechnique", "equivalence-classes"); - ON_CALL(mock_publisher_, reportActivation).WillByDefault(Return(true)); - ON_CALL(mock_publisher_, reportDeactivation).WillByDefault(Return(true)); + ON_CALL(mock_factory_, constructSupervision).WillByDefault(InvokeWithoutArgs([this]() { + return constructDefaultEventPublisher(); + })); + } + + virtual std::unique_ptr> constructDefaultEventPublisher() const + { + auto mock_publisher = std::make_unique>(); + ON_CALL(*mock_publisher, reportActivation).WillByDefault(Return(true)); + ON_CALL(*mock_publisher, reportDeactivation).WillByDefault(Return(true)); + return mock_publisher; + } + + void expectActivationReport(int times = 1) + { + EXPECT_CALL(mock_factory_, constructSupervision).WillOnce(InvokeWithoutArgs([times]() { + auto mock_publisher = std::make_unique>(); + EXPECT_CALL(*mock_publisher, reportActivation).Times(times).WillRepeatedly(Return(true)); + return mock_publisher; + })); + } + + void expectDeactivationReport(int times = 1) + { + EXPECT_CALL(mock_factory_, constructSupervision).WillOnce(InvokeWithoutArgs([times]() { + auto mock_publisher = std::make_unique>(); + EXPECT_CALL(*mock_publisher, reportDeactivation).Times(times).WillRepeatedly(Return(true)); + return mock_publisher; + })); } /// @brief Helper method to create a ProcessInfoNode with the given parameters. @@ -68,8 +97,15 @@ class ProcessInfoNodeFixture : public ::testing::Test config.deployment_config.ready_recovery_action = configuration::RestartAction{restart_attempts, 0U}; config.deployment_config.shutdown_timeout_ms = shutdown_timeout_ms_; + if (application_type == configuration::ApplicationType::ReportingAndSupervised) + { + configuration::ComponentAliveSupervision alive{ + .reporting_cycle_ms = 10, .failed_cycles_tolerance = 1, .min_indications = 0, .max_indications = 0}; + config.component_properties.application_profile.alive_supervision = alive; + } + return std::make_unique( - std::move(config), ProcessHandling{mock_publisher_, &mock_processIf_, process_map_}); + std::move(config), ProcessHandling{&mock_processIf_, process_map_, mock_factory_}); } /// @brief Helper method to create a ProcessInfoNode that is self-terminating. @@ -125,7 +161,7 @@ class ProcessInfoNodeFixture : public ::testing::Test score::cpp::stop_source stop_source_{}; std::shared_ptr process_map_{std::make_shared()}; StrictMock mock_processIf_{}; - NiceMock mock_publisher_{}; + NiceMock mock_factory_{}; }; // Bundles different cases for activate() that occur during startup, before the ready condition is reached. @@ -169,10 +205,10 @@ TEST_F(ProcessInfoNodeStartupTest, CanStartReportingProcess_ReportsRunningInTime { RecordProperty("Description", "Can start a reporting process and check that the state transitions to kRunning."); - auto node = createProcessInfoNode(configuration::ApplicationType::Reporting); + expectActivationReport(); + auto node = createProcessInfoNode(configuration::ApplicationType::ReportingAndSupervised); expectSuccessfulProcessLaunch(); EXPECT_CALL(mock_processIf_, waitForkRunning(_, _)).WillOnce(Return(osal::OsalReturnType::kSuccess)); - EXPECT_CALL(mock_publisher_, reportActivation); auto result = node->activate(score::cpp::stop_token{}); @@ -271,12 +307,12 @@ TEST_F(ProcessInfoNodeStartupCrashTest, ProcesssTerminated_OnWaitForkRunningTime "Description", "If waitForkRunning times out, the process reports kActivationTimedOut and ends up in state kTerminated."); - auto node = createProcessInfoNode(configuration::ApplicationType::Reporting); + expectActivationReport(0); + auto node = createProcessInfoNode(configuration::ApplicationType::ReportingAndSupervised); expectSuccessfulProcessLaunch(); EXPECT_CALL(mock_processIf_, waitForkRunning(_, _)).WillOnce(Return(osal::OsalReturnType::kFail)); // Simulate the OS handler reporting the killed process's exit once termination is requested. expectOsAcknowledgesTermination(node.get()); - EXPECT_CALL(mock_publisher_, reportActivation).Times(0); auto result = node->activate(score::cpp::stop_token{}); @@ -292,7 +328,8 @@ TEST_F(ProcessInfoNodeStartupCrashTest, ReportingProcess_CrashesBeforeReady_NoRe "Process returns kErrorBeforeReady when crashing before reaching its ready condition (kRunning) with 0 restart " "attempts"); - auto node = createProcessInfoNode(configuration::ApplicationType::Reporting); + expectActivationReport(0); + auto node = createProcessInfoNode(configuration::ApplicationType::ReportingAndSupervised); expectSuccessfulProcessLaunch(); // Simulate the OS handler detecting the crash while the process is still waiting to reach kRunning. EXPECT_CALL(mock_processIf_, waitForkRunning(_, _)) @@ -301,7 +338,6 @@ TEST_F(ProcessInfoNodeStartupCrashTest, ReportingProcess_CrashesBeforeReady_NoRe static_cast(node->tryHandleTermination(-1)); }), Return(osal::OsalReturnType::kFail))); - EXPECT_CALL(mock_publisher_, reportActivation).Times(0); auto result = node->activate(score::cpp::stop_token{}); @@ -317,9 +353,10 @@ TEST_F(ProcessInfoNodeStartupCrashTest, ReportingProcess_CrashesBeforeReady_With "Process returns kErrorBeforeReady when crashing before reaching its ready condition (kRunning) with 3 restart " "attempts"); + expectActivationReport(0); constexpr uint32_t kRestartAttempts = 3; constexpr uint32_t kTotalAttempts = kRestartAttempts + 1; - auto node = createProcessInfoNode(configuration::ApplicationType::Reporting, kRestartAttempts); + auto node = createProcessInfoNode(configuration::ApplicationType::ReportingAndSupervised, kRestartAttempts); EXPECT_CALL(mock_processIf_, startProcess(_, _, _)) .Times(kTotalAttempts) @@ -336,7 +373,6 @@ TEST_F(ProcessInfoNodeStartupCrashTest, ReportingProcess_CrashesBeforeReady_With static_cast(node->tryHandleTermination(-1)); }), Return(osal::OsalReturnType::kFail))); - EXPECT_CALL(mock_publisher_, reportActivation).Times(0); auto result = node->activate(score::cpp::stop_token{}); @@ -404,8 +440,10 @@ TEST_F(ProcessInfoNodeStartupCrashTest, TimeoutThenSuccess_WithRestarts) "Description", "A reporting process that times out on the first attempt but reports kRunning on the retry returns kSuccess."); + expectActivationReport(); + constexpr uint32_t kRestartAttempts = 1; - auto node = createProcessInfoNode(configuration::ApplicationType::Reporting, kRestartAttempts); + auto node = createProcessInfoNode(configuration::ApplicationType::ReportingAndSupervised, kRestartAttempts); EXPECT_CALL(mock_processIf_, startProcess(_, _, _)).Times(2).WillRepeatedly(Return(osal::OsalReturnType::kSuccess)); EXPECT_CALL(*process_map_, insertIfNotTerminated(_, _)) @@ -416,7 +454,6 @@ TEST_F(ProcessInfoNodeStartupCrashTest, TimeoutThenSuccess_WithRestarts) .WillOnce(Return(osal::OsalReturnType::kSuccess)); // Simulate the OS handler reporting the killed process's exit on the first (timed-out) attempt. expectOsAcknowledgesTermination(node.get()); - EXPECT_CALL(mock_publisher_, reportActivation); auto result = node->activate(score::cpp::stop_token{}); @@ -516,9 +553,9 @@ TEST_F(ProcessInfoNodeDeactivationTest, CanTerminateNonSelfTerminatingProcess) "to kTerminated."); EXPECT_CALL(mock_processIf_, waitForkRunning(_, _)).WillOnce(Return(osal::OsalReturnType::kSuccess)); - EXPECT_CALL(mock_publisher_, reportDeactivation); + expectDeactivationReport(); - auto node = createRunningProcessInfoNode(configuration::ApplicationType::Reporting); + auto node = createRunningProcessInfoNode(configuration::ApplicationType::ReportingAndSupervised); // Simulate the OS handler reporting the process's exit once termination is requested. expectOsAcknowledgesTermination(node.get()); @@ -579,7 +616,6 @@ TEST_F(ProcessInfoNodeDeactivationTest, ProcessIgnoresSigterm_ForcedWithSigkill) "SIGKILL."); EXPECT_CALL(mock_processIf_, waitForkRunning(_, _)).WillOnce(Return(osal::OsalReturnType::kSuccess)); - EXPECT_CALL(mock_publisher_, reportDeactivation); auto node = createRunningProcessInfoNode_TermTimeout(std::chrono::milliseconds{0}); EXPECT_CALL(mock_processIf_, requestTermination(_)).WillOnce(Return(osal::OsalReturnType::kSuccess)); @@ -598,3 +634,5 @@ TEST_F(ProcessInfoNodeDeactivationTest, ProcessIgnoresSigterm_ForcedWithSigkill) ASSERT_THAT(node->active(), IsFalse()); ASSERT_THAT(node->getState(), Eq(score::mw::lifecycle::ProcessState::kIdle)); } + +} // namespace score::mw::lifecycle::internal diff --git a/score/launch_manager/src/daemon/src/process_group_manager/mock_alive_monitor_thread.hpp b/score/launch_manager/src/daemon/src/process_group_manager/mock_alive_monitor_thread.hpp deleted file mode 100644 index bc3f22309..000000000 --- a/score/launch_manager/src/daemon/src/process_group_manager/mock_alive_monitor_thread.hpp +++ /dev/null @@ -1,41 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2026 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_LCM_IALIVE_MONITOR_THREAD_MOCK_HPP_INCLUDED -#define SCORE_LCM_IALIVE_MONITOR_THREAD_MOCK_HPP_INCLUDED - -#include "score/mw/launch_manager/process_group_manager/ialive_monitor_thread.hpp" - -#include - -namespace score -{ -namespace mw::lifecycle -{ -namespace internal -{ - -/// @brief Reusable gmock mock for IAliveMonitorThread, for use by tests of components that own an alive monitor -/// thread. -class MockAliveMonitorThread : public IAliveMonitorThread -{ - public: - MOCK_METHOD(bool, start, (), (override)); - MOCK_METHOD(void, stop, (), (override)); -}; - -} // namespace internal -} // namespace mw::lifecycle -} // namespace score - -#endif // SCORE_LCM_IALIVE_MONITOR_THREAD_MOCK_HPP_INCLUDED diff --git a/score/launch_manager/src/daemon/src/process_group_manager/process_group_manager.cpp b/score/launch_manager/src/daemon/src/process_group_manager/process_group_manager.cpp index a4adbf861..89ca29a7a 100644 --- a/score/launch_manager/src/daemon/src/process_group_manager/process_group_manager.cpp +++ b/score/launch_manager/src/daemon/src/process_group_manager/process_group_manager.cpp @@ -19,7 +19,6 @@ #include "score/mw/launch_manager/common/log.hpp" #include "score/mw/launch_manager/process_group_manager/details/process_monitor.hpp" -#include "score/mw/launch_manager/process_group_manager/ialive_monitor_thread.hpp" #include "score/mw/launch_manager/process_group_manager/process_group_manager.hpp" namespace score::mw::lifecycle::internal @@ -39,17 +38,15 @@ void ProcessGroupManager::cancel() ProcessGroupManager::ProcessGroupManager( configuration::Config&& config, - std::unique_ptr alive_monitor_thread, + std::unique_ptr alive_monitor, std::shared_ptr recovery_client, - std::unique_ptr supervision_control_notifier, std::unique_ptr watchdog) : configuration_(std::move(config)), process_interface_(), process_map_(nullptr), thread_pool_(nullptr), worker_jobs_(nullptr), - supervision_control_notifier_(std::move(supervision_control_notifier)), - alive_monitor_thread_(std::move(alive_monitor_thread)), + alive_monitor_(std::move(alive_monitor)), recovery_client_(recovery_client), watchdog_(std::move(watchdog)) { @@ -97,7 +94,7 @@ bool ProcessGroupManager::initialize() } LM_LOG_DEBUG() << "Process Group initialization done"; - if (!alive_monitor_thread_->start()) + if (!alive_monitor_->startMonitoring()) { LM_LOG_ERROR() << "Alive monitor thread failed to start"; return false; @@ -132,7 +129,7 @@ void ProcessGroupManager::deinitialize() event_queue_->stop(); } os_handler_.reset(); - alive_monitor_thread_->stop(); + alive_monitor_->stopMonitoring(); // Join the worker threads before destroying the process groups: a worker may // still be (de)activating a ProcessInfoNode owned by a graph, so tearing the @@ -210,7 +207,7 @@ bool ProcessGroupManager::initializeProcessGroups() configuration_.components().size() + configuration_.runTargets().size() + 2, configuration_, worker_jobs_, - ProcessHandling{*supervision_control_notifier_.get(), &process_interface_, process_map_}, + ProcessHandling{&process_interface_, process_map_, alive_monitor_->getSupervisionFactory()}, this); LM_LOG_DEBUG() << "Process group initialized successfully"; diff --git a/score/launch_manager/src/daemon/src/process_group_manager/process_group_manager.hpp b/score/launch_manager/src/daemon/src/process_group_manager/process_group_manager.hpp index 653d000a2..eb8fd58ba 100644 --- a/score/launch_manager/src/daemon/src/process_group_manager/process_group_manager.hpp +++ b/score/launch_manager/src/daemon/src/process_group_manager/process_group_manager.hpp @@ -18,6 +18,7 @@ #include #include +#include "score/mw/launch_manager/alive_monitor/IAliveMonitor.hpp" #include "score/mw/launch_manager/common/concurrency/mpmc_concurrent_queue.hpp" #include "score/mw/launch_manager/common/concurrency/thread_pool.hpp" #include "score/mw/launch_manager/common/constants.hpp" @@ -32,10 +33,8 @@ #include "score/mw/launch_manager/process_group_manager/details/process_launcher.hpp" #include "score/mw/launch_manager/process_group_manager/details/process_monitor.hpp" #include "score/mw/launch_manager/process_group_manager/details/safe_process_map.hpp" -#include "score/mw/launch_manager/process_group_manager/ialive_monitor_thread.hpp" #include "score/mw/launch_manager/process_group_manager/iprocess.hpp" #include "score/mw/launch_manager/recovery_client/recovery_client.hpp" -#include "score/mw/launch_manager/supervision_control_client/isupervision_control_notifier.hpp" #include "score/mw/launch_manager/watchdog/IWatchdogIf.hpp" namespace score::mw::lifecycle::internal @@ -73,9 +72,8 @@ class ProcessGroupManager final : public ITransitionResultPublisher /// legacy configuration where no watchdog is wired. ProcessGroupManager( configuration::Config&& config, - std::unique_ptr alive_monitor_thread, + std::unique_ptr alive_monitor, std::shared_ptr recovery_client, - std::unique_ptr supervision_control_notifier, std::unique_ptr watchdog); /// @brief Initializes the process group manager. @@ -279,10 +277,7 @@ class ProcessGroupManager final : public ITransitionResultPublisher /// @brief Pointer to the gaph. std::shared_ptr graph_{nullptr}; - /// @brief Process state notifier object used to send data to PHM - std::unique_ptr supervision_control_notifier_; - - std::unique_ptr alive_monitor_thread_; + std::unique_ptr alive_monitor_; std::unique_ptr process_monitor_; diff --git a/score/launch_manager/src/daemon/src/supervision_control_client/BUILD b/score/launch_manager/src/daemon/src/supervision_control_client/BUILD deleted file mode 100644 index 1a5e5a213..000000000 --- a/score/launch_manager/src/daemon/src/supervision_control_client/BUILD +++ /dev/null @@ -1,122 +0,0 @@ -# ******************************************************************************* -# Copyright (c) 2026 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 -# ******************************************************************************* -load("@rules_cc//cc:defs.bzl", "cc_library") -load("//tests/utils/bazel:unit_test.bzl", "lm_cc_test") - -cc_library( - name = "supervision_event", - hdrs = ["supervision_event.hpp"], - include_prefix = "score/mw/launch_manager/supervision_control_client", - strip_include_prefix = "/score/launch_manager/src/daemon/src/supervision_control_client", - visibility = ["//score:__subpackages__"], - deps = [ - "//score/launch_manager/src/daemon/src/common:identifier_hash", - ], -) - -cc_library( - name = "isupervision_control_receiver", - hdrs = ["isupervision_control_receiver.hpp"], - include_prefix = "score/mw/launch_manager/supervision_control_client", - strip_include_prefix = "/score/launch_manager/src/daemon/src/supervision_control_client", - visibility = ["//score:__subpackages__"], - deps = [ - ":supervision_event", - "//score/launch_manager:error", - "@score_baselibs//score/result", - ], -) - -cc_library( - name = "isupervision_control_notifier", - hdrs = ["isupervision_control_notifier.hpp"], - include_prefix = "score/mw/launch_manager/supervision_control_client", - strip_include_prefix = "/score/launch_manager/src/daemon/src/supervision_control_client", - visibility = ["//score:__subpackages__"], - deps = [ - ":isupervision_control_receiver", - ":isupervision_event_publisher", - ":supervision_event", - ], -) - -cc_library( - name = "isupervision_event_publisher", - hdrs = ["isupervision_event_publisher.hpp"], - include_prefix = "score/mw/launch_manager/supervision_control_client", - strip_include_prefix = "/score/launch_manager/src/daemon/src/supervision_control_client", - visibility = ["//score:__subpackages__"], - deps = [ - "//score/launch_manager/src/daemon/src/common:identifier_hash", - ], -) - -cc_library( - name = "mock_supervision_event_publisher", - testonly = True, - hdrs = ["mock_supervision_event_publisher.hpp"], - include_prefix = "score/mw/launch_manager/supervision_control_client", - strip_include_prefix = "/score/launch_manager/src/daemon/src/supervision_control_client", - visibility = ["//score:__subpackages__"], - deps = [ - ":isupervision_event_publisher", - "@googletest//:gtest_main", - ], -) - -cc_library( - name = "mock_supervision_control_notifier", - testonly = True, - hdrs = ["mock_supervision_control_notifier.hpp"], - include_prefix = "score/mw/launch_manager/supervision_control_client", - strip_include_prefix = "/score/launch_manager/src/daemon/src/supervision_control_client", - visibility = ["//score:__subpackages__"], - deps = [ - ":isupervision_control_notifier", - "@googletest//:gtest_main", - ], -) - -cc_library( - name = "supervision_control_notifier", - srcs = ["supervision_control_notifier.cpp"], - hdrs = ["supervision_control_notifier.hpp"], - include_prefix = "score/mw/launch_manager/supervision_control_client", - strip_include_prefix = "/score/launch_manager/src/daemon/src/supervision_control_client", - visibility = ["//score:__subpackages__"], - deps = [ - ":isupervision_control_notifier", - "//externals/ipc_dropin", - "//score/launch_manager/src/daemon/src/common:log", - "//score/launch_manager/src/daemon/src/supervision_control_client/details:supervision_control_receiver", - ], -) - -cc_library( - name = "supervision_control_client", - visibility = ["//score:__subpackages__"], - deps = [ - ":supervision_control_notifier", - "//score/launch_manager/src/daemon/src/supervision_control_client/details:supervision_control_receiver", - ], -) - -lm_cc_test( - name = "supervision_control_client_ut", - srcs = ["supervision_control_client_ut.cpp"], - deps = [ - ":supervision_control_client", - "//score/launch_manager/src/daemon/src/supervision_control_client/details:supervision_control_receiver", - "@googletest//:gtest_main", - ], -) diff --git a/score/launch_manager/src/daemon/src/supervision_control_client/details/BUILD b/score/launch_manager/src/daemon/src/supervision_control_client/details/BUILD deleted file mode 100644 index 97bd6d036..000000000 --- a/score/launch_manager/src/daemon/src/supervision_control_client/details/BUILD +++ /dev/null @@ -1,27 +0,0 @@ -# ******************************************************************************* -# Copyright (c) 2026 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 -# ******************************************************************************* -load("@rules_cc//cc:defs.bzl", "cc_library") - -cc_library( - name = "supervision_control_receiver", - srcs = ["supervision_control_receiver.cpp"], - hdrs = ["supervision_control_receiver.hpp"], - include_prefix = "score/mw/launch_manager/supervision_control_client/details", - strip_include_prefix = "/score/launch_manager/src/daemon/src/supervision_control_client/details", - visibility = ["//score/launch_manager/src/daemon/src/supervision_control_client:__pkg__"], - deps = [ - "//externals/ipc_dropin", - "//score/launch_manager/src/daemon/src/common:log", - "//score/launch_manager/src/daemon/src/supervision_control_client:isupervision_control_receiver", - ], -) diff --git a/score/launch_manager/src/daemon/src/supervision_control_client/details/supervision_control_receiver.cpp b/score/launch_manager/src/daemon/src/supervision_control_client/details/supervision_control_receiver.cpp deleted file mode 100644 index a5a92b0f3..000000000 --- a/score/launch_manager/src/daemon/src/supervision_control_client/details/supervision_control_receiver.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/******************************************************************************** - * 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/launch_manager/supervision_control_client/details/supervision_control_receiver.hpp" -#include "score/mw/launch_manager/common/log.hpp" - -namespace score::mw::lifecycle -{ -SupervisionControlReceiver::SupervisionControlReceiver(BufferP ring_buffer) noexcept : ring_buffer_(ring_buffer) -{ -} - -SupervisionControlReceiver::~SupervisionControlReceiver() noexcept -{ -} - -score::Result> SupervisionControlReceiver::getNextSupervisionEvent() noexcept -{ - score::mw::lifecycle::SupervisionEvent event; - if (ring_buffer_->getOverflowFlag()) - { - LM_LOG_ERROR() << "SupervisionControlReceiver::getNextSupervisionEvent: Overflow occurred, " - "will be reported as kCommunicationError"; - return score::Result>{ - score::MakeUnexpected(score::mw::lifecycle::ExecErrc::kCommunicationError)}; - } - - if (ring_buffer_->empty()) - { - return score::Result>{std::nullopt}; - } - - auto res = ring_buffer_->tryDequeue(event); - if (res) - { - return score::Result>{event}; - } - else - { - return score::Result>{ - score::MakeUnexpected(score::mw::lifecycle::ExecErrc::kGeneralError)}; - } -} -} // namespace score::mw::lifecycle diff --git a/score/launch_manager/src/daemon/src/supervision_control_client/details/supervision_control_receiver.hpp b/score/launch_manager/src/daemon/src/supervision_control_client/details/supervision_control_receiver.hpp deleted file mode 100644 index 3ca37c4e2..000000000 --- a/score/launch_manager/src/daemon/src/supervision_control_client/details/supervision_control_receiver.hpp +++ /dev/null @@ -1,68 +0,0 @@ -/******************************************************************************** - * 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 SUPERVISION_CONTROL_RECEIVER_HPP_INCLUDED -#define SUPERVISION_CONTROL_RECEIVER_HPP_INCLUDED - -#include "ipc_dropin/ringbuffer.hpp" -#include "score/mw/launch_manager/supervision_control_client/isupervision_control_receiver.hpp" - -namespace score -{ - -namespace mw::lifecycle -{ - -/// @brief SupervisionControlReceiver implementation for receiving supervision events from the Launch Manager. -class SupervisionControlReceiver final : public ISupervisionControlReceiver -{ - public: - using BufferP = std::shared_ptr(score::mw::lifecycle::BufferConstants::BUFFER_QUEUE_SIZE), - static_cast(score::mw::lifecycle::BufferConstants::BUFFER_MAXPAYLOAD)>>; - - /// @brief Constructor that creates the SupervisionControlReceiver - /// @param ring_buffer Shared pointer to the ring buffer used to receive supervision events - SupervisionControlReceiver(BufferP ring_buffer) noexcept; - - /// @brief Copy constructor is disabled. - SupervisionControlReceiver(const SupervisionControlReceiver&) noexcept = delete; - - /// @brief Move constructor is disabled. - SupervisionControlReceiver(SupervisionControlReceiver&&) noexcept = delete; - - /// @brief Copy-assign is disabled. - SupervisionControlReceiver& operator=(const SupervisionControlReceiver& other) = delete; - - /// @brief Move-assign is disabled. - SupervisionControlReceiver& operator=(SupervisionControlReceiver&& other) = delete; - - /// @brief Destructor. - ~SupervisionControlReceiver() noexcept; - - /// @brief Returns the queued SupervisionEvent, which the alive monitor has not yet parsed. - /// @returns Returns the queued SupervisionEvent. - /// "std::nullopt" is returned in case there is no new information. - /// "score::mw::lifecycle::ExecErrc::kGeneralError" is returned in case of any other error. - score::Result> getNextSupervisionEvent() noexcept override; - - private: - /// @brief Ring buffer through which supervision events are received from the Launch Manager - BufferP ring_buffer_{}; -}; - -} // namespace mw::lifecycle - -} // namespace score - -#endif // SUPERVISION_CONTROL_RECEIVER_HPP_INCLUDED diff --git a/score/launch_manager/src/daemon/src/supervision_control_client/isupervision_control_notifier.hpp b/score/launch_manager/src/daemon/src/supervision_control_client/isupervision_control_notifier.hpp deleted file mode 100644 index 3ae726f53..000000000 --- a/score/launch_manager/src/daemon/src/supervision_control_client/isupervision_control_notifier.hpp +++ /dev/null @@ -1,44 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2026 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 ISUPERVISION_CONTROL_NOTIFIER_HPP_INCLUDED -#define ISUPERVISION_CONTROL_NOTIFIER_HPP_INCLUDED - -#include "score/mw/launch_manager/supervision_control_client/isupervision_control_receiver.hpp" -#include "score/mw/launch_manager/supervision_control_client/isupervision_event_publisher.hpp" -#include "score/mw/launch_manager/supervision_control_client/supervision_event.hpp" - -namespace score -{ - -namespace mw::lifecycle -{ - -/// @brief ISupervisionControlNotifier interface for forwarding supervision events to the alive monitor. -/// The Launch Manager uses this interface to notify the alive monitor whenever a supervised -/// process reaches running state (activation) or starts terminating (deactivation). -class ISupervisionControlNotifier : public ISupervisionEventPublisher -{ - public: - /// @brief Destructor. - virtual ~ISupervisionControlNotifier() noexcept = default; - - /// @brief Construct and return the receiver instance used to receive supervision events. - /// @return Supervision control receiver instance - virtual std::unique_ptr constructReceiver() = 0; -}; - -} // namespace mw::lifecycle - -} // namespace score - -#endif diff --git a/score/launch_manager/src/daemon/src/supervision_control_client/isupervision_control_receiver.hpp b/score/launch_manager/src/daemon/src/supervision_control_client/isupervision_control_receiver.hpp deleted file mode 100644 index c64b8e31c..000000000 --- a/score/launch_manager/src/daemon/src/supervision_control_client/isupervision_control_receiver.hpp +++ /dev/null @@ -1,46 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2026 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 ISUPERVISION_CONTROL_RECEIVER_HPP_INCLUDED -#define ISUPERVISION_CONTROL_RECEIVER_HPP_INCLUDED - -#include "score/mw/lifecycle/execution_error.h" -#include "score/result/result.h" -#include -#include - -#include "score/mw/launch_manager/supervision_control_client/supervision_event.hpp" - -namespace score -{ - -namespace mw::lifecycle -{ - -/// @brief ISupervisionControlReceiver interface for receiving supervision events. -/// The alive monitor uses this interface to receive supervision events (activation/deactivation) -/// forwarded by the Launch Manager. -class ISupervisionControlReceiver -{ - public: - virtual ~ISupervisionControlReceiver() noexcept = default; - - /// @brief Returns a queued SupervisionEvent that has not yet been parsed. - /// @returns Result containing SupervisionEvent in case of success, or ExecError in case of failure. - virtual score::Result> getNextSupervisionEvent() noexcept = 0; -}; - -} // namespace mw::lifecycle - -} // namespace score - -#endif diff --git a/score/launch_manager/src/daemon/src/supervision_control_client/mock_iprocess_state_notifier.hpp b/score/launch_manager/src/daemon/src/supervision_control_client/mock_iprocess_state_notifier.hpp deleted file mode 100644 index f37203578..000000000 --- a/score/launch_manager/src/daemon/src/supervision_control_client/mock_iprocess_state_notifier.hpp +++ /dev/null @@ -1,44 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2026 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 IPROCESSSTATE_NOTIFIER_MOCK_HPP_INCLUDED -#define IPROCESSSTATE_NOTIFIER_MOCK_HPP_INCLUDED - -#include "score/mw/launch_manager/process_state_client/iprocess_state_notifier.hpp" - -#include - -#include - -namespace score -{ -namespace mw::lifecycle -{ - -/// @brief Reusable gmock mock for IProcessStateNotifier, for use by tests of components that notify PHM of process -/// state changes. -class MockProcessStateNotifier : public IProcessStateNotifier -{ - public: - MOCK_METHOD(std::unique_ptr, constructReceiver, (), (override)); - MOCK_METHOD( - bool, - queuePosixProcess, - (const score::mw::lifecycle::PosixProcess& f_posixProcess), - (noexcept, override)); -}; - -} // namespace mw::lifecycle -} // namespace score - -#endif // IPROCESSSTATE_NOTIFIER_MOCK_HPP_INCLUDED diff --git a/score/launch_manager/src/daemon/src/supervision_control_client/supervision_control_client_ut.cpp b/score/launch_manager/src/daemon/src/supervision_control_client/supervision_control_client_ut.cpp deleted file mode 100644 index 47b8970b1..000000000 --- a/score/launch_manager/src/daemon/src/supervision_control_client/supervision_control_client_ut.cpp +++ /dev/null @@ -1,141 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2026 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/launch_manager/supervision_control_client/details/supervision_control_receiver.hpp" -#include "score/mw/launch_manager/supervision_control_client/supervision_control_notifier.hpp" -#include -#include - -using namespace testing; -using namespace score::mw::lifecycle; - -using score::mw::lifecycle::SupervisionControlReceiver; -using score::mw::lifecycle::internal::SupervisionControlNotifier; - -class SupervisionControlClient_UT : public ::testing::Test -{ - protected: - void SetUp() override - { - RecordProperty("TestType", "interface-test"); - RecordProperty("DerivationTechnique", "explorative-testing "); - notifier_ = std::make_unique(); - receiver_ = notifier_->constructReceiver(); - } - void TearDown() override - { - receiver_.reset(); - notifier_.reset(); - } - std::unique_ptr notifier_; - std::unique_ptr receiver_; -}; - -TEST_F(SupervisionControlClient_UT, SupervisionControlClient_ConstructReceiver_Succeeds) -{ - RecordProperty( - "Description", - "This test verifies that the SupervisionControlNotifier can successfully construct a " - "SupervisionControlReceiver instance."); - ASSERT_NE(notifier_, nullptr); - ASSERT_NE(receiver_, nullptr); -} - -TEST_F(SupervisionControlClient_UT, SupervisionControlClient_QueueOneEvent_Succeeds) -{ - RecordProperty( - "Description", - "This test verifies that a single SupervisionEvent can be successfully queued using the " - "SupervisionControlNotifier and retrieved using the SupervisionControlReceiver."); - SupervisionEvent event1{ - .id = score::mw::lifecycle::IdentifierHash("Process1"), - .eventType = score::mw::lifecycle::SupervisionEventType::kActivation, - .systemClockTimestamp = {}}; - - clock_gettime(CLOCK_MONOTONIC, &event1.systemClockTimestamp); - - bool queued = notifier_->reportActivation(event1.id, event1.systemClockTimestamp); - ASSERT_TRUE(queued); - - auto result = receiver_->getNextSupervisionEvent(); - ASSERT_TRUE(result.has_value()); - ASSERT_TRUE(result->has_value()); - EXPECT_EQ(result->value().id, event1.id); - EXPECT_EQ(result->value().eventType, event1.eventType); - EXPECT_EQ(result->value().systemClockTimestamp.tv_nsec, event1.systemClockTimestamp.tv_nsec); - - auto no_more = receiver_->getNextSupervisionEvent(); - ASSERT_TRUE(no_more.has_value()); - ASSERT_FALSE(no_more->has_value()); -} - -TEST_F(SupervisionControlClient_UT, SupervisionControlClient_QueueMaxNumberOfEvents_Succeeds) -{ - RecordProperty( - "Description", - "This test verifies that the SupervisionControlNotifier can successfully queue the maximum number of " - "SupervisionEvent " - "instances defined by the buffer size, and that they can be retrieved using the SupervisionControlReceiver."); - for (size_t i = 0; i < static_cast(BufferConstants::BUFFER_QUEUE_SIZE); ++i) - { - SupervisionEvent event{ - .id = score::mw::lifecycle::IdentifierHash("Process" + std::to_string(i)), - .eventType = score::mw::lifecycle::SupervisionEventType::kActivation, - .systemClockTimestamp = {}}; - bool queued = notifier_->reportActivation(event.id, event.systemClockTimestamp); - ASSERT_TRUE(queued) << "Failed to queue event at index " << i; - } - - for (size_t i = 0; i < static_cast(BufferConstants::BUFFER_QUEUE_SIZE); ++i) - { - auto result = receiver_->getNextSupervisionEvent(); - ASSERT_TRUE(result.has_value()); - ASSERT_TRUE(result->has_value()); - EXPECT_EQ(result->value().id, score::mw::lifecycle::IdentifierHash("Process" + std::to_string(i))); - } - - auto no_more = receiver_->getNextSupervisionEvent(); - ASSERT_TRUE(no_more.has_value()); - ASSERT_FALSE(no_more->has_value()); -} - -TEST_F(SupervisionControlClient_UT, SupervisionControlClient_QueueOneEventTooMany_Fails) -{ - RecordProperty( - "Description", - "This test verifies that attempting to queue a SupervisionEvent when the buffer is already at maximum capacity " - "results in a failure, and that no additional events can be retrieved from the receiver."); - SupervisionEvent event1{ - .id = score::mw::lifecycle::IdentifierHash("Process1"), - .eventType = score::mw::lifecycle::SupervisionEventType::kActivation, - .systemClockTimestamp = {}}; - - for (size_t i = 0; i < static_cast(BufferConstants::BUFFER_QUEUE_SIZE); ++i) - { - SupervisionEvent event{ - .id = score::mw::lifecycle::IdentifierHash("Process" + std::to_string(i)), - .eventType = score::mw::lifecycle::SupervisionEventType::kActivation, - .systemClockTimestamp = {}}; - bool queued = notifier_->reportActivation(event.id, event.systemClockTimestamp); - ASSERT_TRUE(queued) << "Failed to queue event at index " << i; - } - - bool queued = notifier_->reportActivation(event1.id, event1.systemClockTimestamp); - ASSERT_FALSE(queued) << "Expected queuing to fail due to full buffer"; - - auto result = receiver_->getNextSupervisionEvent(); - ASSERT_FALSE(result.has_value()) << "Expected no events to be retrievable"; - - EXPECT_EQ( - static_cast(*result.error()), - score::mw::lifecycle::ExecErrc::kCommunicationError); -} diff --git a/score/launch_manager/src/daemon/src/supervision_control_client/supervision_control_notifier.cpp b/score/launch_manager/src/daemon/src/supervision_control_client/supervision_control_notifier.cpp deleted file mode 100644 index 6a06b05ae..000000000 --- a/score/launch_manager/src/daemon/src/supervision_control_client/supervision_control_notifier.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/******************************************************************************** - * 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/launch_manager/supervision_control_client/supervision_control_notifier.hpp" -#include "score/mw/launch_manager/common/log.hpp" -#include "score/mw/launch_manager/supervision_control_client/details/supervision_control_receiver.hpp" - -namespace score::mw::lifecycle::internal -{ - -SupervisionControlNotifier::SupervisionControlNotifier() noexcept -{ - ring_buffer_ = std::make_shared(score::mw::lifecycle::BufferConstants::BUFFER_QUEUE_SIZE), - static_cast(score::mw::lifecycle::BufferConstants::BUFFER_MAXPAYLOAD)>>(); - - ring_buffer_->initialize(); -} - -SupervisionControlNotifier::~SupervisionControlNotifier() noexcept -{ -} - -bool SupervisionControlNotifier::reportActivation(IdentifierHash id, timespec time) noexcept -{ - return queueSupervisionEvent({id, SupervisionEventType::kActivation, time}); -} - -bool SupervisionControlNotifier::reportDeactivation(IdentifierHash id, timespec time) noexcept -{ - return queueSupervisionEvent({id, SupervisionEventType::kDeactivation, time}); -} - -bool SupervisionControlNotifier::queueSupervisionEvent(const score::mw::lifecycle::SupervisionEvent& f_event) noexcept -{ - bool ret = true; - if (ring_buffer_->tryEnqueue(f_event)) - { - // nothing - } - else - { - LM_LOG_ERROR() << "Failed to queue supervision event"; - ret = false; - } - return ret; -} - -std::unique_ptr SupervisionControlNotifier::constructReceiver() -{ - return std::make_unique(ring_buffer_); -} - -} // namespace score::mw::lifecycle::internal diff --git a/score/launch_manager/src/daemon/src/supervision_control_client/supervision_control_notifier.hpp b/score/launch_manager/src/daemon/src/supervision_control_client/supervision_control_notifier.hpp deleted file mode 100644 index 7b6aeb3e3..000000000 --- a/score/launch_manager/src/daemon/src/supervision_control_client/supervision_control_notifier.hpp +++ /dev/null @@ -1,82 +0,0 @@ -/******************************************************************************** - * 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 SUPERVISION_CONTROL_NOTIFIER_HPP_INCLUDED -#define SUPERVISION_CONTROL_NOTIFIER_HPP_INCLUDED - -#include "ipc_dropin/ringbuffer.hpp" -#include "score/mw/launch_manager/supervision_control_client/isupervision_control_notifier.hpp" -#include "score/mw/launch_manager/supervision_control_client/supervision_event.hpp" - -namespace score -{ - -namespace mw::lifecycle -{ - -namespace internal -{ - -/// @brief SupervisionControlNotifier implementation for forwarding supervision events to the alive monitor. -/// The Launch Manager creates an instance of this class to queue supervision events -/// (activation/deactivation) for the alive monitor to consume via the receiver. -class SupervisionControlNotifier final : public ISupervisionControlNotifier -{ - public: - /// @brief Constructor that creates the SupervisionControlNotifier. - SupervisionControlNotifier() noexcept; - - /// @brief Copy constructor is disabled. - SupervisionControlNotifier(const SupervisionControlNotifier&) noexcept = delete; - - /// @brief Move constructor is disabled. - SupervisionControlNotifier(SupervisionControlNotifier&&) noexcept = delete; - - /// @brief Copy-assign is disabled. - SupervisionControlNotifier& operator=(const SupervisionControlNotifier& other) = delete; - - /// @brief Move-assign is disabled. - SupervisionControlNotifier& operator=(SupervisionControlNotifier&& other) = delete; - - /// @brief Destructor. - ~SupervisionControlNotifier() noexcept; - - /// @brief Construct and return the receiver instance used to receive supervision events. - /// @return Supervision control receiver instance - std::unique_ptr constructReceiver() override; - - /// @brief Report that process with @param id has reached the active state at @param time - bool reportActivation(IdentifierHash id, timespec time) noexcept override; - - /// @brief Report that process with @param id has changed from the active state at @param time - bool reportDeactivation(IdentifierHash id, timespec time) noexcept override; - - private: - /// @brief Writes via IPC the latest supervision event, so that the alive monitor can be informed about it. - /// @param[in] f_event The SupervisionEvent to be queued - /// @returns True on success, false for failure - bool queueSupervisionEvent(const score::mw::lifecycle::SupervisionEvent& f_event) noexcept; - - /// @brief Ring buffer through which supervision events are forwarded to the alive monitor - std::shared_ptr(score::mw::lifecycle::BufferConstants::BUFFER_QUEUE_SIZE), - static_cast(score::mw::lifecycle::BufferConstants::BUFFER_MAXPAYLOAD)>> - ring_buffer_{}; -}; - -} // namespace internal - -} // namespace mw::lifecycle - -} // namespace score -#endif