Skip to content

Add tests which are verifying comp_req__launch_man__launcher_exit_shutdown - #450

Open
TimoSteuerwaldETAS wants to merge 14 commits into
eclipse-score:mainfrom
etas-contrib:feature/shutdown_tests
Open

Add tests which are verifying comp_req__launch_man__launcher_exit_shutdown#450
TimoSteuerwaldETAS wants to merge 14 commits into
eclipse-score:mainfrom
etas-contrib:feature/shutdown_tests

Conversation

@TimoSteuerwaldETAS

@TimoSteuerwaldETAS TimoSteuerwaldETAS commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Part of #317

comp_req__launch_man__launcher_exit_shutdown
The Launch Manager shall exit after performing shutdown operation by
stopping all the Processes <Process> it owns in the dependency order when requested.

@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown

License Check Results

🚀 The license check job ran with the Bazel command:

bazel run --lockfile_mode=error //:license-check

Status: ⚠️ Needs Review

Click to expand output
[License Check Output]
Extracting Bazel installation...
Starting local Bazel server (8.6.0) and connecting to it...
INFO: Invocation ID: b5d213cb-2572-42e6-b9e7-f03d5d5c42bd
Computing main repo mapping: 
Computing main repo mapping: 
Loading: 
Loading: 0 packages loaded
Loading: 0 packages loaded
Loading: 0 packages loaded
    currently loading: 
Loading: 0 packages loaded
    currently loading: 
Loading: 0 packages loaded
    currently loading: 
Loading: 0 packages loaded
    currently loading: 
Loading: 0 packages loaded
    currently loading: 
Loading: 0 packages loaded
    currently loading: 
Analyzing: target //:license-check (1 packages loaded, 0 targets configured)
Analyzing: target //:license-check (1 packages loaded, 0 targets configured)

Analyzing: target //:license-check (35 packages loaded, 10 targets configured)

Analyzing: target //:license-check (89 packages loaded, 10 targets configured)

Analyzing: target //:license-check (147 packages loaded, 1130 targets configured)

Analyzing: target //:license-check (159 packages loaded, 5477 targets configured)

Analyzing: target //:license-check (169 packages loaded, 6334 targets configured)

Analyzing: target //:license-check (171 packages loaded, 7357 targets configured)

Analyzing: target //:license-check (176 packages loaded, 11463 targets configured)

Analyzing: target //:license-check (176 packages loaded, 11463 targets configured)

INFO: Analyzed target //:license-check (177 packages loaded, 11589 targets configured).
INFO: Found 1 target...
Target //:license.check.license_check up-to-date:
  bazel-bin/license.check.license_check
  bazel-bin/license.check.license_check.jar
INFO: Elapsed time: 24.216s, Critical Path: 0.28s
INFO: 16 processes: 4 disk cache hit, 12 internal.
INFO: Build completed successfully, 16 total actions
INFO: Running command line: bazel-bin/license.check.license_check ./formatted.txt <args omitted>
usage: org.eclipse.dash.licenses.cli.Main [-batch <int>] [-cd <url>]
       [-confidence <int>] [-ef <url>] [-excludeSources <sources>] [-help] [-lic
       <url>] [-project <shortname>] [-repo <url>] [-review] [-summary <file>]
       [-timeout <seconds>] [-token <token>]

@github-actions

Copy link
Copy Markdown

The created documentation from the pull request is available at: docu-html

These commits have been squashed together to ease review.
After cherry pick some conflicts have been solved wrong, this is why
there are two additional commits also.

For details please also have a look onto the internal branch in etas-eng
feature/create-process-launch-fit-specification-unverified

The original commit message of this commit:

Add a log message for SIGTERM receival

Move dir tests/integration/lm_shutdown to tests/integration/lm_shutdown_during_rt_switch

Update references accordingly

Add lm_shutdown_during_switch_to_off

A test which verifies that a launch manager shutdown signalled via SIGTERM does not cancel existent switches to off.
Currently does not fail, but should fail. As switch to off gets cancelled and reinitiated by launch manager code.

Let test fail, as switch to off is cancelled by lm

Revert to 1s timeout for lm shutdown & add some comments

Still not 100% stable.

Fix cherry pick odyssey

Former fix for SIGSEGV of old branch
@TimoSteuerwaldETAS
TimoSteuerwaldETAS deployed to workflow-approval August 13, 2026 14:58 — with GitHub Actions Active
@TimoSteuerwaldETAS
TimoSteuerwaldETAS deployed to workflow-approval August 13, 2026 14:58 — with GitHub Actions Active
@TimoSteuerwaldETAS TimoSteuerwaldETAS changed the title Add tests which are verifying comp_req__lifecycle__launcher_exit_shutdown Add tests which are verifying comp_req__launch_man__launcher_exit_shutdown Aug 14, 2026

@WilliamRoebuck WilliamRoebuck left a comment

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 have a few suggestions, although many are about the length of inline comments 😆

Comment on lines +554 to +558
// Only processes with a live OS process still to stop actually consume the
// SIGTERM+timeout wait during the transition to Off. Already terminated/failed
// processes short-circuit deactivation, and never-started nodes (pid == 0) have
// nothing to wait for, so neither should inflate the bound. This excludes
// one-shot / ready-on-termination processes that have already exited.

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.

This comment should be removed, it makes the code seem more complex than it is

/// @details The largest configured shutdown_timeout (termination_timeout_ms_) across the process
/// nodes of this process group that still have a live process to stop (pid > 0 and not yet
/// terminated), or zero if there are none.
/// @return The timeout in milliseconds, or zero if there are no live processes to stop.

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.

Suggested change
/// @return The timeout in milliseconds, or zero if there are no live processes to stop.
/// @brief Returns the largest configured shutdown_timeout across all running processes

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'm not sure if it shows up my suggested change but I think the @details section should be removed

/// @return The current state of this process.
score::mw::lifecycle::ProcessState getState() const;

/// @return The configured shutdown_timeout (termination_timeout_ms_) for this process, or zero

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.

Suggested change
/// @return The configured shutdown_timeout (termination_timeout_ms_) for this process, or zero
/// @return The configured shutdown_timeout for this process, or zero

Comment on lines +157 to +166
// Stop and join the worker threads BEFORE destroying the process groups.
// Worker threads run ProcessInfoNode::doWork(), which dereferences its Graph
// (nodeExecuted(), getState(), ...) via a raw back-pointer. If a transition is
// still completing on a worker thread (e.g. an in-progress switch to Off that
// is allowed to continue during shutdown), destroying the graphs first would be
// a use-after-free.
worker_threads_.reset();
worker_jobs_.reset();

process_groups_.clear();

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 this might not be necessary. The situation the comment describes no longer exists and the worker threads don't have any access to Graph


watchdog_->serviceWatchdog();
}
LM_LOG_WARN() << "ProcessGroupManager::run() - received SIGTERM, exiting";

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.

Should be debug level I think

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I would stick at least to info level, as this is a major state change to the whole launch manager.

Comment on lines +108 to +129
# Wait until the switch to run_target_c is underway: component_a is being
# terminated (and is now stalling), so run_target_c has not been activated
# yet. This state is signalled via file a_terminating.
# This is the window in which the shutdown request must win.
_wait_for_file(target, a_terminating, proc, timeout_s=10.0)

# run_target_c must not have started yet at this point.
exit_code, _ = target.execute(f"test -f {c_started}")
# The assertion below could only fail if either the sleep in component_a's termination code is too short
# or component_a has been killed by launch manager, because it takes too long to react on SIGTERM.
assert exit_code != 0, (
"run_target_c was activated before shutdown was requested - this should not happen"
)

# Request shutdown: send SIGTERM to the launch manager process only, so
# that the launch manager itself stops the processes it owns (rather than
# the OS terminating the whole process group directly).
lm_pid = _launch_manager_pid(target)
assert lm_pid is not None, "Could not find the running launch_manager process"
logger.info(f"Sending SIGTERM to launch_manager (pid {lm_pid})")
exit_code, _ = target.execute(f"kill -15 {lm_pid}")
assert exit_code == 0, "Failed to send SIGTERM to the launch manager"

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.

Would it be possible to achieve the same behaviour simply using the normal run until file deployed method? This sends a sigterm to lm and lets it terminate gracefully

Comment on lines +22 to +32
/// @brief How long component_a stalls while it is being terminated. By stalling here, component_a keeps the switch (to
/// the "Off" run target) in the STOP phase, giving the test a deterministic window to send SIGTERM to the launch
/// manager while the switch to Off is still in progress.
///
/// It must be comfortably larger than the time the test needs to observe
/// `a_terminating` and deliver the SIGTERM to the launch manager, so the switch
/// to Off is still in progress when that SIGTERM arrives. It must also be
/// comfortably smaller than component_a's configured shutdown_timeout: the launch
/// manager honours that per-process shutdown_timeout during its own shutdown, so
/// component_a is given time to exit on its own and terminates gracefully (and
/// writes its XML result) rather than being force-terminated (SIGKILLed).

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.

This can probably be 2 lines also

Comment on lines +119 to +128
_wait_for_file(target, a_terminating, proc, timeout_s=10.0)

# Request shutdown: send SIGTERM to the launch manager process only, so
# that the launch manager itself stops the processes it owns (rather than
# the OS terminating the whole process group directly).
lm_pid = _launch_manager_pid(target)
assert lm_pid is not None, "Could not find the running launch_manager process"
logger.info(f"Sending SIGTERM to launch_manager (pid {lm_pid})")
exit_code, _ = target.execute(f"kill -15 {lm_pid}")
assert exit_code == 0, "Failed to send SIGTERM to the launch manager"

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.

Same comment as the other python file, hopefully we can remove these two methods

ASSERT_TRUE(check_clean({test_end_location, a_started, a_terminating}));

const auto pid = getpid();
const std::string step_msg = "Report running with pid == " + std::to_string(pid);

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.

Please don't rely on log messages for tests! If we use the normal shutdown procedure as I mentioned in the other test's python file, this shouldn't be needed

Comment on lines +175 to +183
/// Poll the atomic flag instead of calling pause() directly. A process-directed
/// signal (from kill) is delivered to an arbitrary one of the process's threads
/// that has the signal unblocked. If the test process has more than one thread
/// (e.g. a ControlClient, whose constructor spawns a background IPC thread), the
/// handler may run on a background thread and set exitRequested there. pause()
/// only returns for a signal handled on the calling thread, so a main thread
/// blocked in pause() would not wake - it would stay parked until the launch
/// manager SIGKILLs it at its shutdown_timeout, and never write its XML result.
/// Polling observes the flag regardless of which thread handled the signal.

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.

This doesn't sound correct to me. If the child thread caught the signal, would exit requested actually be set? I ran the test using the normal pause() 450 times and didn't get any failures. I would leave these changes out of this PR and create an issue if something pops up again

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

2 participants