Skip to main content

github_mcp/core/
shutdown_handler.rs

1// GitHub v3 REST API MCP server — generated by mcpify. Do not hand-edit.
2
3use std::future::Future;
4use std::pin::Pin;
5use std::sync::Mutex;
6
7pub type CleanupFn = Box<dyn Fn() -> Pin<Box<dyn Future<Output = ()> + Send>> + Send + Sync>;
8
9static CLEANUP_FNS: Mutex<Vec<CleanupFn>> = Mutex::new(Vec::new());
10
11/// Registers a cleanup callback, run in reverse registration order during
12/// shutdown.
13pub fn on_shutdown(f: CleanupFn) {
14    CLEANUP_FNS.lock().unwrap().push(f);
15}
16
17async fn shutdown(signal: &str) {
18    tracing::info!(signal, "shutting down");
19
20    let fns: Vec<CleanupFn> = {
21        let mut guard = CLEANUP_FNS.lock().unwrap();
22        std::mem::take(&mut *guard)
23    };
24    for f in fns.into_iter().rev() {
25        f().await;
26    }
27
28    std::process::exit(0);
29}
30
31/// Installs SIGINT/SIGTERM handlers for graceful termination — REQ-2.3.4.
32/// Spawns its own task; callers don't await this.
33pub fn install_shutdown_handlers() {
34    tokio::spawn(async {
35        #[cfg(unix)]
36        {
37            let mut terminate =
38                tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate())
39                    .expect("failed to install SIGTERM handler");
40            tokio::select! {
41                _ = tokio::signal::ctrl_c() => shutdown("SIGINT").await,
42                _ = terminate.recv() => shutdown("SIGTERM").await,
43            }
44        }
45        #[cfg(not(unix))]
46        {
47            let _ = tokio::signal::ctrl_c().await;
48            shutdown("SIGINT").await;
49        }
50    });
51}
52
53#[cfg(test)]
54mod tests {
55    use super::*;
56
57    #[test]
58    fn registers_a_cleanup_callback_without_panicking() {
59        // `shutdown()` itself calls `std::process::exit`, so it's
60        // deliberately not exercised here — same untested-by-design
61        // boundary `targets::typescript`'s `shutdown-handler.ts` has.
62        on_shutdown(Box::new(|| Box::pin(async {})));
63    }
64}