mecha10_runtime/
shutdown.rs

1//! Graceful shutdown handling
2
3use tokio::sync::broadcast;
4
5/// Handle for triggering and waiting for graceful shutdown
6#[derive(Clone)]
7pub struct ShutdownHandle {
8    tx: broadcast::Sender<()>,
9}
10
11impl ShutdownHandle {
12    /// Create a new shutdown handle
13    pub fn new() -> Self {
14        let (tx, _) = broadcast::channel(1);
15        Self { tx }
16    }
17
18    /// Trigger shutdown
19    pub fn shutdown(&self) {
20        // Ignore error if no receivers
21        let _ = self.tx.send(());
22    }
23
24    /// Wait for shutdown signal
25    pub async fn wait_shutdown(&self) {
26        let mut rx = self.tx.subscribe();
27        let _ = rx.recv().await;
28    }
29
30    /// Subscribe to shutdown signals
31    pub fn subscribe(&self) -> broadcast::Receiver<()> {
32        self.tx.subscribe()
33    }
34}
35
36impl Default for ShutdownHandle {
37    fn default() -> Self {
38        Self::new()
39    }
40}
41
42/// Wait for Ctrl+C signal
43pub async fn wait_for_signal() {
44    #[cfg(unix)]
45    {
46        use tokio::signal::unix::{signal, SignalKind};
47        let mut sigterm = signal(SignalKind::terminate()).expect("Failed to setup SIGTERM handler");
48        let mut sigint = signal(SignalKind::interrupt()).expect("Failed to setup SIGINT handler");
49
50        tokio::select! {
51            _ = sigterm.recv() => {
52                tracing::info!("Received SIGTERM, shutting down gracefully");
53            }
54            _ = sigint.recv() => {
55                tracing::info!("Received SIGINT, shutting down gracefully");
56            }
57        }
58    }
59
60    #[cfg(not(unix))]
61    {
62        tokio::signal::ctrl_c().await.expect("Failed to listen for Ctrl+C");
63        tracing::info!("Received Ctrl+C, shutting down gracefully");
64    }
65}