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