use std::sync::OnceLock;
use std::sync::atomic::{AtomicBool, AtomicI32, Ordering};
static SHUTDOWN_REQUESTED: AtomicBool = AtomicBool::new(false);
static SHUTDOWN_EXIT_CODE: AtomicI32 = AtomicI32::new(0);
static INSTALLED: OnceLock<()> = OnceLock::new();
pub fn install() {
if INSTALLED.set(()).is_err() {
return;
}
#[cfg(unix)]
install_unix();
}
#[must_use]
pub fn is_requested() -> bool {
SHUTDOWN_REQUESTED.load(Ordering::Acquire)
}
#[must_use]
pub fn exit_code() -> Option<i32> {
let code = SHUTDOWN_EXIT_CODE.load(Ordering::Acquire);
(code != 0).then_some(code)
}
#[cfg(unix)]
fn install_unix() {
use signal_hook::consts::{SIGHUP, SIGINT, SIGTERM};
use signal_hook::iterator::Signals;
let mut signals = match Signals::new([SIGINT, SIGTERM, SIGHUP]) {
Ok(signals) => signals,
Err(err) => {
tracing::warn!(
error = %err,
"failed to install shutdown signal handler; SIGTERM/SIGINT/SIGHUP \
will skip Drop and may strand WAL frames"
);
return;
}
};
std::thread::Builder::new()
.name("br-shutdown".to_string())
.spawn(move || {
for signo in signals.forever() {
let exit = 128 + signo;
let was_first = SHUTDOWN_EXIT_CODE
.compare_exchange(0, exit, Ordering::AcqRel, Ordering::Acquire)
.is_ok();
SHUTDOWN_REQUESTED.store(true, Ordering::Release);
if !was_first {
signal_hook::low_level::exit(exit);
}
}
})
.map(drop)
.unwrap_or_else(|err| {
tracing::warn!(
error = %err,
"failed to spawn br-shutdown thread; falling back to default signal action"
);
});
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::atomic::Ordering;
#[test]
fn install_is_idempotent() {
install();
install();
install();
if !is_requested() {
SHUTDOWN_REQUESTED.store(false, Ordering::Release);
SHUTDOWN_EXIT_CODE.store(0, Ordering::Release);
}
}
#[test]
fn exit_code_is_none_until_signal_fires() {
if !is_requested() {
assert_eq!(exit_code(), None);
}
}
}