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`](../pipe/) and [`iterator`](../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 //! extern crate signal_hook;
28 //!
29 //! use std::io::Error;
30 //! use std::sync::Arc;
31 //! use std::sync::atomic::{AtomicUsize, Ordering};
32 //!
33 //! use signal_hook::flag as signal_flag;
34 //!
35 //! fn main() -> Result<(), Error> {
36 //!     let term = Arc::new(AtomicUsize::new(0));
37 //!     const SIGTERM: usize = signal_hook::SIGTERM as usize;
38 //!     const SIGINT: usize = signal_hook::SIGINT as usize;
39 //! #   #[cfg(not(windows))]
get_logger()40 //!     const SIGQUIT: usize = signal_hook::SIGQUIT as usize;
41 //!     signal_flag::register_usize(signal_hook::SIGTERM, Arc::clone(&term), SIGTERM)?;
42 //!     signal_flag::register_usize(signal_hook::SIGINT, Arc::clone(&term), SIGINT)?;
43 //! #   #[cfg(not(windows))]
44 //!     signal_flag::register_usize(signal_hook::SIGQUIT, Arc::clone(&term), SIGQUIT)?;
45 //!
46 //! #   // Hack to terminate the example when run as a doc-test.
47 //! #   term.store(SIGTERM, Ordering::Relaxed);
48 //!     loop {
49 //!         match term.load(Ordering::Relaxed) {
50 //!             0 => {
51 //!                 // Do some useful stuff here
52 //!             }
53 //!             SIGTERM => {
54 //!                 eprintln!("Terminating on the TERM signal");
55 //!                 break;
56 //!             }
57 //!             SIGINT => {
58 //!                 eprintln!("Terminating on the INT signal");
59 //!                 break;
60 //!             }
61 //! #           #[cfg(not(windows))]
62 //!             SIGQUIT => {
63 //!                 eprintln!("Terminating on the QUIT signal");
64 //!                 break;
65 //!             }
66 //!             _ => unreachable!(),
67 //!         }
68 //!     }
69 //!
70 //!     Ok(())
71 //! }
72 //! ```
73 //!
74 //! Sending a signal to self and seeing it arrived (not of a practical usage on itself):
75 //!
76 //! ```rust
77 //! extern crate libc;
78 //! extern crate signal_hook;
79 //!
80 //! use std::io::Error;
81 //! use std::sync::Arc;
82 //! use std::sync::atomic::{AtomicBool, Ordering};
83 //! use std::thread;
84 //! use std::time::Duration;
85 //!
86 //! fn main() -> Result<(), Error> {
87 //!     let got = Arc::new(AtomicBool::new(false));
88 //! #   #[cfg(not(windows))]
89 //!     signal_hook::flag::register(signal_hook::SIGUSR1, Arc::clone(&got))?;
90 //! #   #[cfg(windows)]
91 //! #   signal_hook::flag::register(signal_hook::SIGTERM, Arc::clone(&got))?;
92 //!     unsafe {
93 //! #       #[cfg(not(windows))]
94 //!         libc::raise(signal_hook::SIGUSR1);
95 //! #       #[cfg(windows)]
96 //! #       libc::raise(signal_hook::SIGTERM);
97 //!     }
98 //!     // A sleep here, because it could run the signal handler in another thread and we may not
99 //!     // see the flag right away. This is still a hack and not guaranteed to work, it is just an
100 //!     // example!
101 //!     thread::sleep(Duration::from_secs(1));
102 //!     assert!(got.load(Ordering::Relaxed));
103 //!     Ok(())
104 //! }
105 //! ```
106 //!
107 //! Reloading a configuration on `SIGHUP` (which is a common behaviour of many UNIX daemons,
108 //! together with reopening the log file).
109 //!
110 //! ```rust
111 //! extern crate signal_hook;
112 //!
113 //! use std::io::Error;
114 //! use std::sync::Arc;
115 //! use std::sync::atomic::{AtomicBool, Ordering};
116 //!
117 //! use signal_hook::flag as signal_flag;
118 //!
119 //! fn main() -> Result<(), Error> {
120 //!     // We start with true, to load the configuration in the very first iteration too.
121 //!     let reload = Arc::new(AtomicBool::new(true));
122 //!     let term = Arc::new(AtomicBool::new(false));
123 //! #   #[cfg(not(windows))]
124 //!     signal_flag::register(signal_hook::SIGHUP, Arc::clone(&reload))?;
125 //!     signal_flag::register(signal_hook::SIGINT, Arc::clone(&term))?;
126 //!     signal_flag::register(signal_hook::SIGTERM, Arc::clone(&term))?;
127 //! #   #[cfg(not(windows))]
128 //!     signal_flag::register(signal_hook::SIGQUIT, Arc::clone(&term))?;
129 //!     while !term.load(Ordering::Relaxed) {
130 //!         // Using swap here, not load, to reset it back to false once it is reloaded.
131 //!         if reload.swap(false, Ordering::Relaxed) {
132 //!             // Reload the config here
133 //! #
134 //! #           // Hiden hack to make the example terminate when run as doc-test. Not part of the
135 //! #           // real code.
136 //! #           term.store(true, Ordering::Relaxed);
137 //!         }
138 //!         // Serve one request
139 //!     }
140 //!     Ok(())
141 //! }
142 //! ```
143 
144 use std::io::Error;
145 use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
146 use std::sync::Arc;
147 
148 use libc::c_int;
149 
150 use crate::SigId;
151 
152 /// Registers an action to set the flag to `true` whenever the given signal arrives.
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 { crate::register(signal, move || flag.store(true, 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 { crate::register(signal, move || flag.store(value, Ordering::SeqCst)) }
164 }
165 
166 #[cfg(test)]
167 mod tests {
168     use std::sync::atomic;
169     use std::time::{Duration, Instant};
170 
171     use super::*;
172 
173     fn self_signal() {
174         unsafe {
175             #[cfg(not(windows))]
176             libc::raise(crate::SIGUSR1);
177             #[cfg(windows)]
178             libc::raise(crate::SIGTERM);
179         }
180     }
181 
182     fn wait_flag(flag: &AtomicBool) -> bool {
183         let start = Instant::now();
184         while !flag.load(Ordering::Relaxed) {
185             atomic::spin_loop_hint();
186             if Instant::now() - start > Duration::from_secs(1) {
187                 // We reached a timeout and nothing happened yet.
188                 // In theory, using timeouts for thread-synchronization tests is wrong, but a
189                 // second should be enough in practice.
190                 return false;
191             }
192         }
193         true
194     }
195 
196     #[test]
197     fn register_unregister() {
198         // When we register the action, it is active.
199         let flag = Arc::new(AtomicBool::new(false));
200         #[cfg(not(windows))]
201         let signal = register(crate::SIGUSR1, Arc::clone(&flag)).unwrap();
202         #[cfg(windows)]
203         let signal = register(crate::SIGTERM, Arc::clone(&flag)).unwrap();
204         self_signal();
205         assert!(wait_flag(&flag));
206         // But stops working after it is unregistered.
207         assert!(crate::unregister(signal));
208         flag.store(false, Ordering::Relaxed);
209         self_signal();
210         assert!(!wait_flag(&flag));
211         // And the unregistration actually dropped its copy of the Arc
212         assert_eq!(1, Arc::strong_count(&flag));
213     }
214 }
215