Skip to content

Instantly share code, notes, and snippets.

@ikwuoz
Last active June 30, 2026 15:12
Show Gist options
  • Select an option

  • Save ikwuoz/56dee7adf0a4fdd9a38bf3a0547dcda4 to your computer and use it in GitHub Desktop.

Select an option

Save ikwuoz/56dee7adf0a4fdd9a38bf3a0547dcda4 to your computer and use it in GitHub Desktop.
Minimal rust bin snippet for sigterm bug validation
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)
}
}

Steps to execute:

  1. Create a throwaway binary cargo init /tmp/sigterm-test cd /tmp/sigterm-test

  2. Add these to Cargo.toml section

# [dependencies]
# tokio = { version = "1", features = ["rt", "macros", "signal", "time"] }
# tokio-util = { version = "0.7.18" }
  1. Copy the code above into src/main.rs, then: cargo run echo "Exit code: $?"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment