use std::sync::atomic::{AtomicBool, AtomicI32, Ordering};
use std::sync::{Arc, OnceLock};
use anyhow::{Result, bail};
static RECEIVED: AtomicI32 = AtomicI32::new(0);
static CANCELLED: OnceLock<Arc<AtomicBool>> = OnceLock::new();
pub fn install(cancelled: Arc<AtomicBool>) -> Result<()> {
if CANCELLED.set(cancelled).is_err() {
bail!("cannot register cancellation handler twice");
}
#[cfg(unix)]
for signal in [libc::SIGINT, libc::SIGTERM, libc::SIGHUP] {
unsafe {
let mut action: libc::sigaction = std::mem::zeroed();
action.sa_sigaction = handle as extern "C" fn(libc::c_int) as libc::sighandler_t;
libc::sigemptyset(&mut action.sa_mask);
action.sa_flags = libc::SA_RESTART;
if libc::sigaction(signal, &action, std::ptr::null_mut()) != 0 {
bail!(
"cannot register cancellation handler: {}",
std::io::Error::last_os_error()
);
}
}
}
#[cfg(not(unix))]
ctrlc::set_handler(|| handle(2))
.map_err(|error| anyhow::anyhow!("cannot register cancellation handler: {error}"))?;
Ok(())
}
extern "C" fn handle(signal: std::ffi::c_int) {
let _ = RECEIVED.compare_exchange(0, signal, Ordering::SeqCst, Ordering::SeqCst);
if let Some(cancelled) = CANCELLED.get() {
cancelled.store(true, Ordering::SeqCst);
}
}
pub fn exit_code() -> i32 {
match RECEIVED.load(Ordering::SeqCst) {
0 => 130,
signal => 128 + signal,
}
}