fix(process): eliminate stop-signal registration race
Install SIGINT and SIGTERM handlers synchronously before publishing process readiness. Propagate registration failures through Myelin and demo startup instead of silently leaving the default signal action active.
This commit is contained in:
parent
02701280ed
commit
74ba89c0ce
4 changed files with 16 additions and 10 deletions
|
|
@ -2375,7 +2375,8 @@ fn spawn_stop_listener(
|
|||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
swactor_process::spawn_os_stop_signal_wait(sender, actor);
|
||||
swactor_process::spawn_os_stop_signal_wait(sender, actor)
|
||||
.map_err(|error| format!("install orchestrator stop signal handler: {error}"))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -147,19 +147,22 @@ fn spawn_stop_channel_wait_thread(
|
|||
}
|
||||
|
||||
/// Wait for SIGINT/SIGTERM and notify an actor once.
|
||||
///
|
||||
/// Signal handlers are installed before this function returns. A caller may
|
||||
/// therefore publish readiness immediately after a successful return without
|
||||
/// racing the operating system's default signal action.
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn spawn_os_stop_signal_wait(sender: ExternalSender, actor: ActorAddress) {
|
||||
pub fn spawn_os_stop_signal_wait(sender: ExternalSender, actor: ActorAddress) -> io::Result<()> {
|
||||
let mut signals = signal_hook::iterator::Signals::new([
|
||||
signal_hook::consts::signal::SIGINT,
|
||||
signal_hook::consts::signal::SIGTERM,
|
||||
])?;
|
||||
thread::spawn(move || {
|
||||
let Ok(mut signals) = signal_hook::iterator::Signals::new([
|
||||
signal_hook::consts::signal::SIGINT,
|
||||
signal_hook::consts::signal::SIGTERM,
|
||||
]) else {
|
||||
return;
|
||||
};
|
||||
if signals.forever().next().is_some() {
|
||||
let _ = sender.send_to(actor, ProcessStopSignal);
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
|
|
|
|||
|
|
@ -503,7 +503,8 @@ fn run_supervisor(args: &[String]) -> Result<(), String> {
|
|||
})
|
||||
.map_err(|error| format!("spawn supervisor stop actor: {error}"))?;
|
||||
#[cfg(target_os = "linux")]
|
||||
swactor_process::spawn_os_stop_signal_wait(runtime.create_sender(), stop_actor);
|
||||
swactor_process::spawn_os_stop_signal_wait(runtime.create_sender(), stop_actor)
|
||||
.map_err(|error| format!("install supervisor stop signal handler: {error}"))?;
|
||||
println!("demo: dashboard on http://localhost:{port}");
|
||||
println!(" /view/fleet — per-node cards (pid, lifecycle)");
|
||||
println!(" /view/demo-control — Fleet Control: stages, feeds, kill / provision / edge");
|
||||
|
|
|
|||
|
|
@ -256,7 +256,8 @@ pub fn run_node_role(supervisor_addr_json: &str, attempt: u64) -> Result<(), Str
|
|||
})
|
||||
.map_err(|error| format!("spawn node stop actor: {error}"))?;
|
||||
#[cfg(target_os = "linux")]
|
||||
swactor_process::spawn_os_stop_signal_wait(runtime.create_sender(), stop_actor);
|
||||
swactor_process::spawn_os_stop_signal_wait(runtime.create_sender(), stop_actor)
|
||||
.map_err(|error| format!("install node stop signal handler: {error}"))?;
|
||||
|
||||
// The actor owns lifecycle; the entrypoint waits only for its terminal signal.
|
||||
completion.wait();
|
||||
|
|
|
|||
Loading…
Reference in a new issue