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