1use std::sync::atomic::{AtomicBool, Ordering};
4use std::sync::Arc;
5
6static CLEANUP_REQUESTED: AtomicBool = AtomicBool::new(false);
8
9pub fn is_cleanup_requested() -> bool {
11 CLEANUP_REQUESTED.load(Ordering::SeqCst)
12}
13
14pub fn request_cleanup() {
16 CLEANUP_REQUESTED.store(true, Ordering::SeqCst);
17}
18
19pub fn register_signal_handlers() -> anyhow::Result<()> {
22 ctrlc::set_handler(move || {
24 eprintln!("\nReceived interrupt signal, cleaning up...");
25 request_cleanup();
26 std::process::exit(5); })
28 .map_err(|e| anyhow::anyhow!("Failed to set Ctrl+C handler: {}", e))?;
29
30 #[cfg(unix)]
32 {
33 use signal_hook::consts::signal::*;
34 use signal_hook::flag;
35
36 let cleanup_flag = Arc::new(AtomicBool::new(false));
37
38 flag::register(SIGTERM, Arc::clone(&cleanup_flag))
40 .map_err(|e| anyhow::anyhow!("Failed to register SIGTERM handler: {}", e))?;
41
42 flag::register(SIGHUP, Arc::clone(&cleanup_flag))
44 .map_err(|e| anyhow::anyhow!("Failed to register SIGHUP handler: {}", e))?;
45
46 std::thread::spawn(move || loop {
48 if cleanup_flag.load(Ordering::Relaxed) {
49 eprintln!("\nReceived termination signal, cleaning up...");
50 request_cleanup();
51 std::process::exit(5);
52 }
53 std::thread::sleep(std::time::Duration::from_millis(100));
54 });
55 }
56
57 Ok(())
58}
59
60pub struct CleanupGuard {
62 cleanup_fn: Option<Box<dyn FnOnce() + Send>>,
63}
64
65impl CleanupGuard {
66 pub fn new<F>(cleanup_fn: F) -> Self
68 where
69 F: FnOnce() + Send + 'static,
70 {
71 Self {
72 cleanup_fn: Some(Box::new(cleanup_fn)),
73 }
74 }
75
76 pub fn cleanup(mut self) {
78 if let Some(cleanup_fn) = self.cleanup_fn.take() {
79 cleanup_fn();
80 }
81 }
82}
83
84impl Drop for CleanupGuard {
85 fn drop(&mut self) {
86 if let Some(cleanup_fn) = self.cleanup_fn.take() {
87 cleanup_fn();
88 }
89 }
90}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95
96 #[test]
97 fn test_cleanup_flag() {
98 assert!(!is_cleanup_requested());
99 request_cleanup();
100 assert!(is_cleanup_requested());
101 }
102
103 #[test]
104 fn test_cleanup_guard() {
105 use std::sync::Arc;
106 let cleaned = Arc::new(AtomicBool::new(false));
107 let cleaned_clone = Arc::clone(&cleaned);
108
109 {
110 let _guard = CleanupGuard::new(move || {
111 cleaned_clone.store(true, Ordering::SeqCst);
112 });
113 assert!(!cleaned.load(Ordering::SeqCst));
114 }
115
116 assert!(cleaned.load(Ordering::SeqCst));
117 }
118}