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 | |
140 | use std::io::Error; |
141 | use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; |
142 | use std::sync::Arc; |
143 | |
144 | use libc::{c_int, EINVAL}; |
145 | |
146 | use 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. |
153 | pub 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. |
162 | pub 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. |
186 | pub 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. |
217 | pub 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)] |
232 | mod 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 | |