simple_signal/
lib.rs

1//! A simple wrapper for handling Unix process signals.
2
3#![cfg_attr(feature="nightly", feature(static_condvar))]
4#![cfg_attr(feature="nightly", feature(static_mutex))]
5
6#[cfg(feature="stable")]
7#[macro_use]
8extern crate lazy_static;
9
10use std::sync::atomic::Ordering;
11use std::thread;
12
13#[derive(Clone, Copy, PartialEq, Eq, Debug)]
14pub enum Signal {
15    Hup,
16    Int,
17    Quit,
18    Ill,
19    Abrt,
20    Fpe,
21    Kill,
22    Segv,
23    Pipe,
24    Alrm,
25    Term,
26}
27
28#[cfg(feature="nightly")]
29mod features {
30    use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT};
31    use std::sync::{StaticCondvar, CONDVAR_INIT, StaticMutex, MUTEX_INIT};
32    pub static CVAR: StaticCondvar = CONDVAR_INIT;
33    pub static MUTEX: StaticMutex = MUTEX_INIT;
34    pub static MASK: AtomicUsize = ATOMIC_USIZE_INIT;
35}
36
37#[cfg(not(feature="nightly"))]
38mod features {
39    use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT};
40    use std::sync::{Condvar, Mutex};
41    lazy_static! {
42        pub static ref CVAR: Condvar = Condvar::new();
43        pub static ref MUTEX: Mutex<bool> = Mutex::new(false);
44    }
45    pub static MASK: AtomicUsize = ATOMIC_USIZE_INIT;
46}
47
48use self::features::*;
49
50#[cfg(unix)]
51mod platform {
52    extern crate libc;
53
54    use self::libc::{c_int, signal, sighandler_t};
55    use self::libc::{SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, SIGFPE, SIGKILL, SIGSEGV, SIGPIPE, SIGALRM, SIGTERM};
56    use std::mem;
57    use std::sync::atomic::Ordering;
58    use super::Signal;
59
60    pub extern "C" fn handler(sig: c_int) {
61        let mask = match sig {
62            SIGHUP => 1,
63            SIGINT => 2,
64            SIGQUIT => 4,
65            SIGILL => 8,
66            SIGABRT => 16,
67            SIGFPE => 32,
68            SIGKILL => 64,
69            SIGSEGV => 128,
70            SIGPIPE => 256,
71            SIGALRM => 512,
72            SIGTERM => 1024,
73            _ => return,
74        };
75
76        loop {
77            let prev_mask = super::features::MASK.load(Ordering::Relaxed);
78            let new_mask = prev_mask | mask;
79            if super::features::MASK.compare_and_swap(prev_mask, new_mask, Ordering::Relaxed) == new_mask {
80                break;
81            }
82        }
83        super::features::CVAR.notify_all();
84    }
85
86    #[inline]
87    pub unsafe fn set_os_handler(sig: Signal) {
88        let os_sig = match sig {
89            Signal::Hup => SIGHUP,
90            Signal::Int => SIGINT,
91            Signal::Quit => SIGQUIT,
92            Signal::Ill => SIGILL,
93            Signal::Abrt => SIGABRT,
94            Signal::Fpe => SIGFPE,
95            Signal::Kill => SIGKILL,
96            Signal::Segv => SIGSEGV,
97            Signal::Pipe => SIGPIPE,
98            Signal::Alrm => SIGALRM,
99            Signal::Term => SIGTERM,
100        };
101
102        signal(os_sig, mem::transmute::<_, sighandler_t>(handler as extern "C" fn(_)));
103    }
104}
105
106use self::platform::*;
107
108/// Sets up a signal handler.
109///
110/// # Example
111/// ```
112/// use simple_signal::{self, Signal};
113/// simple_signal::set_handler(&[Signal::Int, Signal::Term], |signals| println!("Caught: {:?}", signals));
114/// ```
115pub fn set_handler<F>(signals: &[Signal], user_handler: F) where F: Fn(&[Signal]) + 'static + Send {
116    for &signal in signals.iter() {
117        unsafe { set_os_handler(signal) }
118    }
119    thread::spawn(move || {
120        let mut signals = Vec::new();
121        loop {
122            let mask = MASK.load(Ordering::Relaxed);
123            if mask == 0 {
124                let _ = CVAR.wait(MUTEX.lock().unwrap());
125                thread::yield_now();
126                continue;
127            }
128            signals.clear();
129            if mask & 1 != 0 { signals.push(Signal::Hup) }
130            if mask & 2 != 0 { signals.push(Signal::Int) }
131            if mask & 4 != 0 { signals.push(Signal::Quit) }
132            if mask & 8 != 0 { signals.push(Signal::Ill) }
133            if mask & 16 != 0 { signals.push(Signal::Abrt) }
134            if mask & 32 != 0 { signals.push(Signal::Fpe) }
135            if mask & 64 != 0 { signals.push(Signal::Kill) }
136            if mask & 128 != 0 { signals.push(Signal::Segv) }
137            if mask & 256 != 0 { signals.push(Signal::Pipe) }
138            if mask & 512 != 0 { signals.push(Signal::Alrm) }
139            if mask & 1024 != 0 { signals.push(Signal::Term) }
140            MASK.store(0, Ordering::Relaxed);
141            user_handler(&signals);
142        }
143    });
144}
145
146#[cfg(test)]
147mod test {
148    extern crate libc;
149
150    use std::sync::mpsc::sync_channel;
151    use self::libc::c_int;
152    use self::libc::{SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, SIGFPE, SIGKILL, SIGSEGV, SIGPIPE, SIGALRM, SIGTERM};
153    use super::{Signal};
154    use super::platform::handler;
155
156    fn to_os_signal(signal: Signal) -> c_int {
157        match signal {
158            Signal::Hup => SIGHUP,
159            Signal::Int => SIGINT,
160            Signal::Quit => SIGQUIT,
161            Signal::Ill => SIGILL,
162            Signal::Abrt => SIGABRT,
163            Signal::Fpe => SIGFPE,
164            Signal::Kill => SIGKILL,
165            Signal::Segv => SIGSEGV,
166            Signal::Pipe => SIGPIPE,
167            Signal::Alrm => SIGALRM,
168            Signal::Term => SIGTERM,
169        }
170    }
171
172    #[test]
173    fn all_signals() {
174        let signals = [Signal::Hup, Signal::Int, Signal::Quit, Signal::Abrt, Signal::Term];
175        let (tx, rx) = sync_channel(0);
176        super::set_handler(&signals, move |signals| tx.send(signals.to_owned()).unwrap());
177        // Check all signals one-by-one.
178        for &signal in signals.iter() {
179            handler(to_os_signal(signal));
180            assert_eq!(rx.recv().unwrap(), vec![signal]);
181        }
182        // Check all signals simultaneously.
183        for &signal in signals.iter() {
184            handler(to_os_signal(signal))
185        }
186        assert_eq!(rx.recv().unwrap(), signals.to_owned());
187    }
188}