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.
|
2016-10-24 15:55:00 +02:00
|
|
|
#[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).expect("unable to add SIGTERM to mask");
|
|
|
|
mask.add(SIGINT).expect("unable to add SIGINT to mask");
|
|
|
|
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 {
|
|
|
|
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(()))
|
|
|
|
}
|
|
|
|
}
|