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
//! 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`](../pipe/) and [`iterator`](../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 //! extern crate signal_hook; //! //! use std::io::Error; //! use std::sync::Arc; //! use std::sync::atomic::{AtomicUsize, Ordering}; //! //! use signal_hook::flag as signal_flag; //! //! fn main() -> Result<(), Error> { //! let term = Arc::new(AtomicUsize::new(0)); //! const SIGTERM: usize = signal_hook::SIGTERM as usize; //! const SIGINT: usize = signal_hook::SIGINT as usize; //! # #[cfg(not(windows))] //! const SIGQUIT: usize = signal_hook::SIGQUIT as usize; //! signal_flag::register_usize(signal_hook::SIGTERM, Arc::clone(&term), SIGTERM)?; //! signal_flag::register_usize(signal_hook::SIGINT, Arc::clone(&term), SIGINT)?; //! # #[cfg(not(windows))] //! signal_flag::register_usize(signal_hook::SIGQUIT, Arc::clone(&term), SIGQUIT)?; //! //! # // Hack to terminate the example when run as a doc-test. //! # term.store(SIGTERM, Ordering::Relaxed); //! loop { //! match term.load(Ordering::Relaxed) { //! 0 => { //! // Do some useful stuff here //! } //! SIGTERM => { //! eprintln!("Terminating on the TERM signal"); //! break; //! } //! SIGINT => { //! eprintln!("Terminating on the INT signal"); //! break; //! } //! # #[cfg(not(windows))] //! SIGQUIT => { //! 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 //! extern crate libc; //! extern crate signal_hook; //! //! use std::io::Error; //! use std::sync::Arc; //! use std::sync::atomic::{AtomicBool, Ordering}; //! use std::thread; //! use std::time::Duration; //! //! fn main() -> Result<(), Error> { //! let got = Arc::new(AtomicBool::new(false)); //! # #[cfg(not(windows))] //! signal_hook::flag::register(signal_hook::SIGUSR1, Arc::clone(&got))?; //! # #[cfg(windows)] //! # signal_hook::flag::register(signal_hook::SIGTERM, Arc::clone(&got))?; //! unsafe { //! # #[cfg(not(windows))] //! libc::raise(signal_hook::SIGUSR1); //! # #[cfg(windows)] //! # libc::raise(signal_hook::SIGTERM); //! } //! // 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 //! extern crate signal_hook; //! //! use std::io::Error; //! use std::sync::Arc; //! use std::sync::atomic::{AtomicBool, Ordering}; //! //! 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(signal_hook::SIGHUP, Arc::clone(&reload))?; //! signal_flag::register(signal_hook::SIGINT, Arc::clone(&term))?; //! signal_flag::register(signal_hook::SIGTERM, Arc::clone(&term))?; //! # #[cfg(not(windows))] //! signal_flag::register(signal_hook::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; use crate::SigId; /// Registers an action to set the flag to `true` whenever the given signal arrives. 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 { crate::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 { crate::register(signal, move || flag.store(value, Ordering::SeqCst)) } } #[cfg(test)] mod tests { use std::sync::atomic; use std::time::{Duration, Instant}; use super::*; fn self_signal() { unsafe { #[cfg(not(windows))] libc::raise(crate::SIGUSR1); #[cfg(windows)] libc::raise(crate::SIGTERM); } } fn wait_flag(flag: &AtomicBool) -> bool { let start = Instant::now(); while !flag.load(Ordering::Relaxed) { 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(crate::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::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)); } }