From 23fcf158dc99f995b4da8893aa8847c4b586ac38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20Fu=C3=9Fberger?= Date: Tue, 30 Jun 2026 08:24:11 +0200 Subject: [PATCH 1/3] Bring back C function prototypes --- score/launch_manager/src/alive/src/alive.cpp | 31 +++++++++------- score/launch_manager/src/alive/src/alive.h | 37 +++++++++++++++++-- .../src/lifecycle_client/src/report_running.h | 34 +++++++++++++---- 3 files changed, 77 insertions(+), 25 deletions(-) diff --git a/score/launch_manager/src/alive/src/alive.cpp b/score/launch_manager/src/alive/src/alive.cpp index 53eec5903..8f825dac8 100644 --- a/score/launch_manager/src/alive/src/alive.cpp +++ b/score/launch_manager/src/alive/src/alive.cpp @@ -14,8 +14,8 @@ #include "score/mw/lifecycle/alive.h" #include "score/mw/launch_manager/alive_monitor/details/AliveImpl.h" -#include #include +#include // The public API is only sending alive notification. No need to support different checkpoints. static constexpr std::uint32_t kDefaultCheckpointId{1U}; @@ -23,10 +23,7 @@ static constexpr std::uint32_t kDefaultCheckpointId{1U}; namespace score::mw::lifecycle { -Alive::Alive(const std::string_view& instance) noexcept(false) : - aliveImplPtr(std::make_unique(instance)) -{ -} +Alive::Alive(const std::string_view& instance) noexcept(false) : aliveImplPtr(std::make_unique(instance)) {} Alive::Alive(Alive&& se) noexcept = default; @@ -49,41 +46,47 @@ void Alive::ReportFailure() const noexcept } // namespace score::mw::lifecycle - #ifdef __cplusplus extern "C" { #endif -void* score_lcm_alive_initialize(const char* instanceSpecifier) noexcept { - if(instanceSpecifier == nullptr) { +void* score_lcm_alive_initialize(const char* instanceSpecifier) +{ + if (instanceSpecifier == nullptr) + { return nullptr; } - try { + try + { auto* alivePtr = new score::mw::lifecycle::Alive(instanceSpecifier); return static_cast(alivePtr); - } catch (...) { + } + catch (...) + { return nullptr; } } -void score_lcm_alive_deinitialize(void* instance) noexcept { +void score_lcm_alive_deinitialize(void* instance) +{ SCORE_LANGUAGE_FUTURECPP_PRECONDITION(instance != nullptr); auto* alivePtr = static_cast(instance); delete alivePtr; } -void score_lcm_alive_report_alive(void* instance) noexcept { +void score_lcm_alive_report_alive(void* instance) +{ SCORE_LANGUAGE_FUTURECPP_PRECONDITION(instance != nullptr); static_cast(instance)->ReportAlive(); } -void score_lcm_alive_report_failure(void* instance) noexcept { +void score_lcm_alive_report_failure(void* instance) +{ SCORE_LANGUAGE_FUTURECPP_PRECONDITION(instance != nullptr); static_cast(instance)->ReportFailure(); } - #ifdef __cplusplus } #endif diff --git a/score/launch_manager/src/alive/src/alive.h b/score/launch_manager/src/alive/src/alive.h index 376cf1bda..8e6daf967 100644 --- a/score/launch_manager/src/alive/src/alive.h +++ b/score/launch_manager/src/alive/src/alive.h @@ -14,6 +14,7 @@ #ifndef SCORE_LCM_ALIVE_H_ #define SCORE_LCM_ALIVE_H_ +#ifdef __cplusplus #include #include #include @@ -26,14 +27,14 @@ class AliveImpl; /// @brief Alive API for reporting alive notifications to the launch manager. /// An alive notification indicates that the component is still active and functioning correctly. -/// The launch manager is configured with an expected alive notification interval, -/// and if it does not receive an alive notification within that interval, +/// The launch manager is configured with an expected alive notification interval, +/// and if it does not receive an alive notification within that interval, /// it executes the configured recovery action. /// /// Each process may only use a single Alive instance. class Alive { -public: + public: /// @brief Creation of an Alive. /// @param [in] instance Instance specifier (currently unused) /// @throws std::runtime_error if the configured IPC channel to connect to launch manager is not existing @@ -67,10 +68,38 @@ class Alive /// @note Not Implemented. This method currently does nothing. void ReportFailure() const noexcept; -private: + private: /// @brief Unique pointer to implementation class of Alive std::unique_ptr aliveImplPtr; }; } // namespace score::mw::lifecycle + +extern "C" { +#endif /* __cplusplus */ + +// These prototypes are required to support applications written in C, which cannot use the C++ namespaces and classes. + +/// @brief Initialize an Alive instance. +/// @param [in] instanceSpecifier Instance specifier string (must not be NULL) +/// @return Opaque pointer to the Alive instance, or NULL on failure +void* score_lcm_alive_initialize(const char* instanceSpecifier); + +/// @brief Deinitialize (destroy) an Alive instance. +/// @param [in] instance Opaque pointer previously returned by score_lcm_alive_initialize (must not be NULL) +void score_lcm_alive_deinitialize(void* instance); + +/// @brief Report an alive notification. +/// @param [in] instance Opaque pointer previously returned by score_lcm_alive_initialize (must not be NULL) +void score_lcm_alive_report_alive(void* instance); + +/// @brief Report a direct failure. +/// @param [in] instance Opaque pointer previously returned by score_lcm_alive_initialize (must not be NULL) +/// @note Not Implemented. This method currently does nothing. +void score_lcm_alive_report_failure(void* instance); + +#ifdef __cplusplus +} +#endif + #endif // SCORE_LCM_ALIVE_H_ diff --git a/score/launch_manager/src/lifecycle_client/src/report_running.h b/score/launch_manager/src/lifecycle_client/src/report_running.h index 6a37c1faf..ec8bfdce1 100644 --- a/score/launch_manager/src/lifecycle_client/src/report_running.h +++ b/score/launch_manager/src/lifecycle_client/src/report_running.h @@ -14,9 +14,16 @@ #ifndef SCORE_MW_LIFECYCLE_REPORT_RUNNING_H_ #define SCORE_MW_LIFECYCLE_REPORT_RUNNING_H_ -namespace score { -namespace mw { -namespace lifecycle { +#include + +#ifdef __cplusplus + +namespace score +{ +namespace mw +{ +namespace lifecycle +{ /// @brief Signals to the Launch Manager that this process has finished /// initialization and is now entering its Running state. @@ -36,8 +43,21 @@ namespace lifecycle { /// shutdown accordingly. void report_running() noexcept; -} // namespace lifecycle -} // namespace mw -} // namespace score +} // namespace lifecycle +} // namespace mw +} // namespace score + +extern "C" { +#endif /* __cplusplus */ + +// This prototype is required to support applications written in C, which cannot use the C++ namespaces and classes. + +/// @brief Signals to the Launch Manager that this process has finished initialization and is now running. +/// @return 0 on success, -1 on failure +int8_t score_lcm_ReportRunning(void); + +#ifdef __cplusplus +} +#endif -#endif \ No newline at end of file +#endif /* SCORE_MW_LIFECYCLE_REPORT_RUNNING_H_ */ From 2c2aa11ab527f573594d13243f14dd8b27406198 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20Fu=C3=9Fberger?= Date: Tue, 30 Jun 2026 08:37:51 +0200 Subject: [PATCH 2/3] Add example application for C-code --- examples/c_supervised_app/BUILD | 34 +++++++++++ examples/c_supervised_app/main.c | 59 +++++++++++++++++++ examples/demo_verification/BUILD | 1 + .../lifecycle_demo_test.json | 15 +++++ examples/demo_verification/test_examples.py | 7 ++- 5 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 examples/c_supervised_app/BUILD create mode 100644 examples/c_supervised_app/main.c diff --git a/examples/c_supervised_app/BUILD b/examples/c_supervised_app/BUILD new file mode 100644 index 000000000..97deed6d6 --- /dev/null +++ b/examples/c_supervised_app/BUILD @@ -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 +# ******************************************************************************* +load("@rules_cc//cc:defs.bzl", "cc_binary") + +cc_binary( + name = "c_supervised_app", + srcs = [ + "main.c", + ], + linkopts = select({ + "@platforms//os:qnx": [ + "-lsocket", + ], + "@platforms//os:linux": [ + "-lpthread", + "-lrt", + ], + }), + visibility = ["//visibility:public"], + deps = [ + "//score/launch_manager:alive_cc", + "//score/launch_manager:lifecycle_cc", + ], +) diff --git a/examples/c_supervised_app/main.c b/examples/c_supervised_app/main.c new file mode 100644 index 000000000..d906ff40b --- /dev/null +++ b/examples/c_supervised_app/main.c @@ -0,0 +1,59 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include +#include +#include +#include + +#include +#include + +static volatile sig_atomic_t exit_requested = 0; + +static void signal_handler(int signal) +{ + if (signal == SIGINT || signal == SIGTERM) + { + exit_requested = 1; + } +} + +int main(void) +{ + signal(SIGINT, signal_handler); + signal(SIGTERM, signal_handler); + + void* alive = score_lcm_alive_initialize("/example/instance"); + if (alive == NULL) + { + fprintf(stderr, "Failed to initialize alive instance\n"); + return EXIT_FAILURE; + } + + if (score_lcm_ReportRunning() != 0) + { + fprintf(stderr, "Failed to report running state\n"); + score_lcm_alive_deinitialize(alive); + return EXIT_FAILURE; + } + + while (!exit_requested) + { + score_lcm_alive_report_alive(alive); + usleep(50000); + } + + score_lcm_alive_deinitialize(alive); + return EXIT_SUCCESS; +} diff --git a/examples/demo_verification/BUILD b/examples/demo_verification/BUILD index 91a0efdb6..540375b3b 100644 --- a/examples/demo_verification/BUILD +++ b/examples/demo_verification/BUILD @@ -24,6 +24,7 @@ launch_manager_config( pkg_files( name = "examples_main_files", srcs = [ + "//examples/c_supervised_app", "//examples/control_application:control_daemon", "//examples/control_application:lmcontrol", "//examples/cpp_lifecycle_app", diff --git a/examples/demo_verification/lifecycle_demo_test.json b/examples/demo_verification/lifecycle_demo_test.json index bd0a92645..0ad84cdb4 100644 --- a/examples/demo_verification/lifecycle_demo_test.json +++ b/examples/demo_verification/lifecycle_demo_test.json @@ -94,6 +94,20 @@ } } }, + "c_supervised_app": { + "component_properties": { + "binary_name": "c_supervised_app", + "application_profile": { + "application_type": "Reporting_And_Supervised" + } + }, + "deployment_config": { + "environmental_variables": { + "PROCESSIDENTIFIER": "c_supervised_app", + "IDENTIFIER": "c_supervised_app" + } + } + }, "lifecycle_app": { "component_properties": { "binary_name": "cpp_lifecycle_app" @@ -121,6 +135,7 @@ "control_daemon", "cpp_supervised_app", "rust_supervised_app", + "c_supervised_app", "lifecycle_app" ], "recovery_action": { diff --git a/examples/demo_verification/test_examples.py b/examples/demo_verification/test_examples.py index 1a1e28d92..ea617e149 100644 --- a/examples/demo_verification/test_examples.py +++ b/examples/demo_verification/test_examples.py @@ -15,7 +15,12 @@ from tests.utils.testing_utils.setup_test import setup_test from attribute_plugin import add_test_properties -_DEMO_APPS = ("cpp_supervised_app", "rust_supervised_app", "cpp_lifecycle_app") +_DEMO_APPS = ( + "cpp_supervised_app", + "rust_supervised_app", + "cpp_lifecycle_app", + "c_supervised_app", +) def _assert_running(target, *binaries): From eac2394b456fe410e20b634545dcafbe22ab04b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20Fu=C3=9Fberger?= Date: Wed, 1 Jul 2026 13:39:01 +0200 Subject: [PATCH 3/3] Apply suggested interface changes --- examples/c_supervised_app/main.c | 2 +- score/launch_manager/src/alive/src/alive.h | 4 ++-- .../src/lifecycle_client/src/report_running.cpp | 2 +- .../launch_manager/src/lifecycle_client/src/report_running.h | 2 +- .../src/lifecycle_client/src/rust/src/lifecycle.rs | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/c_supervised_app/main.c b/examples/c_supervised_app/main.c index d906ff40b..1fddc7321 100644 --- a/examples/c_supervised_app/main.c +++ b/examples/c_supervised_app/main.c @@ -41,7 +41,7 @@ int main(void) return EXIT_FAILURE; } - if (score_lcm_ReportRunning() != 0) + if (score_lcm_report_running() != 0) { fprintf(stderr, "Failed to report running state\n"); score_lcm_alive_deinitialize(alive); diff --git a/score/launch_manager/src/alive/src/alive.h b/score/launch_manager/src/alive/src/alive.h index 8e6daf967..3741d8cdc 100644 --- a/score/launch_manager/src/alive/src/alive.h +++ b/score/launch_manager/src/alive/src/alive.h @@ -32,7 +32,7 @@ class AliveImpl; /// it executes the configured recovery action. /// /// Each process may only use a single Alive instance. -class Alive +class Alive final { public: /// @brief Creation of an Alive. @@ -57,7 +57,7 @@ class Alive Alive& operator=(Alive&& se) noexcept; /// @brief Destructor of an Alive - virtual ~Alive() noexcept; + ~Alive() noexcept; /// @brief Reports an alive notification /// @remark Thread safety: This method is NOT thread safe. diff --git a/score/launch_manager/src/lifecycle_client/src/report_running.cpp b/score/launch_manager/src/lifecycle_client/src/report_running.cpp index 78956e2dd..ffd27a5c1 100644 --- a/score/launch_manager/src/lifecycle_client/src/report_running.cpp +++ b/score/launch_manager/src/lifecycle_client/src/report_running.cpp @@ -22,7 +22,7 @@ void score::mw::lifecycle::report_running() noexcept { extern "C" { #endif -int8_t score_lcm_ReportRunning(void) { +int8_t score_lcm_report_running(void) { // RULECHECKER_comment(1, 2, check_static_object_dynamic_initialization, "static variable is in function scope so this initialization is safe", false) static score::mw::lifecycle::ReportRunningImpl g_impl{}; const auto result = g_impl.ReportRunningState(); diff --git a/score/launch_manager/src/lifecycle_client/src/report_running.h b/score/launch_manager/src/lifecycle_client/src/report_running.h index ec8bfdce1..5b0ee04f1 100644 --- a/score/launch_manager/src/lifecycle_client/src/report_running.h +++ b/score/launch_manager/src/lifecycle_client/src/report_running.h @@ -54,7 +54,7 @@ extern "C" { /// @brief Signals to the Launch Manager that this process has finished initialization and is now running. /// @return 0 on success, -1 on failure -int8_t score_lcm_ReportRunning(void); +int8_t score_lcm_report_running(void); #ifdef __cplusplus } diff --git a/score/launch_manager/src/lifecycle_client/src/rust/src/lifecycle.rs b/score/launch_manager/src/lifecycle_client/src/rust/src/lifecycle.rs index c024e2a12..7c27fa3de 100644 --- a/score/launch_manager/src/lifecycle_client/src/rust/src/lifecycle.rs +++ b/score/launch_manager/src/lifecycle_client/src/rust/src/lifecycle.rs @@ -14,9 +14,9 @@ use libc::c_int; #[link(name = "report_running")] unsafe extern "C" { - fn score_lcm_ReportRunning() -> c_int; + fn score_lcm_report_running() -> c_int; } pub fn report_execution_state_running() -> bool { - unsafe { score_lcm_ReportRunning() == 0 } + unsafe { score_lcm_report_running() == 0 } }