use std::io;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use signal_hook::SigId;
use signal_hook::consts::{SIGHUP, SIGINT, SIGQUIT, SIGTERM};
use signal_hook::flag;
const STOPPING: [i32; 4] = [SIGTERM, SIGHUP, SIGINT, SIGQUIT];
#[derive(Debug, Default)]
pub(crate) struct StopRequest {
requested: Arc<AtomicBool>,
handlers: Vec<SigId>,
}
impl StopRequest {
pub(crate) fn watch() -> io::Result<Self> {
let mut stop = Self::default();
for signal in STOPPING {
let shutdown =
flag::register_conditional_shutdown(signal, 1, Arc::clone(&stop.requested))?;
stop.handlers.push(shutdown);
stop.handlers
.push(flag::register(signal, Arc::clone(&stop.requested))?);
}
Ok(stop)
}
pub(crate) fn requested(&self) -> bool {
self.requested.load(Ordering::Relaxed)
}
}
impl Drop for StopRequest {
fn drop(&mut self) {
for handler in self.handlers.drain(..) {
signal_hook::low_level::unregister(handler);
}
}
}
#[cfg(test)]
mod tests {
use super::StopRequest;
#[test]
fn a_signal_sets_the_request_and_a_later_watch_starts_fresh() {
let earlier = StopRequest::watch().expect("signal handlers register");
assert!(!earlier.requested());
signal_hook::low_level::raise(signal_hook::consts::SIGQUIT).expect("raise works");
assert!(
earlier.requested(),
"the signal set the flag and the process lives"
);
drop(earlier);
let later = StopRequest::watch().expect("signal handlers register");
assert!(!later.requested());
signal_hook::low_level::raise(signal_hook::consts::SIGQUIT).expect("raise works");
assert!(later.requested());
}
}