use std::future::Future;
use std::pin::Pin;
use std::sync::Mutex;
pub type CleanupFn = Box<dyn Fn() -> Pin<Box<dyn Future<Output = ()> + Send>> + Send + Sync>;
static CLEANUP_FNS: Mutex<Vec<CleanupFn>> = Mutex::new(Vec::new());
pub fn on_shutdown(f: CleanupFn) {
CLEANUP_FNS.lock().unwrap().push(f);
}
async fn shutdown(signal: &str) {
tracing::info!(signal, "shutting down");
let fns: Vec<CleanupFn> = {
let mut guard = CLEANUP_FNS.lock().unwrap();
std::mem::take(&mut *guard)
};
for f in fns.into_iter().rev() {
f().await;
}
std::process::exit(0);
}
pub fn install_shutdown_handlers() {
tokio::spawn(async {
#[cfg(unix)]
{
let mut terminate =
tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate())
.expect("failed to install SIGTERM handler");
tokio::select! {
_ = tokio::signal::ctrl_c() => shutdown("SIGINT").await,
_ = terminate.recv() => shutdown("SIGTERM").await,
}
}
#[cfg(not(unix))]
{
let _ = tokio::signal::ctrl_c().await;
shutdown("SIGINT").await;
}
});
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn registers_a_cleanup_callback_without_panicking() {
on_shutdown(Box::new(|| Box::pin(async {})));
}
}