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