use std::{
process,
sync::{
OnceLock,
atomic::{AtomicBool, Ordering},
},
};
use anyhow::{Result, anyhow};
pub const EXIT_SIGNALED: i32 = 130;
static HANDLER: OnceLock<Result<(), String>> = OnceLock::new();
static CLEANUP: OnceLock<fn()> = OnceLock::new();
static GRACEFUL: AtomicBool = AtomicBool::new(false);
static REQUESTED: AtomicBool = AtomicBool::new(false);
#[derive(Clone, Debug)]
pub struct Shutdown(());
impl Shutdown {
pub fn install() -> Result<Self> {
GRACEFUL.store(true, Ordering::SeqCst);
install_handler()?;
Ok(Self(()))
}
pub fn is_requested(&self) -> bool {
REQUESTED.load(Ordering::SeqCst)
}
pub fn request(&self) {
REQUESTED.store(true, Ordering::SeqCst);
}
}
#[cfg(feature = "spinner")]
pub(crate) fn install_handler_with(cleanup: fn()) -> Result<()> {
let _ = CLEANUP.set(cleanup);
install_handler()
}
fn install_handler() -> Result<()> {
HANDLER
.get_or_init(|| ctrlc::set_handler(handle).map_err(|err| err.to_string()))
.clone()
.map_err(|err| anyhow!("Cannot install the termination signal handler: {err}"))
}
fn handle() {
if GRACEFUL.load(Ordering::SeqCst) && !REQUESTED.swap(true, Ordering::SeqCst) {
return;
}
if let Some(cleanup) = CLEANUP.get() {
cleanup();
}
process::exit(EXIT_SIGNALED);
}