| 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 | |