watchexec/src/interrupt_handler.rs

83 lines
2.0 KiB
Rust
Raw Normal View History

2016-10-22 21:37:03 +02:00
use std::sync::Mutex;
2016-10-23 21:15:28 +02:00
use std::sync::atomic::{AtomicBool, Ordering};
2016-10-22 21:37:03 +02:00
use std::sync::mpsc::{channel, Receiver, Sender, SendError};
lazy_static! {
static ref INTERRUPT_TX: Mutex<Option<Sender<()>>> = Mutex::new(None);
2016-10-23 21:15:28 +02:00
static ref INTERRUPT_REQUESTED: AtomicBool = AtomicBool::new(false);
2016-10-22 21:37:03 +02:00
}
2016-10-23 21:15:28 +02:00
/// On Unix platforms, mask reception of SIGINT/SIGTERM, spawn a thread,
/// and sigwait on those signals to safely relay them.
#[cfg(unix)]
2016-10-22 21:37:03 +02:00
pub fn install() -> Receiver<()> {
use std::thread;
2016-10-23 21:15:28 +02:00
use nix::sys::signal::{SigSet, SIGTERM, SIGINT};
let mut mask = SigSet::empty();
mask.add(SIGTERM);
mask.add(SIGINT);
2016-10-23 21:15:28 +02:00
mask.thread_set_mask().expect("unable to set signal mask");
2016-10-22 21:37:03 +02:00
let rx = create_channel();
thread::spawn(move || {
2016-10-23 21:15:28 +02:00
loop {
let _ = mask.wait().expect("unable to sigwait");
2016-10-22 21:37:03 +02:00
let result = send_interrupt();
if result.is_err() {
break;
}
}
});
rx
}
/// On Windows, use SetConsoleCtrlHandler() to send an interrupt
/// SetConsoleCtrlHandler runs in it's own thread, so it's safe.
#[cfg(windows)]
pub fn install() -> Receiver<()> {
use kernel32::SetConsoleCtrlHandler;
2016-10-22 21:51:17 +02:00
use winapi::{BOOL, DWORD, TRUE};
2016-10-22 21:37:03 +02:00
pub unsafe extern "system" fn ctrl_handler(_: DWORD) -> BOOL {
let _ = send_interrupt();
2016-10-22 21:51:17 +02:00
TRUE
2016-10-22 21:37:03 +02:00
}
let rx = create_channel();
2016-10-22 21:51:17 +02:00
unsafe {
SetConsoleCtrlHandler(Some(ctrl_handler), TRUE);
}
2016-10-22 21:37:03 +02:00
rx
}
2016-10-23 21:15:28 +02:00
pub fn interrupt_requested() -> bool {
INTERRUPT_REQUESTED.load(Ordering::Relaxed)
}
2016-10-22 21:37:03 +02:00
fn create_channel() -> Receiver<()> {
let mut guard = INTERRUPT_TX.lock().unwrap();
if (*guard).is_some() {
panic!("interrupt_handler::install() already called!");
}
let (tx, rx) = channel();
(*guard) = Some(tx);
rx
}
fn send_interrupt() -> Result<(), SendError<()>> {
2016-10-23 21:15:28 +02:00
INTERRUPT_REQUESTED.store(true, Ordering::Relaxed);
2016-10-22 21:37:03 +02:00
if let Some(ref mut tx) = *INTERRUPT_TX.lock().unwrap() {
tx.send(())
} else {
Err(SendError(()))
}
}