1use std::sync::Arc;
6use tokio::sync::broadcast;
7
8use super::ShutdownCoordinator;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum Signal {
13 Term,
15 Int,
17 Hup,
19}
20
21pub struct SignalHandler {
23 coordinator: Arc<ShutdownCoordinator>,
25 reload_tx: broadcast::Sender<()>,
27}
28
29impl SignalHandler {
30 pub fn new(coordinator: Arc<ShutdownCoordinator>) -> Self {
32 let (reload_tx, _) = broadcast::channel(16);
33 Self {
34 coordinator,
35 reload_tx,
36 }
37 }
38
39 pub fn subscribe_reload(&self) -> broadcast::Receiver<()> {
41 self.reload_tx.subscribe()
42 }
43
44 pub fn trigger_reload(&self) {
46 let _ = self.reload_tx.send(());
47 }
48
49 pub fn coordinator(&self) -> &Arc<ShutdownCoordinator> {
51 &self.coordinator
52 }
53
54 #[cfg(unix)]
56 pub async fn setup_handlers(self: Arc<Self>) {
57 use tokio::signal::unix::{signal, SignalKind};
58
59 let handler = Arc::clone(&self);
60
61 let term_handler = Arc::clone(&handler);
63 tokio::spawn(async move {
64 let mut sigterm =
65 signal(SignalKind::terminate()).expect("Failed to setup SIGTERM handler");
66 loop {
67 sigterm.recv().await;
68 log_signal(Signal::Term);
69 term_handler.coordinator.shutdown();
70 }
71 });
72
73 let int_handler = Arc::clone(&handler);
75 tokio::spawn(async move {
76 let mut sigint =
77 signal(SignalKind::interrupt()).expect("Failed to setup SIGINT handler");
78 loop {
79 sigint.recv().await;
80 log_signal(Signal::Int);
81 int_handler.coordinator.shutdown();
82 }
83 });
84
85 let hup_handler = Arc::clone(&handler);
87 tokio::spawn(async move {
88 let mut sighup = signal(SignalKind::hangup()).expect("Failed to setup SIGHUP handler");
89 loop {
90 sighup.recv().await;
91 log_signal(Signal::Hup);
92 hup_handler.trigger_reload();
93 }
94 });
95 }
96
97 #[cfg(windows)]
99 pub async fn setup_handlers(self: Arc<Self>) {
100 use tokio::signal::ctrl_c;
101
102 let handler = Arc::clone(&self);
103
104 tokio::spawn(async move {
106 loop {
107 if ctrl_c().await.is_ok() {
108 log_signal(Signal::Int);
109 handler.coordinator.shutdown();
110 }
111 }
112 });
113 }
114
115 pub async fn wait_for_shutdown(&self) {
117 let mut rx = self.coordinator.subscribe();
118 let _ = rx.recv().await;
119 }
120}
121
122fn log_signal(signal: Signal) {
124 match signal {
125 Signal::Term => eprintln!("Received SIGTERM, initiating graceful shutdown..."),
126 Signal::Int => eprintln!("Received SIGINT, initiating graceful shutdown..."),
127 Signal::Hup => eprintln!("Received SIGHUP, reloading configuration..."),
128 }
129}
130
131pub async fn setup_default_handlers(coordinator: Arc<ShutdownCoordinator>) -> Arc<SignalHandler> {
133 let handler = Arc::new(SignalHandler::new(coordinator));
134 handler.clone().setup_handlers().await;
135 handler
136}
137
138pub async fn wait_for_ctrl_c() {
140 #[cfg(unix)]
141 {
142 use tokio::signal::unix::{signal, SignalKind};
143 let mut sigint = signal(SignalKind::interrupt()).expect("Failed to setup SIGINT handler");
144 let mut sigterm = signal(SignalKind::terminate()).expect("Failed to setup SIGTERM handler");
145
146 tokio::select! {
147 _ = sigint.recv() => {
148 log_signal(Signal::Int);
149 }
150 _ = sigterm.recv() => {
151 log_signal(Signal::Term);
152 }
153 }
154 }
155
156 #[cfg(windows)]
157 {
158 use tokio::signal::ctrl_c;
159 let _ = ctrl_c().await;
160 log_signal(Signal::Int);
161 }
162}
163
164#[cfg(test)]
165mod tests {
166 use super::*;
167 use std::time::Duration;
168
169 #[tokio::test]
170 async fn test_signal_handler_creation() {
171 let coord = Arc::new(ShutdownCoordinator::default());
172 let handler = SignalHandler::new(coord);
173
174 assert!(!handler.coordinator().is_shutdown());
175 }
176
177 #[tokio::test]
178 async fn test_reload_subscription() {
179 let coord = Arc::new(ShutdownCoordinator::default());
180 let handler = SignalHandler::new(coord);
181
182 let mut rx = handler.subscribe_reload();
183
184 handler.trigger_reload();
186
187 let result = tokio::time::timeout(Duration::from_millis(100), rx.recv()).await;
189 assert!(result.is_ok());
190 }
191
192 #[tokio::test]
193 async fn test_wait_for_shutdown() {
194 let coord = Arc::new(ShutdownCoordinator::default());
195 let handler = Arc::new(SignalHandler::new(Arc::clone(&coord)));
196
197 let handler_clone = Arc::clone(&handler);
198
199 tokio::spawn(async move {
201 tokio::time::sleep(Duration::from_millis(10)).await;
202 handler_clone.coordinator().shutdown();
203 });
204
205 let result =
207 tokio::time::timeout(Duration::from_millis(100), handler.wait_for_shutdown()).await;
208 assert!(result.is_ok());
209 }
210}