Add tests which are verifying comp_req__launch_man__launcher_exit_shutdown - #450
Add tests which are verifying comp_req__launch_man__launcher_exit_shutdown#450TimoSteuerwaldETAS wants to merge 14 commits into
Conversation
License Check Results🚀 The license check job ran with the Bazel command: bazel run --lockfile_mode=error //:license-checkStatus: Click to expand output |
f064618 to
bee9de9
Compare
|
The created documentation from the pull request is available at: docu-html |
66c58a4 to
3494414
Compare
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
Other ControlClientMocks need to be updated separately.
3494414 to
3f23834
Compare
WilliamRoebuck
left a comment
There was a problem hiding this comment.
I have a few suggestions, although many are about the length of inline comments 😆
| // 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. |
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
| /// @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 |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
| /// @return The configured shutdown_timeout (termination_timeout_ms_) for this process, or zero | |
| /// @return The configured shutdown_timeout for this process, or zero |
| // 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(); |
There was a problem hiding this comment.
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"; |
There was a problem hiding this comment.
Should be debug level I think
There was a problem hiding this comment.
I would stick at least to info level, as this is a major state change to the whole launch manager.
| # 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" |
There was a problem hiding this comment.
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
| /// @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). |
There was a problem hiding this comment.
This can probably be 2 lines also
| _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" |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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
| /// 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. |
There was a problem hiding this comment.
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
Part of #317
comp_req__launch_man__launcher_exit_shutdown
The
Launch Managershall exit after performing shutdown operation bystopping all the
Processes <Process>it owns in the dependency order when requested.