nexus_memory_hooks/
signal.rs1use futures::StreamExt;
7use std::sync::Arc;
8use tokio::sync::{broadcast, Mutex};
9
10use crate::error::{HookError, Result};
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum SignalEvent {
15 Interrupt,
17
18 Terminate,
20
21 Hangup,
23
24 User1,
26
27 User2,
29}
30
31pub struct SignalConfig {
33 pub handle_interrupt: bool,
35
36 pub handle_terminate: bool,
38
39 pub handle_hangup: bool,
41}
42
43impl Default for SignalConfig {
44 fn default() -> Self {
45 Self {
46 handle_interrupt: true,
47 handle_terminate: true,
48 handle_hangup: false,
49 }
50 }
51}
52
53pub struct SignalHandler {
55 event_sender: broadcast::Sender<SignalEvent>,
57
58 config: SignalConfig,
60
61 installed: Arc<Mutex<bool>>,
63}
64
65impl SignalHandler {
66 pub fn new() -> Self {
68 let (event_sender, _) = broadcast::channel(16);
69
70 Self {
71 event_sender,
72 config: SignalConfig::default(),
73 installed: Arc::new(Mutex::new(false)),
74 }
75 }
76
77 pub fn with_config(config: SignalConfig) -> Self {
79 let (event_sender, _) = broadcast::channel(16);
80
81 Self {
82 event_sender,
83 config,
84 installed: Arc::new(Mutex::new(false)),
85 }
86 }
87
88 pub fn subscribe(&self) -> broadcast::Receiver<SignalEvent> {
90 self.event_sender.subscribe()
91 }
92
93 #[cfg(unix)]
95 pub async fn install(&self) -> Result<()> {
96 let mut installed = self.installed.lock().await;
97 if *installed {
98 return Ok(());
99 }
100
101 use signal_hook::consts::*;
102 use signal_hook_tokio::Signals;
103
104 let mut signals_to_handle = Vec::new();
105
106 if self.config.handle_interrupt {
107 signals_to_handle.push(SIGINT);
108 }
109 if self.config.handle_terminate {
110 signals_to_handle.push(SIGTERM);
111 }
112 if self.config.handle_hangup {
113 signals_to_handle.push(SIGHUP);
114 }
115
116 let mut signals = Signals::new(signals_to_handle).map_err(|e| {
117 HookError::SignalError(format!("Failed to create signal handler: {}", e))
118 })?;
119
120 let event_sender = self.event_sender.clone();
121 let _installed_flag = self.installed.clone();
122
123 tokio::spawn(async move {
124 while let Some(signal) = signals.next().await {
125 let event = match signal {
126 SIGINT => SignalEvent::Interrupt,
127 SIGTERM => SignalEvent::Terminate,
128 SIGHUP => SignalEvent::Hangup,
129 SIGUSR1 => SignalEvent::User1,
130 SIGUSR2 => SignalEvent::User2,
131 _ => continue,
132 };
133
134 let _ = event_sender.send(event);
135 }
136 });
137
138 *installed = true;
139 Ok(())
140 }
141
142 #[cfg(windows)]
144 pub async fn install(&self) -> Result<()> {
145 let mut installed = self.installed.lock().await;
146 if *installed {
147 return Ok(());
148 }
149
150 use tokio::signal;
151
152 let event_sender = self.event_sender.clone();
153 let event_sender_ctrl = event_sender.clone();
154
155 if self.config.handle_interrupt {
157 tokio::spawn(async move {
158 match signal::ctrl_c().await {
159 Ok(()) => {
160 let _ = event_sender_ctrl.send(SignalEvent::Interrupt);
161 }
162 Err(e) => {
163 tracing::error!("Ctrl+C handler error: {}", e);
164 }
165 }
166 });
167 }
168
169 *installed = true;
170 Ok(())
171 }
172
173 pub async fn is_installed(&self) -> bool {
175 *self.installed.lock().await
176 }
177
178 pub fn send(&self, event: SignalEvent) -> Result<()> {
180 self.event_sender
181 .send(event)
182 .map_err(|e| HookError::SignalError(format!("Failed to send signal: {}", e)))?;
183 Ok(())
184 }
185}
186
187impl Default for SignalHandler {
188 fn default() -> Self {
189 Self::new()
190 }
191}
192
193pub async fn on_signal<F>(handler: &SignalHandler, callback: F) -> Result<()>
195where
196 F: FnOnce() + Send + 'static,
197{
198 let mut receiver = handler.subscribe();
199
200 tokio::spawn(async move {
201 if let Ok(_signal) = receiver.recv().await {
202 callback();
203 }
204 });
205
206 Ok(())
207}
208
209pub async fn on_signal_async<F, Fut>(handler: &SignalHandler, callback: F) -> Result<()>
211where
212 F: FnOnce() -> Fut + Send + 'static,
213 Fut: std::future::Future<Output = ()> + Send,
214{
215 let mut receiver = handler.subscribe();
216
217 tokio::spawn(async move {
218 if let Ok(_signal) = receiver.recv().await {
219 callback().await;
220 }
221 });
222
223 Ok(())
224}
225
226#[cfg(test)]
227mod tests {
228 use super::*;
229
230 #[tokio::test]
231 async fn test_signal_handler_new() {
232 let handler = SignalHandler::new();
233 assert!(!handler.is_installed().await);
234 }
235
236 #[tokio::test]
237 async fn test_signal_handler_subscribe() {
238 let handler = SignalHandler::new();
239 let mut receiver = handler.subscribe();
240
241 handler.send(SignalEvent::Interrupt).unwrap();
243
244 let event = receiver.try_recv();
245 assert!(event.is_ok());
246 assert_eq!(event.unwrap(), SignalEvent::Interrupt);
247 }
248
249 #[tokio::test]
250 async fn test_signal_config_default() {
251 let config = SignalConfig::default();
252 assert!(config.handle_interrupt);
253 assert!(config.handle_terminate);
254 assert!(!config.handle_hangup);
255 }
256}