diff --git a/score/launch_manager/src/daemon/BUILD b/score/launch_manager/src/daemon/BUILD index 34705a5949..f0f06f2848 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 8bea5e7268..2ceef92c73 100644 --- a/score/launch_manager/src/daemon/src/alive_monitor/BUILD +++ b/score/launch_manager/src/daemon/src/alive_monitor/BUILD @@ -19,3 +19,27 @@ 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/supervision_control_client: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", + ], +) 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 59% 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 ebc428abba..43579897ed 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/supervision_control_client/isupervision_factory.hpp" namespace score::mw::lifecycle::internal::saf::daemon { @@ -26,13 +24,21 @@ 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 + /// @warning Not valid if @c init() failed + virtual void start() = 0; + + /// @brief Stop the monitor thread + /// @warning Not valid if @c init() failed + virtual void stop() = 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 + /// @warning Not valid if @c init() failed + [[nodiscard]] virtual ISupervisionFactory& getSupervisionFactory() const = 0; + + /// @brief Initialize the AliveMonitor functionality + /// @return True if initialization was successful, false otherwise. + [[nodiscard]] virtual bool init() noexcept = 0; }; } // namespace score::mw::lifecycle::internal::saf::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 2a229864bf..99b2cc2825 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 0000000000..d93b50fc03 --- /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 9584790546..b8101db5ff 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,40 +11,38 @@ * 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) + AliveSupervisionConfig config, + const std::size_t supervised_components) + : m_recovery_client(recovery_client), config_(config), supervised_components_(supervised_components) { } -EInitCode AliveMonitorImpl::init() noexcept +bool AliveMonitorImpl::init() noexcept { - EInitCode initResult{EInitCode::kGeneralError}; try { m_osClock.startMeasurement(); - m_daemon = std::make_unique(m_osClock, std::move(m_observable_event_receiver)); - initResult = m_daemon->init(m_recovery_client, m_config); + m_daemon = std::make_unique(m_osClock, supervised_components_); + EInitCode initResult = m_daemon->init(m_recovery_client, config_); if (initResult == EInitCode::kNoError) { const long ms{m_osClock.endMeasurement()}; LM_LOG_DEBUG() << "AliveMonitor: Initialization took " << ms << " ms"; + return true; } else { @@ -54,22 +52,41 @@ EInitCode AliveMonitorImpl::init() noexcept catch (const std::exception& e) { std::cerr << "AliveMonitor: Initialization failed due to standard exception: " << e.what() << ".\n"; - initResult = EInitCode::kGeneralError; } catch (...) { std::cerr << "AliveMonitor: Initialization failed due to exception!\n"; - initResult = EInitCode::kGeneralError; } - return initResult; + return false; +} + +void AliveMonitorImpl::start() noexcept +{ + alive_monitor_thread_ = std::thread([this]() { + threadFn(stop_thread_); + }); +} + +void AliveMonitorImpl::stop() noexcept +{ + stop_thread_.store(true); + if (alive_monitor_thread_.joinable()) + { + alive_monitor_thread_.join(); + } } -bool AliveMonitorImpl::run(std::atomic_bool& cancel_thread) noexcept +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 f73887303c..4c7c442089 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::mw::lifecycle @@ -28,30 +30,54 @@ namespace internal::saf::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 OsClock = internal::saf::timers::OsClockInterface; +using configuration::AliveSupervisionConfig; class AliveMonitorImpl : public IAliveMonitor { + static_assert( + std::is_trivially_copyable_v, + "AliveSupervisionConfig is copied to this object since it is trivially copyable. If this changes, it should be " + "passed by move instead"); + public: AliveMonitorImpl( SptrIRecoveryClient recovery_client, - UptrISupervisionControlReceiver observable_event_receiver, - const Config& config); + AliveSupervisionConfig config, + const std::size_t supervised_components); + + /// @brief @see IAliveMonitor definition + void start() noexcept override; - EInitCode init() noexcept override; + /// @brief @see IAliveMonitor definition + void stop() noexcept override; - bool run(std::atomic_bool& cancel_thread) noexcept override; + /// @brief @see IAliveMonitor definition + [[nodiscard]] ISupervisionFactory& getSupervisionFactory() const noexcept override; + + /// @brief @see IAliveMonitor definition + [[nodiscard]] bool init() noexcept override; private: + /// @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; + + /// @brief Client to send recovery requests to. SptrIRecoveryClient m_recovery_client{nullptr}; + /// @brief Daemon responsible for alive supervisions. UptrPhmDaemon m_daemon{nullptr}; + /// @brief Interface used to retrieve time. OsClock m_osClock{}; - UptrISupervisionControlReceiver m_observable_event_receiver; - const Config& m_config; + /// @brief Parameters for alive supervision. + AliveSupervisionConfig config_; + /// @brief Thread in which the alive monitor shall run. + std::thread alive_monitor_thread_{}; + /// @brief If true, exit the alive monitor thread's loop. + std::atomic_bool stop_thread_{false}; + /// @brief The number of components that require alive supervision. + std::size_t supervised_components_; }; } // namespace internal::saf::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 4cc7d1804e..c29b9df3ac 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,7 @@ cc_library( deps = [ ":phm_daemon_config", ":sw_cluster_handler", + "//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", @@ -59,18 +60,10 @@ cc_library( "//score/launch_manager/src/daemon/src/alive_monitor/details/timers:cycle_timer", "//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:isupervision_factory", ], ) -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 f9d2862ba3..28139c54c8 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 @@ -23,17 +23,15 @@ namespace score::mw::lifecycle::internal::saf::daemon { -/* RULECHECKER_comment(0, 6, check_expensive_to_copy_in_parameter, "Move only types cannot be passed by const ref", - 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)} + supervisionStateReader_{buffer_} { - static_cast(f_osClock); + buffer_->initialize(); + supervisionManager.reserve(supervised_components); } void PhmDaemon::performCyclicTriggers(void) @@ -46,7 +44,7 @@ void PhmDaemon::performCyclicTriggers(void) syncTimestamp = UINT64_MAX; } - if (processStateReader.distributeChanges(syncTimestamp)) + if (supervisionStateReader_.distributeChanges(syncTimestamp)) { supervisionManager.performCyclicTriggers(syncTimestamp); } @@ -57,38 +55,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 c16191d33f..9b022bf58d 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,6 +19,7 @@ #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" @@ -26,28 +27,18 @@ #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/configuration/config.hpp" +#include "score/mw/launch_manager/supervision_control_client/isupervision_factory.hpp" 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 -}; - /// @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; @@ -55,20 +46,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; @@ -84,17 +71,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); @@ -187,13 +171,22 @@ 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"); + if (supervisionManager.constructWorker(id, config, uid, recoveryClient, supervisionStateReader_)) + { + return std::make_unique(id, buffer_); + } + return {}; + } + 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); @@ -204,6 +197,9 @@ class PhmDaemon /// @brief For fixed time-step execution during the cyclic execution CycleTimer cycleTimer; + /// @brief Buffer that supervision events are pushed to and read from + std::shared_ptr buffer_; + /// @brief Recovery interface to Launch Manager std::shared_ptr recoveryClient; @@ -211,7 +207,7 @@ class PhmDaemon SupervisionManager supervisionManager; /// @brief Observable Event Reader for PHM daemon - ObservableEventReader processStateReader; + ObservableEventReader supervisionStateReader_; }; } // namespace score::mw::lifecycle::internal::saf::daemon 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 f84851cf9c..ececd3d353 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() const +{ + 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 a01967aaf3..cf8f990639 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 @@ -86,6 +86,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 + [[nodiscard]] bool full() const; + /// @brief Construct required worker objects for provided component /// @details Construct the interfaces, checkpoints, supervisions and recovery notifications /// @param [in] id Identifier of the component @@ -94,7 +97,7 @@ class SupervisionManager /// @param [in] f_recoveryClient_r Interface to the launch manager for recovery /// @param [in] f_processStateReader_r Process state reader object for PHM daemon /// @return Construction is successful (true), otherwise failure (false) - bool constructWorker( + [[nodiscard]] bool constructWorker( const IdentifierHash& id, const ComponentAliveSupervision& component_config, const uid_t uid, @@ -137,6 +140,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 6417eaddac..73fd72d0ec 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 f8caa9f515..59e4e55e59 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 e8cdc0c307..2999e2fef4 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 @@ -22,7 +22,7 @@ cc_library( deps = [ "//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", + "//score/launch_manager/src/daemon/src/supervision_control_client:supervision_event", ], ) @@ -35,9 +35,10 @@ cc_library( visibility = ["//score/launch_manager/src/daemon/src/alive_monitor:__subpackages__"], deps = [ ":observable_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", + "//score/launch_manager/src/daemon/src/supervision_control_client:supervision_event", ], ) 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 448bbe4b27..52734a7d2f 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 87fbfbb9d5..6b47e339dc 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 @@ -18,9 +18,10 @@ #include "score/mw/launch_manager/alive_monitor/details/ifexm/ObservableEvent.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::mw::lifecycle::internal::saf::ifexm { @@ -30,12 +31,9 @@ namespace score::mw::lifecycle::internal::saf::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; @@ -70,10 +68,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/process_group_manager/mock_alive_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/mock_alive_monitor_thread.hpp rename to score/launch_manager/src/daemon/src/alive_monitor/mock_alive_monitor.hpp index 6392fec365..a405d0e323 100644 --- a/score/launch_manager/src/daemon/src/process_group_manager/mock_alive_monitor_thread.hpp +++ b/score/launch_manager/src/daemon/src/alive_monitor/mock_alive_monitor.hpp @@ -10,26 +10,24 @@ * * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ +#ifndef MOCK_ALIVE_MONITOR_HPP_INCLUDED +#define MOCK_ALIVE_MONITOR_HPP_INCLUDED -#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 "score/mw/launch_manager/alive_monitor/IAliveMonitor.hpp" #include -namespace score::mw::lifecycle::internal +namespace score::mw::lifecycle::internal::saf::daemon { -/// @brief Reusable gmock mock for IAliveMonitorThread, for use by tests of components that own an alive monitor -/// thread. -class MockAliveMonitorThread : public IAliveMonitorThread +class MockAliveMonitor : public IAliveMonitor { public: - MOCK_METHOD(bool, start, (), (override)); + MOCK_METHOD(void, start, (), (override)); MOCK_METHOD(void, stop, (), (override)); + MOCK_METHOD(ISupervisionFactory&, getSupervisionFactory, (), (const, override)); + MOCK_METHOD(bool, init, (), (noexcept, override)); }; -} // namespace score::mw::lifecycle::internal +} // namespace score::mw::lifecycle::internal::saf::daemon -#endif // SCORE_LCM_IALIVE_MONITOR_THREAD_MOCK_HPP_INCLUDED +#endif diff --git a/score/launch_manager/src/daemon/src/main.cpp b/score/launch_manager/src/daemon/src/main.cpp index 9ffa87a35d..c9323205c2 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,30 @@ 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()); + recoveryClient, config_result.value().takeAliveSupervision(), supervised_components)}; - std::unique_ptr aliveMonitorThread{ - std::make_unique(std::move(healthMonitor))}; + GraphConfig graph_config = { + config_result.value().takeComponents(), + config_result.value().takeRunTargets(), + config_result.value().takeFallbackRunTarget(), + config_result.value().takeInitialRunTarget(), + }; auto watchdog = watchdog::createWatchdog(); auto process_group_manager = std::make_unique( - std::move(config_result).value(), - std::move(aliveMonitorThread), + std::move(graph_config), + std::move(healthMonitor), recoveryClient, - std::move(supervision_control_notifier), - std::move(watchdog)); + std::move(watchdog), + config_result.value().takeWatchdog()); 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 fba3d4a79b..b88ce6d7ec 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", @@ -97,7 +63,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 e2c4c56dd1..0000000000 --- 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 2fcddf7962..0000000000 --- a/score/launch_manager/src/daemon/src/process_group_manager/alive_monitor_thread.hpp +++ /dev/null @@ -1,52 +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::mw::lifecycle::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 score::mw::lifecycle::internal -#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 5161c140ef..749bc1bf47 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 @@ -146,7 +146,7 @@ cc_library( ":safe_process_map", "//score/launch_manager/src/daemon/src/osal:ifile_waiter", "//score/launch_manager/src/daemon/src/process_group_manager:iprocess", - "//score/launch_manager/src/daemon/src/supervision_control_client:isupervision_event_publisher", + "//score/launch_manager/src/daemon/src/supervision_control_client:isupervision_factory", ], ) @@ -169,7 +169,7 @@ cc_library( "//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/launch_manager/src/daemon/src/supervision_control_client:iactivation_state_reporter", "@score_baselibs//score/language/futurecpp", ], ) @@ -182,7 +182,8 @@ lm_cc_test( ":safe_process_map", "//score/launch_manager/src/daemon/src/osal:mock_ifile_waiter", "//score/launch_manager/src/daemon/src/process_group_manager:mock_iprocess", - "//score/launch_manager/src/daemon/src/supervision_control_client:mock_supervision_event_publisher", + "//score/launch_manager/src/daemon/src/supervision_control_client:mock_activation_state_reporter", + "//score/launch_manager/src/daemon/src/supervision_control_client:mock_supervision_factory", "@googletest//:gtest_main", ], ) @@ -234,7 +235,8 @@ lm_cc_test( ":graph", "//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", + "//score/launch_manager/src/daemon/src/supervision_control_client:mock_activation_state_reporter", + "//score/launch_manager/src/daemon/src/supervision_control_client:mock_supervision_factory", "@googletest//:gtest_main", ], ) @@ -352,12 +354,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/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/supervision_control_client:mock_activation_state_reporter", + "//score/launch_manager/src/daemon/src/supervision_control_client:mock_supervision_factory", "//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.cpp b/score/launch_manager/src/daemon/src/process_group_manager/details/graph.cpp index 966a5f5dc2..f52e492f2c 100644 --- a/score/launch_manager/src/daemon/src/process_group_manager/details/graph.cpp +++ b/score/launch_manager/src/daemon/src/process_group_manager/details/graph.cpp @@ -40,20 +40,17 @@ namespace /// @return A populated dependency graph with all components and run targets. void CreateDependencyGraph( DependencyGraph& graph, - configuration::Config& config, + GraphConfig& config, ProcessHandling process_handling, std::chrono::milliseconds& off_state_transition_timeout) { - std::vector run_targets = config.takeRunTargets(); - std::vector components = config.takeComponents(); - // dependencies can only be wired up once every node exists, so collect // them while creating the nodes std::vector>> pending_dependencies; pending_dependencies.reserve(graph.capacity()); // add all comps - for (auto& component_config : components) + for (auto& component_config : config.components_) { const auto name = component_config.name; auto depends_on = std::move(component_config.component_properties.depends_on); @@ -67,7 +64,7 @@ void CreateDependencyGraph( // add all rts bool off_rt_defined = false; - for (auto& run_target : run_targets) + for (auto& run_target : config.run_targets_) { const auto index = graph.try_emplace( IdentifierHash{run_target.name}, std::in_place_type, IdentifierHash{run_target.name}); @@ -96,7 +93,7 @@ void CreateDependencyGraph( IdentifierHash{Graph::recovery_state_name}, std::in_place_type, IdentifierHash{Graph::recovery_state_name}); - pending_dependencies.emplace_back(fallback_index, config.fallbackRunTarget().depends_on); + pending_dependencies.emplace_back(fallback_index, config.fallback_run_target_.depends_on); // wire up deps for (const auto& [node_identifier, dependencies] : pending_dependencies) @@ -116,7 +113,7 @@ void CreateDependencyGraph( Graph::Graph( uint32_t max_num_nodes, - configuration::Config& configuration, + GraphConfig& configuration, std::shared_ptr job_queue, ProcessHandling process_handling, ITransitionResultPublisher* transition_result_receiver) 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 3ddce53280..27454d9057 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::mw::lifecycle::internal @@ -48,6 +47,19 @@ namespace score::mw::lifecycle::internal using WorkerQueue = MPMCConcurrentQueue, static_cast(ProcessLimits::kMaxProcesses)>; +/// @brief Config members needed to build the graph +struct GraphConfig +{ + /// @brief Components that run targets may depend on + std::vector components_; + /// @brief Run targets that can be activated + std::vector run_targets_; + /// @brief Information about the run target transitioned to in the event of an error + configuration::FallbackRunTargetConfig fallback_run_target_; + /// @brief Name of the first run target to launch + std::string initial_run_target_; +}; + /// @brief GraphState - the graph/process group state. /// @details Enumeration representing the state of the graph. /// @note The allowed/disallowed states are managed by @@ -149,10 +161,13 @@ 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, + GraphConfig& configuration, std::shared_ptr job_queue, ProcessHandling process_handling, ITransitionResultPublisher* transition_result_receiver); @@ -344,7 +359,7 @@ class Graph final mutable std::mutex requested_state_mutex_{}; /// @brief Config pointer to set up graph nodes - configuration::Config& configuration_; + GraphConfig& configuration_; /// @brief Queue to push component tasks to std::shared_ptr job_queue_; 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 43602b7407..11b49e81cf 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 @@ -23,7 +23,8 @@ #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" +#include "score/mw/launch_manager/supervision_control_client/mock_activation_state_reporter.hpp" +#include "score/mw/launch_manager/supervision_control_client/mock_supervision_factory.hpp" namespace score::mw::lifecycle::internal { @@ -52,8 +53,8 @@ class GraphTest : public ::testing::Test RecordProperty("TestType", "interface-test"); RecordProperty("DerivationTechnique", "equivalence-classes"); - ON_CALL(mock_supervision_event_publisher_, reportActivation).WillByDefault(Return(true)); - ON_CALL(mock_supervision_event_publisher_, reportDeactivation).WillByDefault(Return(true)); + ON_CALL(mock_activation_state_reporter_, reportActivation).WillByDefault(Return(true)); + ON_CALL(mock_activation_state_reporter_, reportDeactivation).WillByDefault(Return(true)); SetConfig(); @@ -61,9 +62,9 @@ class GraphTest : public ::testing::Test // (fixture-specific) config is in place. graph_ = std::make_unique( 10U, - config_.value(), + graph_config_, job_queue_, - ProcessHandling{mock_supervision_event_publisher_, &process_interface_, mock_process_map}, + ProcessHandling{&process_interface_, mock_process_map, nullptr, mock_factory_}, &mock_transition_result_publisher_); } @@ -72,12 +73,18 @@ class GraphTest : public ::testing::Test auto procs = generateProcessComponents(1); auto rts = generateRunTargets(1); rts[1].depends_on = {procs[0].name}; - config_ = ConfigBuilder{} - .setComponents(std::move(procs)) - .setRunTargets(std::move(rts)) - .setInitialRunTarget("Startup") - .setFallbackRunTarget(std::move(fallback)) - .build(); + auto config = ConfigBuilder{} + .setComponents(std::move(procs)) + .setRunTargets(std::move(rts)) + .setInitialRunTarget("Startup") + .setFallbackRunTarget(std::move(fallback)) + .build(); + + graph_config_ = GraphConfig{ + config.takeComponents(), + config.takeRunTargets(), + config.takeFallbackRunTarget(), + config.takeInitialRunTarget()}; } std::vector generateProcessComponents(int count) @@ -179,12 +186,13 @@ class GraphTest : public ::testing::Test ASSERT_EQ(graph_->getProcessGroupState(), target); } - std::optional config_{}; + GraphConfig graph_config_{}; std::shared_ptr job_queue_ = std::make_shared(); StrictMock process_interface_{}; std::shared_ptr mock_process_map = std::make_shared(); - NiceMock mock_supervision_event_publisher_{}; + NiceMock mock_activation_state_reporter_{}; MockTransitionResultPublisher mock_transition_result_publisher_{}; + MockSupervisionFactory mock_factory_{}; std::unique_ptr graph_{}; static constexpr std::string_view pg_string{"MainPG"}; @@ -208,12 +216,18 @@ class GraphOrdinaryTransitionTest : public GraphTest auto rts = generateRunTargets(2); rts[1].depends_on = {procs[0].name}; rts[2].depends_on = {procs[1].name}; - config_ = ConfigBuilder{} - .setComponents(std::move(procs)) - .setRunTargets(std::move(rts)) - .setInitialRunTarget("Startup") - .setFallbackRunTarget(std::move(fallback)) - .build(); + auto config = ConfigBuilder{} + .setComponents(std::move(procs)) + .setRunTargets(std::move(rts)) + .setInitialRunTarget("Startup") + .setFallbackRunTarget(std::move(fallback)) + .build(); + + graph_config_ = GraphConfig{ + config.takeComponents(), + config.takeRunTargets(), + config.takeFallbackRunTarget(), + config.takeInitialRunTarget()}; } }; @@ -395,12 +409,18 @@ class GraphImplicitOffTargetTest : public GraphTest rts.push_back(startup); rts.push_back(std::move(rt)); - config_ = ConfigBuilder{} - .setComponents(std::move(procs)) - .setRunTargets(std::move(rts)) - .setInitialRunTarget("Startup") - .setFallbackRunTarget(std::move(fallback)) - .build(); + auto config = ConfigBuilder{} + .setComponents(std::move(procs)) + .setRunTargets(std::move(rts)) + .setInitialRunTarget("Startup") + .setFallbackRunTarget(std::move(fallback)) + .build(); + + graph_config_ = GraphConfig{ + config.takeComponents(), + config.takeRunTargets(), + config.takeFallbackRunTarget(), + config.takeInitialRunTarget()}; } }; @@ -449,12 +469,18 @@ class GraphOffStateTimeoutTest : public GraphTest // The Off run target must be the last entry generateRunTargets() appends. ASSERT_EQ(rts.back().name, "Off"); rts.back().transition_timeout_ms = kOffTimeoutMs; - config_ = ConfigBuilder{} - .setComponents(std::move(procs)) - .setRunTargets(std::move(rts)) - .setInitialRunTarget("Startup") - .setFallbackRunTarget(std::move(fallback)) - .build(); + auto config = ConfigBuilder{} + .setComponents(std::move(procs)) + .setRunTargets(std::move(rts)) + .setInitialRunTarget("Startup") + .setFallbackRunTarget(std::move(fallback)) + .build(); + + graph_config_ = GraphConfig{ + config.takeComponents(), + config.takeRunTargets(), + config.takeFallbackRunTarget(), + config.takeInitialRunTarget()}; } static constexpr std::uint32_t kOffTimeoutMs = 1234; @@ -476,12 +502,18 @@ class GraphHandleComponentEventTest : public GraphTest auto procs = generateProcessComponents(2); auto rts = generateRunTargets(1); rts[1].depends_on = {procs[0].name, procs[1].name}; - config_ = ConfigBuilder{} - .setComponents(std::move(procs)) - .setRunTargets(std::move(rts)) - .setInitialRunTarget("Startup") - .setFallbackRunTarget(std::move(fallback)) - .build(); + auto config = ConfigBuilder{} + .setComponents(std::move(procs)) + .setRunTargets(std::move(rts)) + .setInitialRunTarget("Startup") + .setFallbackRunTarget(std::move(fallback)) + .build(); + + graph_config_ = GraphConfig{ + config.takeComponents(), + config.takeRunTargets(), + config.takeFallbackRunTarget(), + config.takeInitialRunTarget()}; } }; 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 b156a56ad6..5c5907eed6 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/recovery_client/mock_irecovery_client.h" -#include "score/mw/launch_manager/supervision_control_client/mock_supervision_control_notifier.hpp" +#include "score/mw/launch_manager/supervision_control_client/mock_activation_state_reporter.hpp" +#include "score/mw/launch_manager/supervision_control_client/mock_supervision_factory.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() { @@ -112,12 +102,22 @@ Config makeMinimalConfig() .build(); } +GraphConfig takeGraphConfig(Config& config) +{ + return GraphConfig{ + config.takeComponents(), + config.takeRunTargets(), + config.takeFallbackRunTarget(), + config.takeInitialRunTarget()}; +} + class ProcessGroupManagerWatchdogTest : public Test { protected: void expectNormalStartup() { - EXPECT_CALL(*alive_monitor_thread_, start()).WillOnce(Return(true)); + EXPECT_CALL(*alive_monitor_, init()).WillOnce(Return(true)); + EXPECT_CALL(*alive_monitor_, start()); EXPECT_CALL(*watchdog_, init(_, _)).WillOnce(Return(true)); EXPECT_CALL(*watchdog_, enable()).WillOnce(Return(true)); } @@ -127,9 +127,9 @@ 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_, getSupervisionFactory).WillByDefault(ReturnRef(factory_)); auto recovery_client = std::make_shared>(); recovery_client_ = recovery_client.get(); @@ -138,22 +138,24 @@ 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(); + auto config = makeMinimalConfig(); + process_group_manager_ = std::make_unique( - makeMinimalConfig(), - std::move(alive_monitor_thread), + takeGraphConfig(config), + std::move(alive_monitor), std::move(recovery_client), - std::move(supervision_control_notifier), - std::move(watchdog)); + std::move(watchdog), + config.takeWatchdog()); } void TearDown() override @@ -161,11 +163,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 +180,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_, stop()).Times(1); // When auto initialize_result = process_group_manager_->initialize(); @@ -197,7 +199,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_, stop()).Times(1); // When ASSERT_TRUE(process_group_manager_->initialize()); @@ -221,7 +223,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_, stop()).Times(1); // When ASSERT_TRUE(process_group_manager_->initialize()); @@ -232,7 +234,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 +250,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_, stop()).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 eba560a307..ce3e552c63 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 @@ -17,18 +17,15 @@ #include "score/mw/launch_manager/osal/ifile_waiter.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 "score/mw/launch_manager/supervision_control_client/isupervision_factory.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}; @@ -37,6 +34,9 @@ struct ProcessHandling /// @brief Interface used to wait for a FileState ready condition. osal::IFileWaiter* file_waiter_{nullptr}; + + /// @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 b901b4cc43..5f34009a4c 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 @@ -32,19 +32,38 @@ ProcessInfoNode::ProcessInfoNode(configuration::ComponentConfig&& config, Proces exit_code_(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_; + + config_.deployment_config.environmental_variables.add( + "LCM_ALIVE_INTERFACE_PATH", aliveInterfacePath(identifier_)); + + state_publisher_ = process_handling_.supervision_factory.constructSupervision( + identifier_, uid, app_profile.alive_supervision.value()); + + if (!state_publisher_) + { + LM_LOG_ERROR() << "Failed to set up alive supervision for" << identifier_; + } + else + { + LM_LOG_DEBUG() << "Successfully set up alive supervision for" << identifier_; + } + } } IComponent::RequestResult ProcessInfoNode::tryReportCompletion(score::mw::lifecycle::ProcessState new_state) @@ -95,7 +114,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}; @@ -105,14 +124,14 @@ IComponent::RequestResult ProcessInfoNode::tryReportSuccess() std::optional ProcessInfoNode::getTimeForReport() const { - if (!isReporting()) + if (isSupervised() && state_publisher_) { - 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) @@ -219,6 +238,13 @@ bool ProcessInfoNode::isReporting() const return config_.component_properties.application_profile.application_type != configuration::ApplicationType::Native; } +bool ProcessInfoNode::isSupervised() const +{ + const auto app_type = config_.component_properties.application_profile.application_type; + return app_type == configuration::ApplicationType::ReportingAndSupervised || + app_type == configuration::ApplicationType::StateManager; +} + IComponent::RequestResult ProcessInfoNode::startProcess(score::cpp::stop_token stop_token) { LM_LOG_DEBUG() << "Starting process (" << identifier_ << ") from executable" << config_.deployment_config.bin_dir @@ -513,7 +539,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 bd41747abb..f294bc4c54 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 @@ -21,7 +21,7 @@ #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 "score/mw/launch_manager/supervision_control_client/iactivation_state_reporter.hpp" #include #include #include @@ -54,8 +54,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); @@ -71,6 +69,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_) { } @@ -110,6 +109,9 @@ class ProcessInfoNode final : public IComponent /// @brief Returns true if the process is configured to report kRunning bool isReporting() const; + /// @brief Returns true if the process is configured to report to alive monitor + bool isSupervised() const; + /// @brief Atomically transitions to new_state if the transition is valid. For reporting /// processes, also notifies the platform health manager of the state change. /// @param new_state The desired process state. @@ -119,7 +121,7 @@ class ProcessInfoNode final : public IComponent /// @brief Helper method to post on the semaphore waiting for kRunning if it exists void unblockSync(); - /// @brief If this process is configured to report to alive monitor, return the current time + /// @brief If this process is successfully configured to report to alive monitor, return the current time [[nodiscard]] std::optional getTimeForReport() const; /// @brief Get the request result corresponding to the new state reached. For example, if the ready state is @@ -205,6 +207,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 a17986b0a9..931774dbf9 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 @@ -15,7 +15,8 @@ #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 "score/mw/launch_manager/supervision_control_client/mock_activation_state_reporter.hpp" +#include "score/mw/launch_manager/supervision_control_client/mock_supervision_factory.hpp" #include #include #include @@ -26,8 +27,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"}; @@ -47,8 +49,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. @@ -70,8 +99,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_, nullptr, mock_factory_}); } /// @brief Helper method to create a ProcessInfoNode with a FileState ready condition. @@ -92,7 +128,7 @@ class ProcessInfoNodeFixture : public ::testing::Test config.deployment_config.shutdown_timeout_ms = shutdown_timeout_ms_; return std::make_unique( - std::move(config), ProcessHandling{mock_publisher_, &mock_processIf_, process_map_, &mock_file_waiter_}); + std::move(config), ProcessHandling{&mock_processIf_, process_map_, &mock_file_waiter_, mock_factory_}); } /// @brief Helper method to create a ProcessInfoNode that is self-terminating. @@ -149,7 +185,7 @@ class ProcessInfoNodeFixture : public ::testing::Test std::shared_ptr process_map_{std::make_shared()}; StrictMock mock_processIf_{}; StrictMock mock_file_waiter_{}; - NiceMock mock_publisher_{}; + NiceMock mock_factory_{}; }; // Bundles different cases for activate() that occur during startup, before the ready condition is reached. @@ -193,10 +229,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{}); @@ -419,12 +455,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{}); @@ -440,7 +476,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(_, _)) @@ -449,7 +486,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{}); @@ -465,9 +501,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) @@ -484,7 +521,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{}); @@ -552,8 +588,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(_, _)) @@ -564,7 +602,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{}); @@ -664,9 +701,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()); @@ -727,7 +764,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)); @@ -775,7 +811,6 @@ TEST_F(ProcessInfoNodeFileStateTest, ConditionAlreadyMet_ReturnsSuccess) Eq(std::chrono::milliseconds{5}), _)) .WillOnce(Return(osal::OsalReturnType::kSuccess)); - EXPECT_CALL(mock_publisher_, reportActivation); auto result = node->activate(score::cpp::stop_token{}); @@ -796,7 +831,6 @@ TEST_F(ProcessInfoNodeFileStateTest, NotExistingCondition_ReturnsSuccess) EXPECT_CALL(mock_processIf_, ignoreRunning(_)).WillOnce(Return(osal::OsalReturnType::kSuccess)); EXPECT_CALL(mock_file_waiter_, waitForFile(_, Eq(configuration::FileExistenceState::NotExisting), _, _, _)) .WillOnce(Return(osal::OsalReturnType::kSuccess)); - EXPECT_CALL(mock_publisher_, reportActivation); auto result = node->activate(score::cpp::stop_token{}); @@ -833,7 +867,6 @@ TEST_F(ProcessInfoNodeFileStateTest, WaitForFileTimesOut_ReturnsActivationTimedO EXPECT_CALL(mock_file_waiter_, waitForFile(_, _, _, _, _)).WillOnce(Return(osal::OsalReturnType::kTimeout)); // 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{}); @@ -841,3 +874,5 @@ TEST_F(ProcessInfoNodeFileStateTest, WaitForFileTimesOut_ReturnsActivationTimedO ASSERT_THAT(result.error(), Eq(IComponent::ComponentError::kActivationTimedOut)); ASSERT_THAT(node->getState(), Eq(score::mw::lifecycle::ProcessState::kTerminated)); } + +} // namespace score::mw::lifecycle::internal diff --git a/score/launch_manager/src/daemon/src/process_group_manager/ialive_monitor_thread.hpp b/score/launch_manager/src/daemon/src/process_group_manager/ialive_monitor_thread.hpp deleted file mode 100644 index 63804f1a71..0000000000 --- a/score/launch_manager/src/daemon/src/process_group_manager/ialive_monitor_thread.hpp +++ /dev/null @@ -1,29 +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_IALIVE_MONITOR_THREAD_HPP_INCLUDED -#define SCORE_LCM_IALIVE_MONITOR_THREAD_HPP_INCLUDED - -namespace score::mw::lifecycle::internal -{ -class IAliveMonitorThread -{ - public: - virtual bool start() = 0; - virtual void stop() = 0; - - virtual ~IAliveMonitorThread() = default; -}; -} // namespace score::mw::lifecycle::internal - -#endif 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 0c478c0745..e0272242dd 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 @@ -38,19 +37,19 @@ void ProcessGroupManager::cancel() } ProcessGroupManager::ProcessGroupManager( - configuration::Config&& config, - std::unique_ptr alive_monitor_thread, + GraphConfig&& config, + std::unique_ptr alive_monitor, std::shared_ptr recovery_client, - std::unique_ptr supervision_control_notifier, - std::unique_ptr watchdog) + std::unique_ptr watchdog, + std::optional&& watchdog_config) : configuration_(std::move(config)), + watchdog_config_(watchdog_config), process_interface_(), file_waiter_(), 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)) { @@ -82,7 +81,7 @@ bool ProcessGroupManager::initialize() return false; } - const std::size_t total_processes = configuration_.components().size(); + const std::size_t total_processes = configuration_.components_.size(); if (total_processes > static_cast(ProcessLimits::kMaxProcesses)) { @@ -90,6 +89,12 @@ bool ProcessGroupManager::initialize() return false; } + if (!alive_monitor_->init()) + { + LM_LOG_ERROR() << "Alive monitor initialization failed"; + return false; + } + createProcessComponentsObjects(total_processes); if (!initializeProcessGroups()) @@ -98,18 +103,13 @@ bool ProcessGroupManager::initialize() } LM_LOG_DEBUG() << "Process Group initialization done"; - if (!alive_monitor_thread_->start()) - { - LM_LOG_ERROR() << "Alive monitor thread failed to start"; - return false; - } - const auto watchdog_config = configuration_.takeWatchdog(); + alive_monitor_->start(); // Watchdog config may not be available if no watchdog is configured - if (watchdog_config.has_value()) + if (watchdog_config_.has_value()) { - if (!watchdog_->init(std::move(watchdog_config).value(), score::mw::lifecycle::internal::kMainLoopCycleTimeNs)) + if (!watchdog_->init(std::move(watchdog_config_).value(), score::mw::lifecycle::internal::kMainLoopCycleTimeNs)) { LM_LOG_ERROR() << "Watchdog initialization failed"; return false; @@ -133,7 +133,7 @@ void ProcessGroupManager::deinitialize() event_queue_->stop(); } os_handler_.reset(); - alive_monitor_thread_->stop(); + alive_monitor_->stop(); // 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 @@ -208,10 +208,10 @@ bool ProcessGroupManager::initializeProcessGroups() { graph_ = std::make_shared( // size is +2 for fallback + off - configuration_.components().size() + configuration_.runTargets().size() + 2, + configuration_.components_.size() + configuration_.run_targets_.size() + 2, configuration_, worker_jobs_, - ProcessHandling{*supervision_control_notifier_.get(), &process_interface_, process_map_, &file_waiter_}, + ProcessHandling{&process_interface_, process_map_, &file_waiter_, alive_monitor_->getSupervisionFactory()}, this); LM_LOG_DEBUG() << "Process group initialized successfully"; @@ -319,7 +319,7 @@ bool ProcessGroupManager::startInitialTransition() { LM_LOG_DEBUG() << "=============STARTING STARTUP STATE============"; SCORE_LANGUAGE_FUTURECPP_ASSERT_MESSAGE(bool(graph_), "Graph not initialized"); - graph_->startInitialTransition(IdentifierHash{configuration_.initialRunTarget()}); + graph_->startInitialTransition(IdentifierHash{configuration_.initial_run_target_}); return true; } 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 2926d69dc5..6ff11364db 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" @@ -33,10 +34,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,11 +72,11 @@ class ProcessGroupManager final : public ITransitionResultPublisher /// @param watchdog A unique pointer to an IWatchdogIf instance serviced during the main loop. May be nullptr in /// legacy configuration where no watchdog is wired. ProcessGroupManager( - configuration::Config&& config, - std::unique_ptr alive_monitor_thread, + GraphConfig&& config, + std::unique_ptr alive_monitor, std::shared_ptr recovery_client, - std::unique_ptr supervision_control_notifier, - std::unique_ptr watchdog); + std::unique_ptr watchdog, + std::optional&& watchdog_config); /// @brief Initializes the process group manager. /// Sets up a signal handler for SIGINT and SIGTERM so that the main loop of @@ -260,7 +259,10 @@ class ProcessGroupManager final : public ITransitionResultPublisher bool initializeControlClientHandler(); /// @brief The configuration object associated with the ProcessGroupManager. - configuration::Config configuration_; + GraphConfig configuration_; + + /// @brief The configuration object associated with the watchdog. + std::optional watchdog_config_; /// @brief The process interface object associated with the ProcessGroupManager. osal::ProcessLauncher process_interface_; @@ -283,10 +285,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 index 1a5e5a2134..81ad5eebda 100644 --- a/score/launch_manager/src/daemon/src/supervision_control_client/BUILD +++ b/score/launch_manager/src/daemon/src/supervision_control_client/BUILD @@ -20,103 +20,77 @@ cc_library( strip_include_prefix = "/score/launch_manager/src/daemon/src/supervision_control_client", visibility = ["//score:__subpackages__"], deps = [ + "//externals/ipc_dropin", "//score/launch_manager/src/daemon/src/common:identifier_hash", ], ) cc_library( - name = "isupervision_control_receiver", - hdrs = ["isupervision_control_receiver.hpp"], + name = "iactivation_state_reporter", + hdrs = ["iactivation_state_reporter.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"], + name = "supervision_handle", + hdrs = ["supervision_handle.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", + ":iactivation_state_reporter", ":supervision_event", + "//score/launch_manager/src/daemon/src/common:identifier_hash", + "//score/launch_manager/src/daemon/src/common:log", ], ) cc_library( - name = "isupervision_event_publisher", - hdrs = ["isupervision_event_publisher.hpp"], + name = "isupervision_factory", + hdrs = ["isupervision_factory.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_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", + name = "mock_activation_state_reporter", testonly = True, - hdrs = ["mock_supervision_event_publisher.hpp"], + hdrs = ["mock_activation_state_reporter.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", + ":iactivation_state_reporter", "@googletest//:gtest_main", ], ) cc_library( - name = "mock_supervision_control_notifier", + name = "mock_supervision_factory", testonly = True, - hdrs = ["mock_supervision_control_notifier.hpp"], + hdrs = ["mock_supervision_factory.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", + ":isupervision_factory", "@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", + ":supervision_handle", "@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 97bd6d036e..0000000000 --- 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 a5a92b0f3d..0000000000 --- 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 4479da72f3..0000000000 --- a/score/launch_manager/src/daemon/src/supervision_control_client/details/supervision_control_receiver.hpp +++ /dev/null @@ -1,63 +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::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 score::mw::lifecycle - -#endif // SUPERVISION_CONTROL_RECEIVER_HPP_INCLUDED diff --git a/score/launch_manager/src/daemon/src/supervision_control_client/isupervision_event_publisher.hpp b/score/launch_manager/src/daemon/src/supervision_control_client/iactivation_state_reporter.hpp similarity index 55% rename from score/launch_manager/src/daemon/src/supervision_control_client/isupervision_event_publisher.hpp rename to score/launch_manager/src/daemon/src/supervision_control_client/iactivation_state_reporter.hpp index 44ccc783bd..a81a709b83 100644 --- a/score/launch_manager/src/daemon/src/supervision_control_client/isupervision_event_publisher.hpp +++ b/score/launch_manager/src/daemon/src/supervision_control_client/iactivation_state_reporter.hpp @@ -10,29 +10,28 @@ * * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -#ifndef ISUPERVISION_EVENT_PUBLISHER_HPP_INCLUDED -#define ISUPERVISION_EVENT_PUBLISHER_HPP_INCLUDED +#ifndef IACTIVATION_STATE_REPORTER_HPP_INCLUDED +#define IACTIVATION_STATE_REPORTER_HPP_INCLUDED -#include "score/mw/launch_manager/common/identifier_hash.hpp" #include namespace score::mw::lifecycle { -/// @brief ISupervisionEventPublisher interface for forwarding supervision events to the alive monitor. +/// @brief IActivationStateReporter 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 the active state or inactive state -class ISupervisionEventPublisher +class IActivationStateReporter { public: /// @brief Destructor. - virtual ~ISupervisionEventPublisher() noexcept = default; + virtual ~IActivationStateReporter() 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; }; } // namespace score::mw::lifecycle 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 948a07a04e..0000000000 --- a/score/launch_manager/src/daemon/src/supervision_control_client/isupervision_control_notifier.hpp +++ /dev/null @@ -1,39 +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::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 score::mw::lifecycle - -#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 e3a08b4961..0000000000 --- a/score/launch_manager/src/daemon/src/supervision_control_client/isupervision_control_receiver.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 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::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 score::mw::lifecycle - -#endif diff --git a/score/launch_manager/src/daemon/src/supervision_control_client/isupervision_factory.hpp b/score/launch_manager/src/daemon/src/supervision_control_client/isupervision_factory.hpp new file mode 100644 index 0000000000..609a4cfcff --- /dev/null +++ b/score/launch_manager/src/daemon/src/supervision_control_client/isupervision_factory.hpp @@ -0,0 +1,52 @@ +/******************************************************************************** + * 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/common/identifier_hash.hpp" +#include "score/mw/launch_manager/configuration/component_config.hpp" +#include "score/mw/launch_manager/supervision_control_client/supervision_handle.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 process + /// reports its activation using the IActivationStateReporter. + /// @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 Nullptr if the construction failed, handle for the process to start and stop its own supervision + /// otherwise. + 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/supervision_control_client/mock_supervision_control_notifier.hpp b/score/launch_manager/src/daemon/src/supervision_control_client/mock_activation_state_reporter.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/supervision_control_client/mock_activation_state_reporter.hpp index db237840a1..346b33d4d3 100644 --- a/score/launch_manager/src/daemon/src/supervision_control_client/mock_supervision_control_notifier.hpp +++ b/score/launch_manager/src/daemon/src/supervision_control_client/mock_activation_state_reporter.hpp @@ -10,23 +10,22 @@ * * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -#ifndef MOCK_SUPERVISION_CONTROL_NOTIFIER_HPP_INCLUDED -#define MOCK_SUPERVISION_CONTROL_NOTIFIER_HPP_INCLUDED +#ifndef MOCK_ACTIVATION_STATE_REPORTER_HPP_INCLUDED +#define MOCK_ACTIVATION_STATE_REPORTER_HPP_INCLUDED -#include "score/mw/launch_manager/supervision_control_client/isupervision_control_notifier.hpp" +#include "score/mw/launch_manager/supervision_control_client/iactivation_state_reporter.hpp" #include namespace score::mw::lifecycle { -class MockSupervisionControlNotifier : public ISupervisionControlNotifier +class MockActivationStateReporter : public IActivationStateReporter { 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(bool, reportActivation, (timespec time), (override, noexcept)); + MOCK_METHOD(bool, reportDeactivation, (timespec time), (override, noexcept)); }; } // namespace score::mw::lifecycle -#endif // MOCK_SUPERVISION_CONTROL_NOTIFIER_HPP_INCLUDED +#endif // MOCK_ACTIVATION_STATE_REPORTER_HPP_INCLUDED 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 8911199cb2..0000000000 --- a/score/launch_manager/src/daemon/src/supervision_control_client/mock_iprocess_state_notifier.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 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::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 score::mw::lifecycle - -#endif // IPROCESSSTATE_NOTIFIER_MOCK_HPP_INCLUDED diff --git a/score/launch_manager/src/daemon/src/supervision_control_client/mock_supervision_event_publisher.hpp b/score/launch_manager/src/daemon/src/supervision_control_client/mock_supervision_factory.hpp similarity index 61% rename from score/launch_manager/src/daemon/src/supervision_control_client/mock_supervision_event_publisher.hpp rename to score/launch_manager/src/daemon/src/supervision_control_client/mock_supervision_factory.hpp index 645c82cf94..5ed30d71ea 100644 --- a/score/launch_manager/src/daemon/src/supervision_control_client/mock_supervision_event_publisher.hpp +++ b/score/launch_manager/src/daemon/src/supervision_control_client/mock_supervision_factory.hpp @@ -10,22 +10,26 @@ * * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -#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" +#ifndef MOCK_SUPERVISION_FACTORY_HPP_INCLUDED +#define MOCK_SUPERVISION_FACTORY_HPP_INCLUDED + +#include "score/mw/launch_manager/supervision_control_client/isupervision_factory.hpp" #include namespace score::mw::lifecycle { -class MockSupervisionEventPublisher : public ISupervisionEventPublisher +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, + constructSupervision, + (const IdentifierHash id, const uid_t uid, const internal::configuration::ComponentAliveSupervision& config), + (override)); }; } // namespace score::mw::lifecycle -#endif // MOCK_SUPERVISION_EVENT_PUBLISHER_HPP_INCLUDED +#endif 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 index 47b8970b19..8e5691c9ef 100644 --- 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 @@ -10,17 +10,13 @@ * * 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 "score/mw/launch_manager/supervision_control_client/supervision_handle.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: @@ -28,27 +24,20 @@ class SupervisionControlClient_UT : public ::testing::Test { RecordProperty("TestType", "interface-test"); RecordProperty("DerivationTechnique", "explorative-testing "); - notifier_ = std::make_unique(); - receiver_ = notifier_->constructReceiver(); + buffer_ = std::make_shared(); + handle_ = std::make_unique(process_, buffer_); } + void TearDown() override { - receiver_.reset(); - notifier_.reset(); + handle_.reset(); + buffer_.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); -} + const IdentifierHash process_{"Process"}; + std::shared_ptr buffer_; + std::unique_ptr handle_; +}; TEST_F(SupervisionControlClient_UT, SupervisionControlClient_QueueOneEvent_Succeeds) { @@ -56,26 +45,22 @@ TEST_F(SupervisionControlClient_UT, SupervisionControlClient_QueueOneEvent_Succe "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 = {}}; + SupervisionEvent event1{.id = process_, .eventType = SupervisionEventType::kActivation, .systemClockTimestamp = {}}; clock_gettime(CLOCK_MONOTONIC, &event1.systemClockTimestamp); - bool queued = notifier_->reportActivation(event1.id, event1.systemClockTimestamp); + bool queued = handle_->reportActivation(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); + 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); - auto no_more = receiver_->getNextSupervisionEvent(); - ASSERT_TRUE(no_more.has_value()); - ASSERT_FALSE(no_more->has_value()); + bool items_remaining = buffer_->tryDequeue(result); + ASSERT_FALSE(items_remaining); } TEST_F(SupervisionControlClient_UT, SupervisionControlClient_QueueMaxNumberOfEvents_Succeeds) @@ -85,27 +70,25 @@ TEST_F(SupervisionControlClient_UT, SupervisionControlClient_QueueMaxNumberOfEve "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) { - 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); + 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) { - 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))); + ASSERT_TRUE(buffer_->tryDequeue(result)); + EXPECT_EQ(result.id, event.id); } - auto no_more = receiver_->getNextSupervisionEvent(); - ASSERT_TRUE(no_more.has_value()); - ASSERT_FALSE(no_more->has_value()); + bool items_remaining = buffer_->tryDequeue(result); + ASSERT_FALSE(items_remaining); } TEST_F(SupervisionControlClient_UT, SupervisionControlClient_QueueOneEventTooMany_Fails) @@ -114,28 +97,14 @@ TEST_F(SupervisionControlClient_UT, SupervisionControlClient_QueueOneEventTooMan "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 = {}}; + SupervisionEvent event{.id = process_, .eventType = 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); + bool queued = handle_->reportActivation(event.systemClockTimestamp); ASSERT_TRUE(queued) << "Failed to queue event at index " << i; } - bool queued = notifier_->reportActivation(event1.id, event1.systemClockTimestamp); + bool queued = handle_->reportActivation(event.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 6a06b05aee..0000000000 --- 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 1f54bba5e9..0000000000 --- a/score/launch_manager/src/daemon/src/supervision_control_client/supervision_control_notifier.hpp +++ /dev/null @@ -1,72 +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::mw::lifecycle::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 score::mw::lifecycle::internal -#endif diff --git a/score/launch_manager/src/daemon/src/supervision_control_client/supervision_event.hpp b/score/launch_manager/src/daemon/src/supervision_control_client/supervision_event.hpp index 4d558a088c..6288427932 100644 --- a/score/launch_manager/src/daemon/src/supervision_control_client/supervision_event.hpp +++ b/score/launch_manager/src/daemon/src/supervision_control_client/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::mw::lifecycle { @@ -52,6 +54,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 score::mw::lifecycle #endif // SUPERVISION_EVENT_HPP_INCLUDED diff --git a/score/launch_manager/src/daemon/src/supervision_control_client/supervision_handle.hpp b/score/launch_manager/src/daemon/src/supervision_control_client/supervision_handle.hpp new file mode 100644 index 0000000000..ffd56a9225 --- /dev/null +++ b/score/launch_manager/src/daemon/src/supervision_control_client/supervision_handle.hpp @@ -0,0 +1,81 @@ +/******************************************************************************** + * 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/common/identifier_hash.hpp" +#include "score/mw/launch_manager/common/log.hpp" +#include "score/mw/launch_manager/supervision_control_client/iactivation_state_reporter.hpp" +#include "score/mw/launch_manager/supervision_control_client/supervision_event.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 IActivationStateReporter +{ + 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) + { + } + + /// @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}); + } + + 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_; +}; + +} // namespace mw::lifecycle + +} // namespace score + +#endif