signal_hook/
flag.rs

1//! Module for actions setting flags.
2//!
3//! This contains helper functions to set flags whenever a signal happens. The flags are atomic
4//! bools or numbers and the library manipulates them with the `SeqCst` ordering, in case someone
5//! cares about relative order to some *other* atomic variables. If you don't care about the
6//! relative order, you are free to use `Ordering::Relaxed` when reading and resetting the flags.
7//!
8//! # When to use
9//!
10//! The flags in this module allow for polling if a signal arrived since the previous poll. The do
11//! not allow blocking until something arrives.
12//!
13//! Therefore, the natural way to use them is in applications that have some kind of iterative work
14//! with both some upper and lower time limit on one iteration. If one iteration could block for
15//! arbitrary time, the handling of the signal would be postponed for a long time. If the iteration
16//! didn't block at all, the checking for the signal would turn into a busy-loop.
17//!
18//! If what you need is blocking until a signal comes, you might find better tools in the
19//! [`pipe`][crate::low_level::pipe] and [`iterator`][crate::iterator] modules.
20//!
21//! # Examples
22//!
23//! Doing something until terminated. This also knows by which signal it was terminated. In case
24//! multiple termination signals arrive before it is handled, it recognizes the last one.
25//!
26//! ```rust
27//! use std::io::Error;
28//! use std::sync::Arc;
29//! use std::sync::atomic::{AtomicUsize, Ordering};
30//!
31//! use signal_hook::consts::signal::*;
32//! use signal_hook::flag as signal_flag;
33//!
34//! fn main() -> Result<(), Error> {
35//!     let term = Arc::new(AtomicUsize::new(0));
36//!     const SIGTERM_U: usize = SIGTERM as usize;
37//!     const SIGINT_U: usize = SIGINT as usize;
38//! #   #[cfg(not(windows))]
39//!     const SIGQUIT_U: usize = SIGQUIT as usize;
40//!     signal_flag::register_usize(SIGTERM, Arc::clone(&term), SIGTERM_U)?;
41//!     signal_flag::register_usize(SIGINT, Arc::clone(&term), SIGINT_U)?;
42//! #   #[cfg(not(windows))]
43//!     signal_flag::register_usize(SIGQUIT, Arc::clone(&term), SIGQUIT_U)?;
44//!
45//! #   // Hack to terminate the example when run as a doc-test.
46//! #   term.store(SIGTERM_U, Ordering::Relaxed);
47//!     loop {
48//!         match term.load(Ordering::Relaxed) {
49//!             0 => {
50//!                 // Do some useful stuff here
51//!             }
52//!             SIGTERM_U => {
53//!                 eprintln!("Terminating on the TERM signal");
54//!                 break;
55//!             }
56//!             SIGINT_U => {
57//!                 eprintln!("Terminating on the INT signal");
58//!                 break;
59//!             }
60//! #           #[cfg(not(windows))]
61//!             SIGQUIT_U => {
62//!                 eprintln!("Terminating on the QUIT signal");
63//!                 break;
64//!             }
65//!             _ => unreachable!(),
66//!         }
67//!     }
68//!
69//!     Ok(())
70//! }
71//! ```
72//!
73//! Sending a signal to self and seeing it arrived (not of a practical usage on itself):
74//!
75//! ```rust
76//! use std::io::Error;
77//! use std::sync::Arc;
78//! use std::sync::atomic::{AtomicBool, Ordering};
79//! use std::thread;
80//! use std::time::Duration;
81//!
82//! use signal_hook::consts::signal::*;
83//! use signal_hook::low_level::raise;
84//!
85//! fn main() -> Result<(), Error> {
86//!     let got = Arc::new(AtomicBool::new(false));
87//! #   #[cfg(not(windows))]
88//!     signal_hook::flag::register(SIGUSR1, Arc::clone(&got))?;
89//! #   #[cfg(windows)]
90//! #   signal_hook::flag::register(SIGTERM, Arc::clone(&got))?;
91//! #   #[cfg(not(windows))]
92//!     raise(SIGUSR1).unwrap();
93//! #   #[cfg(windows)]
94//! #   raise(SIGTERM).unwrap();
95//!     // A sleep here, because it could run the signal handler in another thread and we may not
96//!     // see the flag right away. This is still a hack and not guaranteed to work, it is just an
97//!     // example!
98//!     thread::sleep(Duration::from_secs(1));
99//!     assert!(got.load(Ordering::Relaxed));
100//!     Ok(())
101//! }
102//! ```
103//!
104//! Reloading a configuration on `SIGHUP` (which is a common behaviour of many UNIX daemons,
105//! together with reopening the log file).
106//!
107//! ```rust
108//! use std::io::Error;
109//! use std::sync::Arc;
110//! use std::sync::atomic::{AtomicBool, Ordering};
111//!
112//! use signal_hook::consts::signal::*;
113//! use signal_hook::flag as signal_flag;
114//!
115//! fn main() -> Result<(), Error> {
116//!     // We start with true, to load the configuration in the very first iteration too.
117//!     let reload = Arc::new(AtomicBool::new(true));
118//!     let term = Arc::new(AtomicBool::new(false));
119//! #   #[cfg(not(windows))]
120//!     signal_flag::register(SIGHUP, Arc::clone(&reload))?;
121//!     signal_flag::register(SIGINT, Arc::clone(&term))?;
122//!     signal_flag::register(SIGTERM, Arc::clone(&term))?;
123//! #   #[cfg(not(windows))]
124//!     signal_flag::register(SIGQUIT, Arc::clone(&term))?;
125//!     while !term.load(Ordering::Relaxed) {
126//!         // Using swap here, not load, to reset it back to false once it is reloaded.
127//!         if reload.swap(false, Ordering::Relaxed) {
128//!             // Reload the config here
129//! #
130//! #           // Hiden hack to make the example terminate when run as doc-test. Not part of the
131//! #           // real code.
132//! #           term.store(true, Ordering::Relaxed);
133//!         }
134//!         // Serve one request
135//!     }
136//!     Ok(())
137//! }
138//! ```
139
140use std::io::Error;
141use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
142use std::sync::Arc;
143
144use libc::{c_int, EINVAL};
145
146use crate::{low_level, SigId};
147
148/// Registers an action to set the flag to `true` whenever the given signal arrives.
149///
150/// # Panics
151///
152/// If the signal is one of the forbidden.
153pub fn register(signal: c_int, flag: Arc<AtomicBool>) -> Result<SigId, Error> {
154    // We use SeqCst for two reasons:
155    // * Signals should not come very often, so the performance does not really matter.
156    // * We promise the order of actions, but setting different atomics with Relaxed or similar
157    //   would not guarantee the effective order.
158    unsafe { low_level::register(signal, move || flag.store(true, Ordering::SeqCst)) }
159}
160
161/// Registers an action to set the flag to the given value whenever the signal arrives.
162pub fn register_usize(signal: c_int, flag: Arc<AtomicUsize>, value: usize) -> Result<SigId, Error> {
163    unsafe { low_level::register(signal, move || flag.store(value, Ordering::SeqCst)) }
164}
165
166/// Terminate the application on a signal if the given condition is true.
167///
168/// This can be used for different use cases. One of them (with the condition being always true) is
169/// just unconditionally terminate on the given signal.
170///
171/// Another is being able to turn on and off the behaviour by the shared flag.
172///
173/// The last one is handling double CTRL+C ‒ if the user presses CTRL+C, we would like to start a
174/// graceful shutdown. But if anything ever gets stuck in the shutdown, second CTRL+C (or other
175/// such termination signal) should terminate the application without further delay.
176///
177/// To do that, one can combine this with [`register`]. On the first run, the flag is `false` and
178/// this doesn't terminate. But then the flag is set to true during the first run and „arms“ the
179/// shutdown on the second run. Note that it matters in which order the actions are registered (the
180/// shutdown must go first). And yes, this also allows asking the user „Do you want to terminate“
181/// and disarming the abrupt shutdown if the user answers „No“.
182///
183/// # Panics
184///
185/// If the signal is one of the forbidden.
186pub fn register_conditional_shutdown(
187    signal: c_int,
188    status: c_int,
189    condition: Arc<AtomicBool>,
190) -> Result<SigId, Error> {
191    let action = move || {
192        if condition.load(Ordering::SeqCst) {
193            low_level::exit(status);
194        }
195    };
196    unsafe { low_level::register(signal, action) }
197}
198
199/// Conditionally runs an emulation of the default action on the given signal.
200///
201/// If the provided condition is true at the time of invoking the signal handler, the equivalent of
202/// the default action of the given signal is run. It is a bit similar to
203/// [`register_conditional_shutdown`], except that it doesn't terminate for non-termination
204/// signals, it runs their default handler.
205///
206/// # Panics
207///
208/// If the signal is one of the forbidden
209///
210/// # Errors
211///
212/// Similarly to the [`emulate_default_handler`][low_level::emulate_default_handler] function, this
213/// one looks the signal up in a table. If it is unknown, an error is returned.
214///
215/// Additionally to that, any errors that can be caused by a registration of a handler can happen
216/// too.
217pub fn register_conditional_default(
218    signal: c_int,
219    condition: Arc<AtomicBool>,
220) -> Result<SigId, Error> {
221    // Verify we know about this particular signal.
222    low_level::signal_name(signal).ok_or_else(|| Error::from_raw_os_error(EINVAL))?;
223    let action = move || {
224        if condition.load(Ordering::SeqCst) {
225            let _ = low_level::emulate_default_handler(signal);
226        }
227    };
228    unsafe { low_level::register(signal, action) }
229}
230
231#[cfg(test)]
232mod tests {
233    use std::sync::atomic;
234    use std::time::{Duration, Instant};
235
236    use super::*;
237    use crate::consts::signal::*;
238
239    fn self_signal() {
240        #[cfg(not(windows))]
241        const SIG: c_int = SIGUSR1;
242        #[cfg(windows)]
243        const SIG: c_int = SIGTERM;
244        crate::low_level::raise(SIG).unwrap();
245    }
246
247    fn wait_flag(flag: &AtomicBool) -> bool {
248        let start = Instant::now();
249        while !flag.load(Ordering::Relaxed) {
250            // Replaced by hint::spin_loop, but we want to support older compiler
251            #[allow(deprecated)]
252            atomic::spin_loop_hint();
253            if Instant::now() - start > Duration::from_secs(1) {
254                // We reached a timeout and nothing happened yet.
255                // In theory, using timeouts for thread-synchronization tests is wrong, but a
256                // second should be enough in practice.
257                return false;
258            }
259        }
260        true
261    }
262
263    #[test]
264    fn register_unregister() {
265        // When we register the action, it is active.
266        let flag = Arc::new(AtomicBool::new(false));
267        #[cfg(not(windows))]
268        let signal = register(SIGUSR1, Arc::clone(&flag)).unwrap();
269        #[cfg(windows)]
270        let signal = register(crate::SIGTERM, Arc::clone(&flag)).unwrap();
271        self_signal();
272        assert!(wait_flag(&flag));
273        // But stops working after it is unregistered.
274        assert!(crate::low_level::unregister(signal));
275        flag.store(false, Ordering::Relaxed);
276        self_signal();
277        assert!(!wait_flag(&flag));
278        // And the unregistration actually dropped its copy of the Arc
279        assert_eq!(1, Arc::strong_count(&flag));
280    }
281
282    // The shutdown is tested in tests/shutdown.rs
283}