Skip to content
2 changes: 1 addition & 1 deletion examples/cpp_lifecycle_app/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,6 @@ cc_binary(
}),
visibility = ["//visibility:public"],
deps = [
"//src/launch_manager_daemon/lifecycle_client_lib:lifecycle_client",
"//src/lifecycle_client_lib",
],
)
164 changes: 104 additions & 60 deletions examples/cpp_lifecycle_app/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,19 @@
#include <unistd.h>
#include <csignal>
#include <atomic>
#include <thread>
#include <iostream>
#include <ctime>
#include <string>
#include <chrono>
#include <vector>

#ifdef __linux__
#include <linux/prctl.h>
#include <sys/prctl.h>
#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
Expand Down Expand Up @@ -53,18 +55,15 @@ std::optional<Config> parseOptions(int argc, char *const *argv) noexcept
switch (static_cast<char>(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;

Expand Down Expand Up @@ -95,86 +94,131 @@ void signalHandler(int)
exitRequested = true;
}

void set_process_name() {
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
}
}

int main(int argc, char **argv)
class LifecycleApp final : public score::mw::lifecycle::Application
{
set_process_name();
public:
std::int32_t Initialize(const score::mw::lifecycle::ApplicationContext& appCtx) override
{
set_process_name();

signal(SIGINT, signalHandler);
signal(SIGTERM, signalHandler);
signal(SIGINT, signalHandler);
signal(SIGTERM, signalHandler);

const auto config = parseOptions(argc, argv);
if (!config)
{
return EXIT_FAILURE;
}
// Build a classic argv for getopt() from ApplicationContext arguments
const auto& args = appCtx.get_arguments();
m_argvStorage.clear();
m_argvStorage.reserve(args.size() + 2);

std::chrono::time_point<std::chrono::steady_clock> startTime = std::chrono::steady_clock::now();
std::chrono::duration<double, std::milli> runTime;
// Ensure argv[0] exists (getopt expects it)
if (args.empty())
{
m_argvStorage.push_back(const_cast<char*>("LifecycleApp"));
}
else
{
for (const auto& s : args)
{
// NOTE: relies on the underlying storage staying alive during Initialize().
m_argvStorage.push_back(const_cast<char*>(s.data()));
}
}

if (true == config->failToStart)
{
return EXIT_FAILURE;
}
m_argvStorage.push_back(nullptr);

score::lcm::LifecycleClient{}.ReportExecutionState(score::lcm::ExecutionState::kRunning);
optind = 1;

timespec req{
static_cast<time_t>(config->responseTimeInMs / 1000),
static_cast<long>((config->responseTimeInMs % 1000) * 1000000L)
};
auto timeLastVerboseLog = std::chrono::steady_clock::now();
while (!exitRequested)
{
if (true == config->crashRequested)
const int argcLocal = static_cast<int>(m_argvStorage.size() - 1);
const auto config = parseOptions(argcLocal, m_argvStorage.data());
if (!config)
{
return EXIT_FAILURE;
}

m_config = *config;

if (true == m_config.failToStart)
{
runTime = std::chrono::steady_clock::now() - startTime;
return EXIT_FAILURE;
}

int timeTillCrash = static_cast<int>(config->crashTimeInMs - runTime.count());
return 0;
}

if (timeTillCrash < config->responseTimeInMs)
std::int32_t Run(const score::cpp::stop_token& stopToken) override
{
std::chrono::time_point<std::chrono::steady_clock> startTime = std::chrono::steady_clock::now();
std::chrono::duration<double, std::milli> runTime;

timespec req{
static_cast<time_t>(m_config.responseTimeInMs / 1000),
static_cast<long>((m_config.responseTimeInMs % 1000) * 1000000L)
};

auto timeLastVerboseLog = std::chrono::steady_clock::now();

while (!exitRequested && !stopToken.stop_requested())
{
if (true == m_config.crashRequested)
{
// OK we need a shorter sleep now
if (timeTillCrash > 0)
runTime = std::chrono::steady_clock::now() - startTime;
int timeTillCrash = static_cast<int>(m_config.crashTimeInMs - runTime.count());

if (timeTillCrash < m_config.responseTimeInMs)
{
timespec crash_req{
static_cast<time_t>(timeTillCrash / 1000),
static_cast<long>((timeTillCrash % 1000) * 1000000L)
};
nanosleep(&crash_req, nullptr);
if (timeTillCrash > 0)
{
timespec crash_req{
static_cast<time_t>(timeTillCrash / 1000),
static_cast<long>((timeTillCrash % 1000) * 1000000L)
};
nanosleep(&crash_req, 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_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;
}
}

nanosleep(&req, nullptr);
}

nanosleep(&req, nullptr);
return EXIT_SUCCESS;
}

// normal exit
return EXIT_SUCCESS;
private:
Config m_config{};
std::vector<char*> m_argvStorage{};
};

int main(int argc, char** argv)
{
return score::mw::lifecycle::run_application<LifecycleApp>(argc, argv);
}
2 changes: 2 additions & 0 deletions src/lifecycle_client_lib/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion src/lifecycle_client_lib/include/lifecyclemanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down
3 changes: 2 additions & 1 deletion src/lifecycle_client_lib/include/runapplication.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ template <typename ApplicationType, typename... Args>
/* 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<ApplicationType> runner(argc, argv);
return runner.AsPosixProcess(std::forward<Args>(args)...);
}

} // namespace lifecycle
Expand Down
12 changes: 11 additions & 1 deletion src/lifecycle_client_lib/src/lifecyclemanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

#include "score/os/errno.h"
#include <cstdlib>

#include "score/lcm/lifecycle_client.h"
#include "score/mw/log/logging.h"
#include "score/os/stdlib_impl.h"

Expand Down Expand Up @@ -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();
}
}
Loading