#![allow(unsafe_code)]
use std::sync::Arc;
use std::sync::OnceLock;
use std::sync::atomic::{AtomicBool, AtomicU8, Ordering};
use anyhow::{Context, Result};
static GLOBAL_SHUTDOWN: OnceLock<Arc<ShutdownSignal>> = OnceLock::new();
const EXIT_SIGINT: i32 = 130;
const EXIT_SIGTERM: i32 = 143;
pub struct ShutdownSignal {
flag: Arc<AtomicBool>,
count: AtomicU8,
signal_code: AtomicU8,
}
impl ShutdownSignal {
#[inline]
pub fn is_shutdown(&self) -> bool {
self.flag.load(Ordering::Acquire)
}
#[inline]
pub fn exit_code(&self) -> u8 {
match self.signal_code.load(Ordering::Acquire) {
143 => 143,
_ => 130,
}
}
#[inline]
pub fn flag(&self) -> Arc<AtomicBool> {
Arc::clone(&self.flag)
}
fn record_signal(&self, code: u8) {
self.signal_code
.compare_exchange(0, code, Ordering::AcqRel, Ordering::Acquire)
.ok();
let prev = self.count.fetch_add(1, Ordering::AcqRel);
if prev >= 1 {
#[cfg(unix)]
{
unsafe { libc::_exit(self.exit_code() as i32) };
}
#[cfg(not(unix))]
{
std::process::exit(self.exit_code() as i32);
}
}
}
}
pub fn reset_sigpipe() {
#[cfg(unix)]
{
unsafe {
libc::signal(libc::SIGPIPE, libc::SIG_DFL);
}
}
}
#[cfg(unix)]
const SHUTDOWN_MSG: &[u8] = b"\natomwrite: shutting down...\n";
pub fn install_handlers() -> Result<Arc<ShutdownSignal>> {
let flag = Arc::new(AtomicBool::new(false));
let signal = Arc::new(ShutdownSignal {
flag: Arc::clone(&flag),
count: AtomicU8::new(0),
signal_code: AtomicU8::new(0),
});
#[cfg(unix)]
{
signal_hook::flag::register(signal_hook::consts::SIGINT, Arc::clone(&flag))
.context("failed to register SIGINT handler")?;
signal_hook::flag::register(signal_hook::consts::SIGTERM, Arc::clone(&flag))
.context("failed to register SIGTERM handler")?;
let sig_int = Arc::clone(&signal);
unsafe {
signal_hook::low_level::register(signal_hook::consts::SIGINT, move || {
let was_first = sig_int.count.load(Ordering::Acquire) == 0;
sig_int.record_signal(EXIT_SIGINT as u8);
if was_first {
let _ = libc::write(2, SHUTDOWN_MSG.as_ptr().cast(), SHUTDOWN_MSG.len());
}
})
.context("failed to register SIGINT counter")?;
}
let sig_term = Arc::clone(&signal);
unsafe {
signal_hook::low_level::register(signal_hook::consts::SIGTERM, move || {
let was_first = sig_term.count.load(Ordering::Acquire) == 0;
sig_term.record_signal(EXIT_SIGTERM as u8);
if was_first {
let _ = libc::write(2, SHUTDOWN_MSG.as_ptr().cast(), SHUTDOWN_MSG.len());
}
})
.context("failed to register SIGTERM counter")?;
}
}
#[cfg(windows)]
{
let flag_win = Arc::clone(&flag);
let sig_win = Arc::clone(&signal);
ctrlc::set_handler(move || {
let was_first = sig_win.count.load(Ordering::Acquire) == 0;
flag_win.store(true, Ordering::Release);
sig_win.record_signal(EXIT_SIGINT as u8);
if was_first {
eprintln!("\natomwrite: shutting down...");
}
})
.context("failed to register Ctrl+C handler")?;
}
#[cfg(not(any(unix, windows)))]
{
let _ = &flag;
tracing::warn!("signal handlers not available on this platform — Ctrl+C may not work");
}
GLOBAL_SHUTDOWN.set(Arc::clone(&signal)).ok();
Ok(signal)
}
pub fn get_or_install_handlers() -> Result<Arc<ShutdownSignal>> {
if let Some(existing) = GLOBAL_SHUTDOWN.get() {
return Ok(Arc::clone(existing));
}
install_handlers()
}