ctrlc_async/lib.rs
1// Copyright (c) 2017 CtrlC developers
2// Licensed under the Apache License, Version 2.0
3// <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
5// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
6// at your option. All files in the project carrying such
7// notice may not be copied, modified, or distributed except
8// according to those terms.
9
10#![warn(missing_docs)]
11
12//! Cross platform handling of Ctrl-C signals.
13//!
14//! [HandlerRoutine]:https://msdn.microsoft.com/en-us/library/windows/desktop/ms683242.aspx
15//!
16//! [set_handler()](fn.set_handler.html) allows setting a handler closure which is executed on
17//! `Ctrl+C`. On Unix, this corresponds to a `SIGINT` signal. On windows, `Ctrl+C` corresponds to
18//! [`CTRL_C_EVENT`][HandlerRoutine] or [`CTRL_BREAK_EVENT`][HandlerRoutine].
19//!
20//! Setting a handler will start a new dedicated signal handling thread where we
21//! execute the handler each time we receive a `Ctrl+C` signal. There can only be
22//! one handler, you would typically set one at the start of your program.
23//!
24//! # Example
25//! ```no_run
26//! use std::sync::atomic::{AtomicBool, Ordering};
27//! use std::sync::Arc;
28//!
29//! fn main() {
30//! let running = Arc::new(AtomicBool::new(true));
31//! let r = running.clone();
32//!
33//! ctrlc_async::set_handler(move || {
34//! r.store(false, Ordering::SeqCst);
35//! }).expect("Error setting Ctrl-C handler");
36//!
37//! println!("Waiting for Ctrl-C...");
38//! while running.load(Ordering::SeqCst) {}
39//! println!("Got it! Exiting...");
40//! }
41//! ```
42//!
43//! # Handling SIGTERM and SIGHUP
44//! Handling of `SIGTERM and SIGHUP` can be enabled with `termination` feature. If this is enabled,
45//! the handler specified by `set_handler()` will be executed for `SIGINT`, `SIGTERM` and `SIGHUP`.
46//!
47
48#[macro_use]
49
50mod error;
51mod platform;
52pub use platform::Signal;
53mod signal;
54pub mod helper;
55pub use signal::*;
56use std::future::Future;
57
58pub use error::Error;
59pub(crate) use helper::*;
60
61use std::sync::atomic::{AtomicBool, Ordering};
62
63static INIT: AtomicBool = AtomicBool::new(false);
64
65/// Register signal handler for Ctrl-C.
66///
67/// Starts a new dedicated signal handling thread. Should only be called once,
68/// typically at the start of your program.
69///
70/// # Example
71/// ```no_run
72/// ctrlc_async::set_handler(|| println!("Hello world!")).expect("Error setting Ctrl-C handler");
73/// ```
74///
75/// # Warning
76/// On Unix, any existing `SIGINT`, `SIGTERM` and `SIGHUP` (if termination feature is enabled) or `SA_SIGINFO`
77/// posix signal handlers will be overwritten. On Windows, multiple handler routines are allowed,
78/// but they are called on a last-registered, first-called basis until the signal is handled.
79///
80/// On Unix, signal dispositions and signal handlers are inherited by child processes created via
81/// `fork(2)` on, but not by child processes created via `execve(2)`.
82/// Signal handlers are not inherited on Windows.
83///
84/// # Errors
85/// Will return an error if another `ctrlc::set_handler()` handler exists or if a
86/// system error occurred while setting the handler.
87///
88/// # Panics
89/// Any panic in the handler will not be caught and will cause the signal handler thread to stop.
90///
91pub fn set_handler<F>(user_handler: F) -> Result<(), Error>
92where F: FnMut() -> () + 'static + Send,
93{
94 set_async_handler(async move {
95 block_on(user_handler);
96 })
97}
98
99/// Register an asynchronous signal handler for Ctrl-C.
100///
101/// Signal handling will be spawned on the tokio runtime. Should only be called once,
102/// typically at the start of your program.
103///
104/// # Example
105/// ```no_run
106/// ctrlc_async::set_async_handler(async { println!("Hello world!"); }).expect("Error setting Ctrl-C handler");
107/// ```
108///
109/// # Warning
110/// On Unix, any existing `SIGINT`, `SIGTERM` and `SIGHUP` (if termination feature is enabled) or `SA_SIGINFO`
111/// posix signal handlers will be overwritten. On Windows, multiple handler routines are allowed,
112/// but they are called on a last-registered, first-called basis until the signal is handled.
113///
114/// On Unix, signal dispositions and signal handlers are inherited by child processes created via
115/// `fork(2)` on, but not by child processes created via `execve(2)`.
116/// Signal handlers are not inherited on Windows.
117///
118/// # Errors
119/// Will return an error if another `ctrlc::set_handler()` handler exists or if a
120/// system error occurred while setting the handler.
121///
122/// # Panics
123/// Any panic in the handler will not be caught and will cause the signal handler thread to stop.
124///
125pub fn set_async_handler<F>(user_handler: F) -> Result<(), Error>
126where
127 F: Future<Output=()> + Send + 'static,
128{
129 if INIT.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst).map_or_else(|e| e, |a| a) {
130 return Err(Error::MultipleHandlers);
131 }
132
133 #[allow(unused_unsafe)]
134 let task = unsafe {
135 match platform::init_os_handler() {
136 Ok(a) => a,
137 Err(err) => {
138 INIT.store(false, Ordering::SeqCst);
139 return Err(err.into());
140 }
141 }
142 };
143
144 spawn(async move {
145 if let Ok(_) = task.await {
146 user_handler.await;
147 }
148 });
149
150 Ok(())
151}