Skip to main content

modkit/runtime/
shutdown.rs

1use anyhow::Result;
2
3/// Wait for a shutdown signal (SIGTERM, SIGINT, or Ctrl+C).
4///
5/// # Errors
6/// Returns an error if signal handling fails.
7pub async fn wait_for_shutdown() -> Result<()> {
8    #[cfg(unix)]
9    {
10        use tokio::signal::unix::{SignalKind, signal};
11        let mut sigterm = signal(SignalKind::terminate())?;
12        let mut sigint = signal(SignalKind::interrupt())?; // Ctrl+C
13        tokio::select! {
14            _ = sigterm.recv() => {},
15            _ = sigint.recv()  => {},
16            _ = tokio::signal::ctrl_c() => {}, // fallback
17        }
18        Ok(())
19    }
20
21    #[cfg(windows)]
22    {
23        use tokio::signal::windows::{ctrl_break, ctrl_c, ctrl_close, ctrl_logoff, ctrl_shutdown};
24        use tokio::time::{Duration, timeout};
25
26        async fn arm_once() -> std::io::Result<()> {
27            // create signal listeners first
28            let mut c = ctrl_c()?;
29            let mut br = ctrl_break()?;
30            let mut cl = ctrl_close()?;
31            let mut lo = ctrl_logoff()?;
32            let mut sh = ctrl_shutdown()?;
33
34            tokio::select! {
35                _ = c.recv()  => {},
36                _ = br.recv() => {},
37                _ = cl.recv() => {},
38                _ = lo.recv() => {},
39                _ = sh.recv() => {},
40            }
41            Ok(())
42        }
43
44        // Debounce: if a “signal” fires within 50ms after arming, ignore first and wait again.
45        match timeout(Duration::from_millis(50), arm_once()).await {
46            Ok(Ok(())) => {
47                tracing::warn!("shutdown: early Windows console signal detected; debouncing");
48                arm_once().await?; // wait for a real one
49            }
50            Ok(Err(e)) => return Err(e.into()),
51            Err(_elapsed) => {
52                // no early signal — just wait normally
53                arm_once().await?;
54            }
55        }
56        Ok(())
57    }
58}