1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
//! Gracefully shutdown. //! //! (Unix) Async signals are tricky to handle properly. This utility let you //! block the main thread and execute arbitrary (thread-safe) code to shutdown //! the process gracefully. //! //! # Usage //! //! 1. Initialize a [SignalGuard](struct.SignalGuard.html) before creating any //! additional threads. //! 2. The [SignalGuard](struct.SignalGuard.html) will block necessary signals //! (`SIGINT`, `SIGQUIT` and `SIGTERM` on *nix, `Ctrl+C` and `Ctrl+Break` on //! Windows) during initialization. //! 3. Spawn new threads to do the real work. //! 4. Register a handle to properly shutdown the application. //! 5. The main thread will be blocked until a signal is received. //! 6. The handler will run in the main thread. //! 7. On Windows the process will terminate after the handler returns (and //! potentially any libc `atexit` handlers). //! //! # Example //! //! ```no_run //! extern crate graceful; //! //! use std::sync::atomic::{ATOMIC_BOOL_INIT, AtomicBool, Ordering}; //! use std::time::Duration; //! use std::thread; //! //! use graceful::SignalGuard; //! //! static STOP: AtomicBool = ATOMIC_BOOL_INIT; //! //! fn main() { //! let signal_guard = SignalGuard::new(); //! //! let handle = thread::spawn(|| { //! println!("Worker thread started. Type Ctrl+C to stop."); //! while !STOP.load(Ordering::Acquire) { //! println!("working..."); //! thread::sleep(Duration::from_millis(500)); //! } //! println!("Bye."); //! }); //! //! signal_guard.at_exit(move |sig| { //! println!("Signal {} received.", sig); //! STOP.store(true, Ordering::Release); //! handle.join().unwrap(); //! }); //! } //! ``` //! #[cfg(unix)] mod platform { extern crate nix; use self::nix::sys::signal::{SigSet, SIGINT, SIGQUIT, SIGTERM}; pub struct SignalGuard(SigSet); impl SignalGuard { /// Block necessary signals (`SIGINT`, `SIGQUIT` and `SIGTERM` on *nix, /// `Ctrl+C` and `Ctrl+Break` on Windows). /// /// New threads should be spawned after this. pub fn new() -> SignalGuard { let mut mask = SigSet::empty(); SignalGuard::init(&mut mask).unwrap(); SignalGuard(mask) } fn init(mask: &mut SigSet) -> nix::Result<()> { mask.add(SIGINT); mask.add(SIGQUIT); mask.add(SIGTERM); mask.thread_block() } /// Block the running thread until a signal is received. Then the /// `handler` will be called in the main thread. /// /// Do not put any code after this. pub fn at_exit<F: FnOnce(usize)>(&self, handler: F) { let sig = self.0.wait().unwrap(); handler(sig as usize); } } } #[cfg(windows)] mod platform { extern crate winapi; extern crate kernel32; use std::sync::mpsc::{sync_channel, SyncSender, Receiver}; use kernel32::SetConsoleCtrlHandler; use winapi::{BOOL, DWORD, TRUE}; static CHAN: (SyncSender<DWORD>, Receiver<DWORD>) = sync_channel(0); unsafe extern "system" fn handler(event: DWORD) -> BOOL { CHAN.0.send(event); CHAN.0.send(0); FALSE } pub struct SignalGuard; impl SignalGuard { pub fn new() -> SignalGuard { unsafe { SetConsoleCtrlHandler(Some(handler), TRUE) }; SignalGuard } pub fn at_exit<F: FnOnce(usize)>(&self, handler: F) { let event = CHAN.1.recv().unwrap(); handler(event as usize); CHAN.1.recv().unwrap(); } } } pub use platform::SignalGuard;