github_mcp/core/
shutdown_handler.rs1use 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
11pub 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
31pub 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 on_shutdown(Box::new(|| Box::pin(async {})));
63 }
64}