|
use std::time::Duration; |
|
use tokio::signal::unix::{signal, SignalKind}; |
|
use tokio::time::sleep; |
|
use tokio_util::sync::CancellationToken; |
|
|
|
fn main() { |
|
let rt = tokio::runtime::Builder::new_current_thread() |
|
.enable_all() |
|
.build() |
|
.unwrap(); |
|
|
|
let code = rt.block_on(async { |
|
let cancel = CancellationToken::new(); |
|
let engine_stopped = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false)); |
|
let engine_stopped_clone = engine_stopped.clone(); |
|
|
|
// App task: blocks until cancelled, returns immediately. |
|
let app = tokio::spawn({ |
|
let cancel = cancel.clone(); |
|
async move { |
|
cancel.cancelled().await; |
|
// No savepoint here — the real app.rs does one, but |
|
// the key question is whether the handler's engine stop fires. |
|
} |
|
}); |
|
|
|
// Install the EXACT same handler from node.rs:826-867. |
|
let mut sigterm = signal(SignalKind::terminate()).unwrap(); |
|
tokio::spawn(async move { |
|
sigterm.recv().await; |
|
eprintln!("[handler] SIGTERM received, cancelling..."); |
|
cancel.cancel(); |
|
sleep(Duration::from_millis(500)).await; // mimic same sleep in the originan SIGTERM handler |
|
eprintln!("[handler] stopping engine..."); |
|
engine_stopped_clone.store(true, std::sync::atomic::Ordering::SeqCst); |
|
eprintln!("[handler] savepoint..."); |
|
eprintln!("[handler] Shutdown complete, exiting"); |
|
std::process::exit(143); // if this executes, exit code = 143 |
|
}); |
|
|
|
// Give the handler time to install the signal listener. |
|
tokio::time::sleep(Duration::from_millis(100)).await; |
|
|
|
// Send SIGTERM to ourselves. |
|
let pid = std::process::id(); |
|
eprintln!("[main] sending SIGTERM to {pid}"); |
|
std::process::Command::new("kill") |
|
.args(["-TERM", &pid.to_string()]) |
|
.spawn() |
|
.unwrap() |
|
.wait() |
|
.unwrap(); |
|
|
|
// Wait for the app task to resolve (happens instantly on cancel). |
|
app.await.unwrap(); |
|
eprintln!("[main] app returned, returning from block_on"); |
|
|
|
// Return whether the engine was stopped before the runtime drops. |
|
engine_stopped.load(std::sync::atomic::Ordering::SeqCst) |
|
}); |
|
|
|
// rt is dropped here, the SIGTERM handler task is killed if still alive. |
|
// If the handler's process::exit(143) fired, we never reach this line. |
|
eprintln!("[main] runtime dropped, engine_stopped = {code}"); |
|
if code { |
|
std::process::exit(0); // unexpected: handler finished in time |
|
} else { |
|
eprintln!("[main] SIGTERM handler killed before stopping engine"); |
|
std::process::exit(0); // expected: handler killed before stopping engine (BUG Valid) |
|
} |
|
} |