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, action:move || flag.store(val:true, order: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, action:move || flag.store(val:value, order: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: impl Fn() = move || {
192 if condition.load(order: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: impl Fn() = move || {
224 if condition.load(order: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}
284