use std::thread;
use parking_lot::Mutex;
use nix::sys::signal::{SigSet, SigmaskHow, Signal};
use crate::{Error, SigType};
type Callback = dyn FnOnce(SigType) + Send + Sync;
static HANDLER: Mutex<Option<Box<Callback>>> = Mutex::new(None);
pub struct KillWait {
jh: thread::JoinHandle<()>
}
impl KillWait {
pub fn wait(self) {
self.jh.join().unwrap();
}
}
pub fn init() -> Result<KillWait, Error> {
let mut ss = SigSet::empty();
ss.add(Signal::SIGINT);
ss.add(Signal::SIGTERM);
let mut oldset = SigSet::empty();
nix::sys::signal::pthread_sigmask(
SigmaskHow::SIG_SETMASK,
Some(&ss),
Some(&mut oldset)
)
.unwrap();
let jh = thread::Builder::new()
.name("sigmon".into())
.spawn(move || {
let mask = unsafe {
let mut mask: libc::sigset_t = std::mem::zeroed();
libc::sigemptyset(&mut mask);
libc::sigaddset(&mut mask, libc::SIGINT);
libc::sigaddset(&mut mask, libc::SIGTERM);
mask
};
loop {
let mut sig: libc::c_int = 0;
let ret = unsafe { libc::sigwait(&mask, &mut sig) };
if ret == 0 {
let signal = Signal::try_from(sig).unwrap();
let Some(handler) = HANDLER.lock().take() else {
continue;
};
match signal {
Signal::SIGINT => {
handler(SigType::Int);
break;
}
Signal::SIGTERM => {
handler(SigType::Term);
break;
}
_ => {}
}
}
}
})
.map_err(|e| {
let msg = format!("Unable to spawn signal monitoring thread; {e}");
Error::Internal(msg)
})?;
Ok(KillWait { jh })
}
pub fn register(
handler: impl FnOnce(SigType) + 'static + Send + Sync
) -> Result<(), Error> {
let mut g = HANDLER.lock();
*g = Some(Box::new(handler));
drop(g);
Ok(())
}