use crossbeam_channel as channel;
use crossbeam_channel::RecvTimeoutError;
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::Duration;
use anyhow::Result;
static GLOBAL_SHUTDOWN_SIGNAL: AtomicBool = AtomicBool::new(false);
#[derive(Clone)] pub struct Waiter {
receiver: channel::Receiver<i32>,
}
#[cfg(not(windows))]
fn notify(signals: &[i32]) -> channel::Receiver<i32> {
use crate::thread::spawn;
use anyhow::Context;
let (s, r) = channel::bounded(1);
let mut signals =
signal_hook::iterator::Signals::new(signals).expect("failed to register signal hook");
spawn("signal", move || {
for signal in signals.forever() {
s.send(signal)
.context(anyhow!("failed to send signal {}", signal))?;
}
Ok(())
});
r
}
#[cfg(windows)]
fn notify(_signals: &[i32]) -> channel::Receiver<i32> {
let (_, dummy) = channel::bounded(1);
dummy
}
impl Waiter {
#[cfg(windows)]
pub fn start() -> Waiter {
Waiter {
receiver: notify(&[]),
}
}
#[cfg(not(windows))]
pub fn start() -> Waiter {
Waiter {
receiver: notify(&[
signal_hook::consts::SIGINT,
signal_hook::consts::SIGTERM,
signal_hook::consts::SIGUSR1, ]),
}
}
pub fn shutdown_check() -> Result<()> {
if GLOBAL_SHUTDOWN_SIGNAL.load(Ordering::Relaxed) {
bail!("Shutdown triggered");
}
Ok(())
}
pub fn wait(&self, duration: Duration) -> Result<()> {
Self::shutdown_check()?;
match self.receiver.recv_timeout(duration) {
Ok(sig) => {
info!("notified via SIG{}", sig);
#[cfg(not(windows))]
if sig != signal_hook::consts::SIGUSR1 {
GLOBAL_SHUTDOWN_SIGNAL.store(true, Ordering::SeqCst);
bail!("Interrupted by signal {}", sig)
};
Ok(())
}
Err(RecvTimeoutError::Timeout) => Ok(()),
Err(RecvTimeoutError::Disconnected) => bail!("signal hook channel disconnected"),
}
}
pub fn poll(&self) -> Result<()> {
self.wait(Duration::from_secs(0))
}
}