From d2a3fc4f684e196b800ed7988774283a7477b179 Mon Sep 17 00:00:00 2001 From: shegazyy Date: Mon, 2 Feb 2026 15:42:50 +0200 Subject: [PATCH 1/5] Implementing report running state in lifecycle manager --- src/lifecycle_client_lib/BUILD | 2 ++ src/lifecycle_client_lib/include/lifecyclemanager.h | 2 +- src/lifecycle_client_lib/src/lifecyclemanager.cpp | 12 +++++++++++- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/lifecycle_client_lib/BUILD b/src/lifecycle_client_lib/BUILD index 9e312a89..5dc05134 100644 --- a/src/lifecycle_client_lib/BUILD +++ b/src/lifecycle_client_lib/BUILD @@ -46,6 +46,7 @@ cc_library( }), visibility = ["//visibility:public"], deps = [ + "//src/launch_manager_daemon/lifecycle_client_lib:lifecycle_client", "@score_baselibs//score/language/futurecpp", "@score_baselibs//score/memory:string_literal", "@score_baselibs//score/mw/log", @@ -117,6 +118,7 @@ cc_library( tags = ["FUSA"], deps = [ ":application", + "//src/launch_manager_daemon/lifecycle_client_lib:lifecycle_client", "@score_baselibs//score/mw/log", "@score_baselibs//score/os:stdlib", "@score_baselibs//score/os/utils:signal", diff --git a/src/lifecycle_client_lib/include/lifecyclemanager.h b/src/lifecycle_client_lib/include/lifecyclemanager.h index 79ec414e..8215c619 100644 --- a/src/lifecycle_client_lib/include/lifecyclemanager.h +++ b/src/lifecycle_client_lib/include/lifecyclemanager.h @@ -83,7 +83,7 @@ class LifeCycleManager /** * \brief Hook function for reporting running state. */ - virtual void report_running() noexcept {}; + virtual void report_running() noexcept ; /** * \brief Hook function for reporting shutdown state. */ diff --git a/src/lifecycle_client_lib/src/lifecyclemanager.cpp b/src/lifecycle_client_lib/src/lifecyclemanager.cpp index eb66e682..97e156ec 100644 --- a/src/lifecycle_client_lib/src/lifecyclemanager.cpp +++ b/src/lifecycle_client_lib/src/lifecyclemanager.cpp @@ -15,7 +15,7 @@ #include "score/os/errno.h" #include - +#include "score/lcm/lifecycle_client.h" #include "score/mw/log/logging.h" #include "score/os/stdlib_impl.h" @@ -163,3 +163,13 @@ void score::mw::lifecycle::LifeCycleManager::handle_signal() /* KW_SUPPRESS_END:MISRA.STDLIB.ABORT,MISRA.USE.EXPANSION */ } } + +void score::mw::lifecycle::LifeCycleManager::report_running() noexcept +{ + mw::log::LogInfo() << "Reporting kRunning to Launch Manager"; + const auto result = score::lcm::LifecycleClient{}.ReportExecutionState(score::lcm::ExecutionState::kRunning); + if (!result.has_value()) + { + mw::log::LogError() << "Failed to report kRunning to Launch Manager: " << result.error().Message(); + } +} From 5fcf6d4672ba250c400c694e6da6f93282016760 Mon Sep 17 00:00:00 2001 From: shegazyy Date: Tue, 3 Feb 2026 15:11:49 +0200 Subject: [PATCH 2/5] demonstrate kRunning reporting in cpp_lifecycle_app --- examples/cpp_lifecycle_app/main.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/examples/cpp_lifecycle_app/main.cpp b/examples/cpp_lifecycle_app/main.cpp index 408c1c44..c6e76047 100644 --- a/examples/cpp_lifecycle_app/main.cpp +++ b/examples/cpp_lifecycle_app/main.cpp @@ -131,8 +131,18 @@ int main(int argc, char **argv) return EXIT_FAILURE; } - score::lcm::LifecycleClient{}.ReportExecutionState(score::lcm::ExecutionState::kRunning); - + auto result = score::lcm::LifecycleClient{}.ReportExecutionState(score::lcm::ExecutionState::kRunning); + + if (!result.has_value()) { + std::cerr << "Failed to report kRunning: " + << result.error().Message() + << std::endl; + } + else + { + std::cout << "ReportExecutionState(kRunning) succeeded" << std::endl; + } + timespec req{ static_cast(config->responseTimeInMs / 1000), static_cast((config->responseTimeInMs % 1000) * 1000000L) From f8b74101087bd8d27b6a24af3cd23fe4cdce1e21 Mon Sep 17 00:00:00 2001 From: shegazyy Date: Thu, 5 Feb 2026 17:08:16 +0200 Subject: [PATCH 3/5] Align example app with the public lifecycle client APIs --- examples/cpp_lifecycle_app/BUILD | 3 +- examples/cpp_lifecycle_app/main.cpp | 160 ++++++++++-------- .../include/runapplication.h | 3 +- 3 files changed, 89 insertions(+), 77 deletions(-) diff --git a/examples/cpp_lifecycle_app/BUILD b/examples/cpp_lifecycle_app/BUILD index 40f35d03..ba8e4544 100644 --- a/examples/cpp_lifecycle_app/BUILD +++ b/examples/cpp_lifecycle_app/BUILD @@ -27,6 +27,7 @@ cc_binary( }), visibility = ["//visibility:public"], deps = [ - "//src/launch_manager_daemon/lifecycle_client_lib:lifecycle_client", + "//src/lifecycle_client_lib:lifecycle_client_lib", + ], ) diff --git a/examples/cpp_lifecycle_app/main.cpp b/examples/cpp_lifecycle_app/main.cpp index c6e76047..195f9642 100644 --- a/examples/cpp_lifecycle_app/main.cpp +++ b/examples/cpp_lifecycle_app/main.cpp @@ -19,17 +19,27 @@ #include #include #include +#include +#include #ifdef __linux__ #include #include #endif -#include "score/lcm/lifecycle_client.h" +#include "src/lifecycle_client_lib/include/application.h" +#include "src/lifecycle_client_lib/include/runapplication.h" -/// @brief CLI configuration options for the not_supervised_application process -struct Config -{ +// ---------- Simple SIGTERM fallback ---------- +namespace { + std::atomic g_stopRequested{false}; + + void onSignal(int) noexcept { + g_stopRequested.store(true, std::memory_order_relaxed); + } +} + +struct Config { std::int32_t responseTimeInMs{100}; bool crashRequested{false}; std::int32_t crashTimeInMs{1000}; @@ -88,13 +98,6 @@ std::optional parseOptions(int argc, char *const *argv) noexcept return config; } -std::atomic exitRequested{false}; - -void signalHandler(int) -{ - exitRequested = true; -} - void set_process_name() { const char* identifier = getenv("PROCESSIDENTIFIER"); if(identifier != nullptr) { @@ -109,82 +112,89 @@ void set_process_name() { #endif } } +class LifecycleApp final : public score::mw::lifecycle::Application { +public: + std::int32_t Initialize(const score::mw::lifecycle::ApplicationContext& appCtx) override { + std::signal(SIGINT, onSignal); + std::signal(SIGTERM, onSignal); + set_process_name(); + + const auto& argStrings = appCtx.get_arguments(); + + m_argvStorage.clear(); + m_argvStorage.reserve(argStrings.size() + 2); + + if (argStrings.empty()) { + // Minimal argv[0] so getopt never sees empty argv + m_argvStorage.push_back(const_cast("LifecycleApp")); + } else { + for (const auto& s : argStrings) { + m_argvStorage.push_back(const_cast(s.c_str())); + } + } -int main(int argc, char **argv) -{ - set_process_name(); - - signal(SIGINT, signalHandler); - signal(SIGTERM, signalHandler); + m_argvStorage.push_back(nullptr); - const auto config = parseOptions(argc, argv); - if (!config) - { - return EXIT_FAILURE; - } + const int argcLocal = static_cast(m_argvStorage.size() - 1); + const auto cfgOpt = parseOptions(argcLocal, m_argvStorage.data()); + if (!cfgOpt.has_value()) { + return EXIT_FAILURE; + } - std::chrono::time_point startTime = std::chrono::steady_clock::now(); - std::chrono::duration runTime; + m_cfg = *cfgOpt; + if (m_cfg.failToStart) { + return EXIT_FAILURE; + } - if (true == config->failToStart) - { - return EXIT_FAILURE; + return 0; } - auto result = score::lcm::LifecycleClient{}.ReportExecutionState(score::lcm::ExecutionState::kRunning); - - if (!result.has_value()) { - std::cerr << "Failed to report kRunning: " - << result.error().Message() - << std::endl; - } - else - { - std::cout << "ReportExecutionState(kRunning) succeeded" << std::endl; - } - - timespec req{ - static_cast(config->responseTimeInMs / 1000), - static_cast((config->responseTimeInMs % 1000) * 1000000L) - }; - auto timeLastVerboseLog = std::chrono::steady_clock::now(); - while (!exitRequested) - { - if (true == config->crashRequested) - { - runTime = std::chrono::steady_clock::now() - startTime; - - int timeTillCrash = static_cast(config->crashTimeInMs - runTime.count()); - - if (timeTillCrash < config->responseTimeInMs) - { - // OK we need a shorter sleep now - if (timeTillCrash > 0) - { - timespec crash_req{ - static_cast(timeTillCrash / 1000), - static_cast((timeTillCrash % 1000) * 1000000L) - }; - nanosleep(&crash_req, nullptr); + std::int32_t Run(const score::cpp::stop_token& stopToken) override { + const auto start = std::chrono::steady_clock::now(); + auto lastVerbose = start; + + timespec sleepReq{ + static_cast(m_cfg.responseTimeInMs / 1000), + static_cast((m_cfg.responseTimeInMs % 1000) * 1000000L) + }; + + while (!stopToken.stop_requested() && !g_stopRequested.load(std::memory_order_relaxed)) { + if (m_cfg.crashRequested) { + const auto elapsedMs = + std::chrono::duration(std::chrono::steady_clock::now() - start).count(); + + const int remaining = static_cast(m_cfg.crashTimeInMs - elapsedMs); + if (remaining < m_cfg.responseTimeInMs) { + if (remaining > 0) { + timespec crashReq{ + static_cast(remaining / 1000), + static_cast((remaining % 1000) * 1000000L) + }; + nanosleep(&crashReq, nullptr); + } + std::abort(); } - - // let's crash... - std::abort(); } - } - - if(config->verbose) { - const auto now = std::chrono::steady_clock::now(); - if(now - timeLastVerboseLog >= std::chrono::seconds(1)) { - std::cout << "LifecycleApp: " << "Running in verbose mode" << std::endl; - timeLastVerboseLog = now; + if (m_cfg.verbose) { + const auto now = std::chrono::steady_clock::now(); + if (now - lastVerbose >= std::chrono::seconds(1)) { + std::cout << "LifecycleApp: Running in verbose mode\n"; + lastVerbose = now; + } } + + nanosleep(&sleepReq, nullptr); } - nanosleep(&req, nullptr); + return EXIT_SUCCESS; } - // normal exit - return EXIT_SUCCESS; +private: + Config m_cfg{}; + std::vector m_argvStorage; // keeps argv stable for getopt() +}; + +int main(int argc, char** argv) { + return score::mw::lifecycle::run_application(argc, argv); } diff --git a/src/lifecycle_client_lib/include/runapplication.h b/src/lifecycle_client_lib/include/runapplication.h index 7b3b4711..1d8b78f2 100644 --- a/src/lifecycle_client_lib/include/runapplication.h +++ b/src/lifecycle_client_lib/include/runapplication.h @@ -68,7 +68,8 @@ template /* NOLINTNEXTLINE(modernize-avoid-c-arrays): array tolerated for command line arguments */ std::int32_t run_application(const std::int32_t argc, const score::StringLiteral argv[], Args&&... args) { - return 0; + score::mw::lifecycle::Run runner(argc, argv); + return runner.AsPosixProcess(std::forward(args)...); } } // namespace lifecycle From 5ec6265faccce227a4e84549e9542a099df49159 Mon Sep 17 00:00:00 2001 From: shegazyy Date: Thu, 5 Feb 2026 19:11:48 +0200 Subject: [PATCH 4/5] Adapt example app with the public lifecycle client APIs --- examples/cpp_lifecycle_app/main.cpp | 152 ++++++++++++++++------------ 1 file changed, 88 insertions(+), 64 deletions(-) diff --git a/examples/cpp_lifecycle_app/main.cpp b/examples/cpp_lifecycle_app/main.cpp index 195f9642..56e78aae 100644 --- a/examples/cpp_lifecycle_app/main.cpp +++ b/examples/cpp_lifecycle_app/main.cpp @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -30,16 +29,9 @@ #include "src/lifecycle_client_lib/include/application.h" #include "src/lifecycle_client_lib/include/runapplication.h" -// ---------- Simple SIGTERM fallback ---------- -namespace { - std::atomic g_stopRequested{false}; - - void onSignal(int) noexcept { - g_stopRequested.store(true, std::memory_order_relaxed); - } -} - -struct Config { +/// @brief CLI configuration options for the not_supervised_application process +struct Config +{ std::int32_t responseTimeInMs{100}; bool crashRequested{false}; std::int32_t crashTimeInMs{1000}; @@ -63,18 +55,15 @@ std::optional parseOptions(int argc, char *const *argv) noexcept switch (static_cast(c)) { case 'r': - // response time config.responseTimeInMs = std::stoi(optarg); break; case 'c': - // crash time config.crashRequested = true; config.crashTimeInMs = std::stoi(optarg); break; case 's': - // start-up failure config.failToStart = true; break; @@ -98,103 +87,138 @@ std::optional parseOptions(int argc, char *const *argv) noexcept return config; } -void set_process_name() { +std::atomic exitRequested{false}; + +void signalHandler(int) +{ + exitRequested = true; +} + +void set_process_name() +{ const char* identifier = getenv("PROCESSIDENTIFIER"); - if(identifier != nullptr) { + if (identifier != nullptr) + { #ifdef __QNXNTO__ - if (pthread_setname_np(pthread_self(), identifier) != 0) { - std::cerr << "Failed to set QNX thread name" << std::endl; - } + if (pthread_setname_np(pthread_self(), identifier) != 0) + { + std::cerr << "Failed to set QNX thread name" << std::endl; + } #elif defined(__linux__) - if (prctl(PR_SET_NAME, identifier) < 0) { - std::cerr << "Failed to set process name to " << identifier << std::endl; - } + if (prctl(PR_SET_NAME, identifier) < 0) + { + std::cerr << "Failed to set process name to " << identifier << std::endl; + } #endif } } -class LifecycleApp final : public score::mw::lifecycle::Application { + +class LifecycleApp final : public score::mw::lifecycle::Application +{ public: - std::int32_t Initialize(const score::mw::lifecycle::ApplicationContext& appCtx) override { - std::signal(SIGINT, onSignal); - std::signal(SIGTERM, onSignal); + std::int32_t Initialize(const score::mw::lifecycle::ApplicationContext& appCtx) override + { set_process_name(); - - const auto& argStrings = appCtx.get_arguments(); + signal(SIGINT, signalHandler); + signal(SIGTERM, signalHandler); + + // Build a classic argv for getopt() from ApplicationContext arguments + const auto& args = appCtx.get_arguments(); m_argvStorage.clear(); - m_argvStorage.reserve(argStrings.size() + 2); + m_argvStorage.reserve(args.size() + 2); - if (argStrings.empty()) { - // Minimal argv[0] so getopt never sees empty argv + // Ensure argv[0] exists (getopt expects it) + if (args.empty()) + { m_argvStorage.push_back(const_cast("LifecycleApp")); - } else { - for (const auto& s : argStrings) { - m_argvStorage.push_back(const_cast(s.c_str())); + } + else + { + for (const auto& s : args) + { + // NOTE: relies on the underlying storage staying alive during Initialize(). + m_argvStorage.push_back(const_cast(s.data())); } } m_argvStorage.push_back(nullptr); + optind = 1; + const int argcLocal = static_cast(m_argvStorage.size() - 1); - const auto cfgOpt = parseOptions(argcLocal, m_argvStorage.data()); - if (!cfgOpt.has_value()) { + const auto config = parseOptions(argcLocal, m_argvStorage.data()); + if (!config) + { return EXIT_FAILURE; } - m_cfg = *cfgOpt; - if (m_cfg.failToStart) { + m_config = *config; + + if (true == m_config.failToStart) + { return EXIT_FAILURE; } return 0; } - std::int32_t Run(const score::cpp::stop_token& stopToken) override { - const auto start = std::chrono::steady_clock::now(); - auto lastVerbose = start; + std::int32_t Run(const score::cpp::stop_token& stopToken) override + { + std::chrono::time_point startTime = std::chrono::steady_clock::now(); + std::chrono::duration runTime; - timespec sleepReq{ - static_cast(m_cfg.responseTimeInMs / 1000), - static_cast((m_cfg.responseTimeInMs % 1000) * 1000000L) + timespec req{ + static_cast(m_config.responseTimeInMs / 1000), + static_cast((m_config.responseTimeInMs % 1000) * 1000000L) }; - while (!stopToken.stop_requested() && !g_stopRequested.load(std::memory_order_relaxed)) { - if (m_cfg.crashRequested) { - const auto elapsedMs = - std::chrono::duration(std::chrono::steady_clock::now() - start).count(); - - const int remaining = static_cast(m_cfg.crashTimeInMs - elapsedMs); - if (remaining < m_cfg.responseTimeInMs) { - if (remaining > 0) { - timespec crashReq{ - static_cast(remaining / 1000), - static_cast((remaining % 1000) * 1000000L) + auto timeLastVerboseLog = std::chrono::steady_clock::now(); + + while (!exitRequested && !stopToken.stop_requested()) + { + if (true == m_config.crashRequested) + { + runTime = std::chrono::steady_clock::now() - startTime; + int timeTillCrash = static_cast(m_config.crashTimeInMs - runTime.count()); + + if (timeTillCrash < m_config.responseTimeInMs) + { + if (timeTillCrash > 0) + { + timespec crash_req{ + static_cast(timeTillCrash / 1000), + static_cast((timeTillCrash % 1000) * 1000000L) }; - nanosleep(&crashReq, nullptr); + nanosleep(&crash_req, nullptr); } + std::abort(); } } - if (m_cfg.verbose) { + if (m_config.verbose) + { const auto now = std::chrono::steady_clock::now(); - if (now - lastVerbose >= std::chrono::seconds(1)) { - std::cout << "LifecycleApp: Running in verbose mode\n"; - lastVerbose = now; + if (now - timeLastVerboseLog >= std::chrono::seconds(1)) + { + std::cout << "LifecycleApp: Running in verbose mode" << std::endl; + timeLastVerboseLog = now; } } - nanosleep(&sleepReq, nullptr); + nanosleep(&req, nullptr); } return EXIT_SUCCESS; } private: - Config m_cfg{}; - std::vector m_argvStorage; // keeps argv stable for getopt() + Config m_config{}; + std::vector m_argvStorage{}; }; -int main(int argc, char** argv) { +int main(int argc, char** argv) +{ return score::mw::lifecycle::run_application(argc, argv); } From 5b0b39e219d3bfa9666ccf6bfddc6bfb5c71313d Mon Sep 17 00:00:00 2001 From: shegazyy Date: Sun, 8 Feb 2026 13:37:43 +0200 Subject: [PATCH 5/5] Use public lifecycle client APIs in Examples --- examples/cpp_lifecycle_app/BUILD | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/cpp_lifecycle_app/BUILD b/examples/cpp_lifecycle_app/BUILD index ba8e4544..436b38cc 100644 --- a/examples/cpp_lifecycle_app/BUILD +++ b/examples/cpp_lifecycle_app/BUILD @@ -27,7 +27,6 @@ cc_binary( }), visibility = ["//visibility:public"], deps = [ - "//src/lifecycle_client_lib:lifecycle_client_lib", - + "//src/lifecycle_client_lib", ], )