Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,37 @@
load("@rules_cc//cc:defs.bzl", "cc_library")
load("//tests/utils/bazel:unit_test.bzl", "lm_cc_test")

cc_library(
name = "component",
srcs = ["component.cpp"],
hdrs = ["component.hpp"],
include_prefix = "score/mw/launch_manager/process_group_manager/details",
strip_include_prefix = "/score/launch_manager/src/daemon/src/process_group_manager/details",
visibility = ["//score/launch_manager/src/daemon/src/process_group_manager:__subpackages__"],
deps = [
":icomponent",
"//score/launch_manager/src/daemon/src/process_group_manager/details/force_stop_action:iforce_stop_action",
"//score/launch_manager/src/daemon/src/process_group_manager/details/ready_condition:iready_condition",
"//score/launch_manager/src/daemon/src/process_group_manager/details/start_action:istart_action",
"//score/launch_manager/src/daemon/src/process_group_manager/details/stop_action:istop_action",
"@score_baselibs//score/language/futurecpp",
],
)

lm_cc_test(
name = "component_UT",
srcs = ["component_UT.cpp"],
deps = [
":component",
":mock_component",
"//score/launch_manager/src/daemon/src/process_group_manager/details/force_stop_action:mock_force_stop_action",
"//score/launch_manager/src/daemon/src/process_group_manager/details/ready_condition:mock_ready_condition",
"//score/launch_manager/src/daemon/src/process_group_manager/details/start_action:mock_start_action",
"//score/launch_manager/src/daemon/src/process_group_manager/details/stop_action:mock_stop_action",
"@googletest//:gtest_main",
],
)

cc_library(
name = "component_task",
hdrs = ["component_task.hpp"],
Expand Down Expand Up @@ -49,6 +80,15 @@ cc_library(
],
)

cc_library(
name = "handle",
hdrs = ["handle.hpp"],
include_prefix = "score/mw/launch_manager/process_group_manager/details",
strip_include_prefix = "/score/launch_manager/src/daemon/src/process_group_manager/details",
visibility = ["//score/launch_manager/src/daemon/src/process_group_manager:__subpackages__"],
deps = ["//score/launch_manager/src/daemon/src/osal:ipc_comms"],
)

cc_library(
name = "mock_component",
testonly = True,
Expand Down Expand Up @@ -371,7 +411,7 @@ cc_library(
hdrs = ["process_launcher.hpp"],
include_prefix = "score/mw/launch_manager/process_group_manager/details",
strip_include_prefix = "/score/launch_manager/src/daemon/src/process_group_manager/details",
visibility = ["//score/launch_manager/src/daemon/src/process_group_manager:__pkg__"],
visibility = ["//score/launch_manager/src/daemon/src/process_group_manager:__subpackages__"],
deps = [
"//score/launch_manager/src/daemon/src/common:log",
"//score/launch_manager/src/daemon/src/common:signal_safe_log",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
/********************************************************************************
* Copyright (c) 2026 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Apache License Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
********************************************************************************/

#include "score/mw/launch_manager/process_group_manager/details/component.hpp"

namespace score::mw::lifecycle::internal
{

Component::Component(
const IStartAction& start_action,
const IStopAction& stop_action,
const IForceStopAction& force_stop_action,
const cpp::span<std::reference_wrapper<const IReadyCondition>> ready_conditions,
IdentifierHash identifier)
: start_action_(start_action),
stop_action_(stop_action),
force_stop_action_(force_stop_action),
ready_conditions_(ready_conditions),
identifier_(identifier),
state_(TerminatedState{})
{
}

IComponent::RequestResult Component::activate(cpp::stop_token stop_token)
{
return std::visit(
[&](auto state) {
return state.activate(*this, stop_token);
},
state_);
}

IComponent::RequestResult Component::TerminatedState::activate(Component& component, cpp::stop_token stop_token)
{
const Result<Handle> start_result = component.start_action_.start(stop_token);
if (!start_result.has_value())
{
return cpp::make_unexpected(ComponentError::kErrorBeforeReady);
}
const Handle handle = start_result.value();

component.state_ = StartingState{handle};

for (const IReadyCondition& ready_condition : component.ready_conditions_)
{
if (!ready_condition.wait(stop_token, handle).has_value())
{
return cpp::make_unexpected(ComponentError::kErrorBeforeReady);
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we'll need to decide how the supervision is handled.
I think a natural point would be to start supervision here after all ready conditions are fulfilled.

Currently, this will always translate to start supervision when kRunning reported.

Though for this we also need the timestamp from the kRunning report, as the supervision has to be started exactly at this timestamp.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it would be possible to have IpcCommsSync store the timestamp (as it should happen only once per instance). That is already stored inside ProcessHandle, so it would be accessible to the supervision.

component.state_ = ReadyState{handle};

return RequestState::kSuccess;
}

IComponent::RequestResult Component::StartingState::activate(
[[maybe_unused]] Component& component,
[[maybe_unused]] cpp::stop_token stop_token)
{
SCORE_LANGUAGE_FUTURECPP_UNREACHABLE_MESSAGE("Cannot activate component in StartingState");
}

IComponent::RequestResult Component::ReadyState::activate(
[[maybe_unused]] Component& component,
[[maybe_unused]] cpp::stop_token stop_token)
{
SCORE_LANGUAGE_FUTURECPP_UNREACHABLE_MESSAGE("Cannot activate component in ReadyState");
}

IComponent::RequestResult Component::TerminatingState::activate(
[[maybe_unused]] Component& component,
[[maybe_unused]] cpp::stop_token stop_token)
{
SCORE_LANGUAGE_FUTURECPP_UNREACHABLE_MESSAGE("Cannot activate component in TerminatingState");
}

IComponent::RequestResult Component::FaultState::activate(
[[maybe_unused]] Component& component,
[[maybe_unused]] cpp::stop_token stop_token)
{
SCORE_LANGUAGE_FUTURECPP_UNREACHABLE_MESSAGE("Cannot activate component in FaultState");
}

IComponent::RequestResult Component::deactivate(cpp::stop_token stop_token)
{
return std::visit(
[&](auto state) {
return state.deactivate(*this, stop_token);
},
state_);
}

IComponent::RequestResult Component::TerminatedState::deactivate(
[[maybe_unused]] Component& component,
[[maybe_unused]] cpp::stop_token stop_token)
{
SCORE_LANGUAGE_FUTURECPP_UNREACHABLE_MESSAGE("Cannot deactivate component in TerminatedState");
}

IComponent::RequestResult Component::StartingState::deactivate(Component& component, cpp::stop_token stop_token)
{
if (!component.stop_action_.stop(stop_token, handle_).has_value())
{
return cpp::make_unexpected(ComponentError::kErrorAfterReady);
}

component.state_ = TerminatingState{handle_};
return RequestState::kWaiting;
}

IComponent::RequestResult Component::ReadyState::deactivate(Component& component, cpp::stop_token stop_token)
{
if (!component.stop_action_.stop(stop_token, handle_).has_value())
{
return cpp::make_unexpected(ComponentError::kErrorAfterReady);
}

component.state_ = TerminatingState{handle_};
return RequestState::kWaiting;
}

IComponent::RequestResult Component::TerminatingState::deactivate(
[[maybe_unused]] Component& component,
[[maybe_unused]] cpp::stop_token stop_token)
{
SCORE_LANGUAGE_FUTURECPP_UNREACHABLE_MESSAGE("Cannot deactivate component in TerminatingState");
}

IComponent::RequestResult Component::FaultState::deactivate(
[[maybe_unused]] Component& component,
[[maybe_unused]] cpp::stop_token stop_token)
{
SCORE_LANGUAGE_FUTURECPP_UNREACHABLE_MESSAGE("Cannot deactivate component in FaultState");
}

IComponent::RequestResult Component::tryHandleTermination(int32_t status)
{
if (status == 0)
{
state_ = TerminatedState{};
}
else
{
state_ = FaultState{};
}

return RequestState::kSuccess;
}

IdentifierHash Component::getIdentifier() const
{
return identifier_;
}

bool Component::active() const
{
return std::holds_alternative<ReadyState>(state_);
}

} // namespace score::mw::lifecycle::internal
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/********************************************************************************
* Copyright (c) 2026 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Apache License Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
********************************************************************************/

#ifndef SCORE_LCM_COMPONENT_HPP_INCLUDED
#define SCORE_LCM_COMPONENT_HPP_INCLUDED

#include "score/mw/launch_manager/process_group_manager/details/force_stop_action/iforce_stop_action.hpp"
#include "score/mw/launch_manager/process_group_manager/details/icomponent.hpp"
#include "score/mw/launch_manager/process_group_manager/details/ready_condition/iready_condition.hpp"
#include "score/mw/launch_manager/process_group_manager/details/start_action/istart_action.hpp"
#include "score/mw/launch_manager/process_group_manager/details/stop_action/istop_action.hpp"
#include <score/span.hpp>
#include <functional>
#include <vector>

namespace score::mw::lifecycle::internal
{

/// @brief Manages and tracks the state of a resource.
class Component final : public IComponent
{
public:
/// @brief Creates a new component from the given actions.
/// @param start_action How to start the resource.
/// @param stop_action How to gracefully stop the resource.
/// @param force_stop_action How to forcefully stop the resource.
/// @param ready_conditions How to decide when the resource has finished its startup.
/// @param identifier Name of the component.
Component(
const IStartAction& start_action,
const IStopAction& stop_action,
const IForceStopAction& force_stop_action,
cpp::span<std::reference_wrapper<const IReadyCondition>> ready_conditions =
cpp::span<std::reference_wrapper<const IReadyCondition>>{},
IdentifierHash identifier = IdentifierHash{});

/// @brief Start the resource and set the component to active.
/// @param stop_token Token which can be used to interrupt the activation.
/// @return Whether the component activation was successful, is waiting for another
/// thread, or an error was encountered.
RequestResult activate(cpp::stop_token stop_token) override;

/// @brief Stop the resource and set the component to inactive.
/// @param stop_token Token which can be used to interrupt the deactivation.
/// @return Whether the component deactivation was successful, is waiting for another
/// thread, or an error was encountered.
RequestResult deactivate(cpp::stop_token stop_token) override;

/// @brief Notify the component that a POSIX process has terminated.
/// @param status Exit code of the process.
/// @return Whether the component activation was successful, is waiting for another
/// thread, or an error was encountered.
RequestResult tryHandleTermination(int32_t status) override;

/// @brief Return the name of the component.
/// @return The name of the component.
[[nodiscard]] IdentifierHash getIdentifier() const override;

/// @brief Return whether the component is active.
/// @return Whether the component is active.
[[nodiscard]] bool active() const override;

private:
/// @brief How to start the resource.
const IStartAction& start_action_;

/// @brief How to gracefully stop the resource.
const IStopAction& stop_action_;

/// @brief How to forcefully stop the resource.
const IForceStopAction& force_stop_action_;

/// @brief How to decide when the resource has finished its startup.
const cpp::span<std::reference_wrapper<const IReadyCondition>> ready_conditions_;

/// @brief Name of the component.
const IdentifierHash identifier_;

class TerminatedState final
{
public:
RequestResult activate(Component& component, cpp::stop_token stop_token);
RequestResult deactivate(Component& component, cpp::stop_token stop_token);
};

class StartingState final
{
public:
Handle handle_;
RequestResult activate(Component& component, cpp::stop_token stop_token);
RequestResult deactivate(Component& component, cpp::stop_token stop_token);
};

class ReadyState final
{
public:
Handle handle_;
RequestResult activate(Component& component, cpp::stop_token stop_token);
RequestResult deactivate(Component& component, cpp::stop_token stop_token);
};

class TerminatingState final
{
public:
Handle handle_;
RequestResult activate(Component& component, cpp::stop_token stop_token);
RequestResult deactivate(Component& component, cpp::stop_token stop_token);
};

class FaultState final
{
public:
Handle handle_;
RequestResult activate(Component& component, cpp::stop_token stop_token);
RequestResult deactivate(Component& component, cpp::stop_token stop_token);
};

/// @brief The current state of the component.
std::variant<TerminatedState, StartingState, ReadyState, TerminatingState, FaultState> state_;
};

} // namespace score::mw::lifecycle::internal

#endif // SCORE_LCM_COMPONENT_HPP_INCLUDED
Loading
Loading