sentinel_proxy/reload/
signals.rs

1//! Signal handling for configuration reload and shutdown.
2//!
3//! Bridges OS signals with the async runtime for graceful handling of
4//! SIGHUP (reload) and SIGTERM/SIGINT (shutdown).
5
6use std::sync::{mpsc, Arc, Mutex};
7
8/// Signal type for cross-thread communication
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum SignalType {
11    /// Reload configuration (SIGHUP)
12    Reload,
13    /// Graceful shutdown (SIGTERM/SIGINT)
14    Shutdown,
15}
16
17/// Signal manager for handling OS signals with async integration
18///
19/// Bridges thread-based signal handlers with the async runtime using channels.
20pub struct SignalManager {
21    /// Sender for signal notifications
22    tx: mpsc::Sender<SignalType>,
23    /// Receiver for signal notifications (wrapped for async)
24    rx: Arc<Mutex<mpsc::Receiver<SignalType>>>,
25}
26
27impl SignalManager {
28    /// Create a new signal manager
29    pub fn new() -> Self {
30        let (tx, rx) = mpsc::channel();
31        Self {
32            tx,
33            rx: Arc::new(Mutex::new(rx)),
34        }
35    }
36
37    /// Get a sender for use in signal handlers
38    pub fn sender(&self) -> mpsc::Sender<SignalType> {
39        self.tx.clone()
40    }
41
42    /// Receive the next signal (blocking)
43    ///
44    /// This should be called from an async context using spawn_blocking
45    pub fn recv_blocking(&self) -> Option<SignalType> {
46        self.rx.lock().ok()?.recv().ok()
47    }
48
49    /// Try to receive a signal without blocking
50    pub fn try_recv(&self) -> Option<SignalType> {
51        self.rx.lock().ok()?.try_recv().ok()
52    }
53}
54
55impl Default for SignalManager {
56    fn default() -> Self {
57        Self::new()
58    }
59}