1use std::sync::atomic::{AtomicBool, Ordering};
8use std::sync::Arc;
9
10use crate::error::{Autom8Error, Result};
11
12#[derive(Clone)]
38pub struct SignalHandler {
39 shutdown_flag: Arc<AtomicBool>,
40}
41
42impl SignalHandler {
43 pub fn new() -> Result<Self> {
52 let shutdown_flag = Arc::new(AtomicBool::new(false));
53 let flag_clone = Arc::clone(&shutdown_flag);
54
55 ctrlc::set_handler(move || {
56 flag_clone.store(true, Ordering::SeqCst);
57 })
58 .map_err(|e| Autom8Error::SignalHandler(e.to_string()))?;
59
60 Ok(Self { shutdown_flag })
61 }
62
63 pub fn is_shutdown_requested(&self) -> bool {
70 self.shutdown_flag.load(Ordering::SeqCst)
71 }
72
73 #[cfg(test)]
78 pub fn reset(&self) {
79 self.shutdown_flag.store(false, Ordering::SeqCst);
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86
87 #[test]
88 fn test_handler_can_be_created() {
89 let shutdown_flag = Arc::new(AtomicBool::new(false));
93 let handler = SignalHandler {
94 shutdown_flag: shutdown_flag.clone(),
95 };
96
97 assert!(!handler.is_shutdown_requested());
99 }
100
101 #[test]
102 fn test_is_shutdown_requested_returns_false_initially() {
103 let shutdown_flag = Arc::new(AtomicBool::new(false));
104 let handler = SignalHandler { shutdown_flag };
105
106 assert!(!handler.is_shutdown_requested());
107 }
108
109 #[test]
110 fn test_is_shutdown_requested_returns_true_when_flag_set() {
111 let shutdown_flag = Arc::new(AtomicBool::new(false));
112 let handler = SignalHandler {
113 shutdown_flag: shutdown_flag.clone(),
114 };
115
116 shutdown_flag.store(true, Ordering::SeqCst);
118
119 assert!(handler.is_shutdown_requested());
120 }
121
122 #[test]
123 fn test_handler_is_thread_safe() {
124 let shutdown_flag = Arc::new(AtomicBool::new(false));
125 let handler = SignalHandler {
126 shutdown_flag: shutdown_flag.clone(),
127 };
128
129 let handler_clone = handler.clone();
131
132 shutdown_flag.store(true, Ordering::SeqCst);
134
135 assert!(handler_clone.is_shutdown_requested());
137 assert!(handler.is_shutdown_requested());
138 }
139
140 #[test]
141 fn test_handler_clone_shares_state() {
142 let shutdown_flag = Arc::new(AtomicBool::new(false));
143 let handler1 = SignalHandler {
144 shutdown_flag: shutdown_flag.clone(),
145 };
146 let handler2 = handler1.clone();
147
148 assert!(!handler1.is_shutdown_requested());
150 assert!(!handler2.is_shutdown_requested());
151
152 shutdown_flag.store(true, Ordering::SeqCst);
154
155 assert!(handler1.is_shutdown_requested());
157 assert!(handler2.is_shutdown_requested());
158 }
159
160 #[test]
161 fn test_reset_clears_shutdown_flag() {
162 let shutdown_flag = Arc::new(AtomicBool::new(true));
163 let handler = SignalHandler { shutdown_flag };
164
165 assert!(handler.is_shutdown_requested());
166
167 handler.reset();
168
169 assert!(!handler.is_shutdown_requested());
170 }
171}