mptc/
signals.rs

1/// Signal Tracking
2use signal_hook as sigs;
3use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
4use std::sync::Arc;
5use std::time::SystemTime;
6
7pub const SIG_FAST_SHUTDOWN: i32 = sigs::consts::SIGTERM;
8pub const SIG_GRACEFUL_SHUTDOWN: i32 = sigs::consts::SIGINT;
9pub const SIG_RELOAD: i32 = sigs::consts::SIGHUP;
10
11/// Tracks various signals so threaded, etc. applications can
12/// easily respond to received signals.
13///
14/// Generally, tracking is setup by the main thread and the SignalTracker
15/// is cloned and distributed to the worker/spawned threads, so all
16/// parties refer to the same tracker for signal events.
17///
18/// track_ methods panic on failure, based on the assumption that
19/// applications register event handlers early on and should exit early
20/// if basic signal handling cannot be setup.
21#[derive(Debug, Clone)]
22pub struct SignalTracker {
23    graceful_shutdown: Arc<AtomicBool>,
24    fast_shutdown: Arc<AtomicBool>,
25    reload: Arc<AtomicBool>,
26    reload_request_time: Arc<AtomicU64>,
27    usr1_is_set: Arc<AtomicBool>,
28    usr2_is_set: Arc<AtomicBool>,
29
30    /// Avoid duplicate signal handlers
31    graceful_shutdown_tracked: bool,
32    fast_shutdown_tracked: bool,
33    reload_tracked: bool,
34    usr1_tracked: bool,
35    usr2_tracked: bool,
36}
37
38impl Default for SignalTracker {
39    fn default() -> Self {
40        Self::new()
41    }
42}
43
44impl SignalTracker {
45    pub fn new() -> SignalTracker {
46        SignalTracker {
47            graceful_shutdown: Arc::new(AtomicBool::new(false)),
48            fast_shutdown: Arc::new(AtomicBool::new(false)),
49            reload: Arc::new(AtomicBool::new(false)),
50            reload_request_time: Arc::new(AtomicU64::new(0)),
51            graceful_shutdown_tracked: false,
52            fast_shutdown_tracked: false,
53            reload_tracked: false,
54            usr1_is_set: Arc::new(AtomicBool::new(false)),
55            usr2_is_set: Arc::new(AtomicBool::new(false)),
56            usr1_tracked: false,
57            usr2_tracked: false,
58        }
59    }
60
61    /// Directly initiate a graceful shutdown request.
62    pub fn request_graceful_shutdown(&self) {
63        self.graceful_shutdown.store(true, Ordering::Relaxed);
64    }
65
66    /// Directly initiate a fast shutdown request.
67    pub fn request_fast_shutdown(&self) {
68        self.fast_shutdown.store(true, Ordering::Relaxed);
69    }
70
71    /// Directly initiate a reload request.
72    pub fn request_reload(&self) {
73        self.reload.store(true, Ordering::Relaxed);
74    }
75
76    /// True if any shutdown signals have been received.
77    pub fn any_shutdown_requested(&self) -> bool {
78        self.graceful_shutdown_requested() || self.fast_shutdown_requested()
79    }
80
81    pub fn track_usr1(&mut self) {
82        if self.usr1_tracked {
83            log::warn!("Already tracking SIGUSR1");
84            return;
85        }
86
87        let result = sigs::flag::register(sigs::consts::SIGUSR1, self.usr1_is_set.clone());
88
89        if let Err(e) = result {
90            panic!("Cannot register graceful usr1 handler: {}", e);
91        }
92
93        self.usr1_tracked = true;
94    }
95
96    pub fn usr1_is_set(&self) -> bool {
97        self.usr1_is_set.load(Ordering::Relaxed)
98    }
99
100    pub fn clear_usr1(&mut self) {
101        self.usr1_is_set.store(false, Ordering::Relaxed);
102    }
103
104    pub fn track_usr2(&mut self) {
105        if self.usr2_tracked {
106            log::warn!("Already tracking SIGUSR2");
107            return;
108        }
109
110        let result = sigs::flag::register(sigs::consts::SIGUSR2, self.usr2_is_set.clone());
111
112        if let Err(e) = result {
113            panic!("Cannot register graceful usr2 handler: {}", e);
114        }
115
116        self.usr2_tracked = true;
117    }
118
119    pub fn usr2_is_set(&self) -> bool {
120        self.usr2_is_set.load(Ordering::Relaxed)
121    }
122
123    pub fn clear_usr2(&mut self) {
124        self.usr2_is_set.store(false, Ordering::Relaxed);
125    }
126
127    /// Activate graceful shutdown signal tracking.
128    ///
129    /// ```
130    /// use mptc::signals::SignalTracker;
131    /// use signal_hook::low_level::raise;
132    ///
133    /// let mut tracker = SignalTracker::new();
134    /// tracker.track_graceful_shutdown();
135    ///
136    /// raise(mptc::signals::SIG_GRACEFUL_SHUTDOWN).expect("Signal Sent");
137    ///
138    /// assert!(tracker.graceful_shutdown_requested());
139    /// assert!(tracker.any_shutdown_requested());
140    ///
141    /// ```
142    pub fn track_graceful_shutdown(&mut self) {
143        if self.graceful_shutdown_tracked {
144            log::warn!("Already tracking graceful shutdowns");
145            return;
146        }
147
148        let result = sigs::flag::register(SIG_GRACEFUL_SHUTDOWN, self.graceful_shutdown.clone());
149
150        if let Err(e) = result {
151            panic!("Cannot register graceful shutdown handler: {}", e);
152        }
153
154        self.graceful_shutdown_tracked = true;
155    }
156
157    pub fn graceful_shutdown_requested(&self) -> bool {
158        self.graceful_shutdown.load(Ordering::Relaxed)
159    }
160
161    /// Activate fast shutdown signal tracking.
162    ///
163    /// ```
164    /// use mptc::signals::SignalTracker;
165    /// use signal_hook::low_level::raise;
166    ///
167    /// let mut tracker = SignalTracker::new();
168    /// tracker.track_fast_shutdown();
169    ///
170    /// raise(mptc::signals::SIG_FAST_SHUTDOWN).expect("Signal Sent");
171    ///
172    /// assert!(tracker.fast_shutdown_requested());
173    /// assert!(tracker.any_shutdown_requested());
174    /// ```
175    pub fn track_fast_shutdown(&mut self) {
176        if self.fast_shutdown_tracked {
177            log::warn!("Already tracking fast shutdowns");
178            return;
179        }
180
181        let result = sigs::flag::register(SIG_FAST_SHUTDOWN, self.fast_shutdown.clone());
182
183        if let Err(e) = result {
184            panic!("Cannot register fast shutdown handler: {}", e);
185        }
186
187        self.fast_shutdown_tracked = true;
188    }
189
190    pub fn fast_shutdown_requested(&self) -> bool {
191        self.fast_shutdown.load(Ordering::Relaxed)
192    }
193
194    /// Activate fast shutdown signal tracking.
195    ///
196    /// ```
197    /// use mptc::signals::SignalTracker;
198    /// use signal_hook::low_level::raise;
199    ///
200    /// let mut tracker = SignalTracker::new();
201    /// tracker.track_reload();
202    ///
203    /// raise(mptc::signals::SIG_RELOAD).expect("Signal Sent");
204    ///
205    /// assert!(tracker.reload_requested());
206    ///
207    /// tracker.handle_reload_requested();
208    ///
209    /// assert!(!tracker.reload_requested());
210    /// assert!(tracker.reload_request_time() > 0);
211    ///
212    /// ```
213    pub fn track_reload(&mut self) {
214        if self.reload_tracked {
215            log::warn!("Already tracking reload signals");
216            return;
217        }
218
219        let result = sigs::flag::register(SIG_RELOAD, self.reload.clone());
220
221        if let Err(e) = result {
222            panic!("Cannot register fast shutdown handler: {}", e);
223        }
224
225        self.reload_tracked = true;
226    }
227
228    pub fn reload_requested(&self) -> bool {
229        self.reload.load(Ordering::Relaxed)
230    }
231
232    /// Reset the reload request flag, which may be needed again later,
233    /// and store the time of the most recent reload request.
234    pub fn handle_reload_requested(&mut self) {
235        self.reload.store(false, Ordering::Relaxed);
236
237        let epoch: u64 = SystemTime::now()
238            .duration_since(SystemTime::UNIX_EPOCH)
239            .expect("Epoch Duration Is Sane")
240            .as_millis()
241            // should be fine for another half billion years or so, I think.
242            .try_into()
243            .expect("Epoch Milliseconds is way too big?");
244
245        self.reload_request_time.store(epoch, Ordering::Relaxed);
246    }
247
248    /// Epoch milliseconds of the reload request time.
249    ///
250    /// Workers spawned before this time know to exit, since they
251    /// are presumably operating with outdated configuration, etc.
252    /// information.
253    pub fn reload_request_time(&self) -> u64 {
254        self.reload_request_time.load(Ordering::Relaxed)
255    }
256}