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};
7use tracing::{debug, trace};
8
9/// Signal type for cross-thread communication
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum SignalType {
12    /// Reload configuration (SIGHUP)
13    Reload,
14    /// Graceful shutdown (SIGTERM/SIGINT)
15    Shutdown,
16}
17
18/// Signal manager for handling OS signals with async integration
19///
20/// Bridges thread-based signal handlers with the async runtime using channels.
21pub struct SignalManager {
22    /// Sender for signal notifications
23    tx: mpsc::Sender<SignalType>,
24    /// Receiver for signal notifications (wrapped for async)
25    rx: Arc<Mutex<mpsc::Receiver<SignalType>>>,
26}
27
28impl SignalManager {
29    /// Create a new signal manager
30    pub fn new() -> Self {
31        debug!("Creating signal manager");
32        let (tx, rx) = mpsc::channel();
33        Self {
34            tx,
35            rx: Arc::new(Mutex::new(rx)),
36        }
37    }
38
39    /// Get a sender for use in signal handlers
40    pub fn sender(&self) -> mpsc::Sender<SignalType> {
41        trace!("Cloning signal sender for handler");
42        self.tx.clone()
43    }
44
45    /// Receive the next signal (blocking)
46    ///
47    /// This should be called from an async context using spawn_blocking
48    pub fn recv_blocking(&self) -> Option<SignalType> {
49        trace!("Waiting for signal (blocking)");
50        let signal = self.rx.lock().ok()?.recv().ok();
51        if let Some(ref s) = signal {
52            debug!(signal = ?s, "Received signal");
53        }
54        signal
55    }
56
57    /// Try to receive a signal without blocking
58    pub fn try_recv(&self) -> Option<SignalType> {
59        let signal = self.rx.lock().ok()?.try_recv().ok();
60        if let Some(ref s) = signal {
61            debug!(signal = ?s, "Received signal (non-blocking)");
62        }
63        signal
64    }
65}
66
67impl Default for SignalManager {
68    fn default() -> Self {
69        Self::new()
70    }
71}