zerodds_corba_ccm/
timer.rs1use alloc::collections::BTreeMap;
10use alloc::sync::Arc;
11use core::sync::atomic::{AtomicBool, AtomicU64, Ordering};
12use std::sync::Mutex;
13use std::thread::{self, JoinHandle};
14use std::time::{Duration, Instant};
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum TimerKind {
19 OneShot,
21 Periodic,
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
27pub struct TimerHandle(pub u64);
28
29pub trait TimerCallback: Send + Sync {
31 fn fire(&self, handle: TimerHandle);
33}
34
35struct TimerEntry {
36 kind: TimerKind,
37 next_fire: Instant,
38 period: Duration,
39 callback: Arc<dyn TimerCallback>,
40}
41
42struct ServiceInner {
43 next_handle: AtomicU64,
44 timers: Mutex<BTreeMap<TimerHandle, TimerEntry>>,
45 shutdown: AtomicBool,
46}
47
48pub struct TimerEventService {
50 inner: Arc<ServiceInner>,
51 worker: Option<JoinHandle<()>>,
52}
53
54impl core::fmt::Debug for TimerEventService {
55 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
56 let n = self.inner.timers.lock().ok().map(|g| g.len()).unwrap_or(0);
57 f.debug_struct("TimerEventService")
58 .field("active_timers", &n)
59 .finish()
60 }
61}
62
63impl Default for TimerEventService {
64 fn default() -> Self {
65 Self::new()
66 }
67}
68
69impl TimerEventService {
70 #[must_use]
72 pub fn new() -> Self {
73 let inner = Arc::new(ServiceInner {
74 next_handle: AtomicU64::new(1),
75 timers: Mutex::new(BTreeMap::new()),
76 shutdown: AtomicBool::new(false),
77 });
78 let inner_w = Arc::clone(&inner);
79 let worker = thread::Builder::new()
80 .name("ccm-timer-service".into())
81 .spawn(move || run_worker(&inner_w))
82 .ok();
83 Self { inner, worker }
84 }
85
86 pub fn create_one_shot(&self, delay: Duration, cb: Arc<dyn TimerCallback>) -> TimerHandle {
88 self.create_internal(TimerKind::OneShot, delay, delay, cb)
89 }
90
91 pub fn create_periodic(&self, period: Duration, cb: Arc<dyn TimerCallback>) -> TimerHandle {
93 self.create_internal(TimerKind::Periodic, period, period, cb)
94 }
95
96 fn create_internal(
97 &self,
98 kind: TimerKind,
99 delay: Duration,
100 period: Duration,
101 callback: Arc<dyn TimerCallback>,
102 ) -> TimerHandle {
103 let handle = TimerHandle(self.inner.next_handle.fetch_add(1, Ordering::Relaxed));
104 let entry = TimerEntry {
105 kind,
106 next_fire: Instant::now() + delay,
107 period,
108 callback,
109 };
110 if let Ok(mut g) = self.inner.timers.lock() {
111 g.insert(handle, entry);
112 }
113 handle
114 }
115
116 pub fn cancel(&self, handle: TimerHandle) -> bool {
118 self.inner
119 .timers
120 .lock()
121 .map(|mut g| g.remove(&handle).is_some())
122 .unwrap_or(false)
123 }
124
125 #[must_use]
127 pub fn active_count(&self) -> usize {
128 self.inner.timers.lock().map(|g| g.len()).unwrap_or(0)
129 }
130
131 pub fn shutdown(mut self) {
133 self.inner.shutdown.store(true, Ordering::Release);
134 if let Some(j) = self.worker.take() {
135 let _ = j.join();
136 }
137 }
138}
139
140impl Drop for TimerEventService {
141 fn drop(&mut self) {
142 self.inner.shutdown.store(true, Ordering::Release);
143 if let Some(j) = self.worker.take() {
144 let _ = j.join();
145 }
146 }
147}
148
149fn run_worker(inner: &Arc<ServiceInner>) {
150 let tick = Duration::from_millis(20);
151 while !inner.shutdown.load(Ordering::Acquire) {
152 let now = Instant::now();
153 let mut to_fire: alloc::vec::Vec<(TimerHandle, Arc<dyn TimerCallback>)> = alloc::vec![];
154 let mut to_remove: alloc::vec::Vec<TimerHandle> = alloc::vec![];
155 if let Ok(mut g) = inner.timers.lock() {
156 for (h, e) in g.iter_mut() {
157 if e.next_fire <= now {
158 to_fire.push((*h, Arc::clone(&e.callback)));
159 match e.kind {
160 TimerKind::OneShot => to_remove.push(*h),
161 TimerKind::Periodic => e.next_fire = now + e.period,
162 }
163 }
164 }
165 for h in &to_remove {
166 g.remove(h);
167 }
168 }
169 for (h, cb) in to_fire {
170 cb.fire(h);
171 }
172 thread::sleep(tick);
173 }
174}
175
176#[cfg(test)]
177#[allow(clippy::expect_used, clippy::unwrap_used, clippy::panic)]
178mod tests {
179 use super::*;
180 use core::sync::atomic::AtomicUsize;
181
182 struct CountingCallback {
183 fired: Arc<AtomicUsize>,
184 }
185 impl TimerCallback for CountingCallback {
186 fn fire(&self, _: TimerHandle) {
187 self.fired.fetch_add(1, Ordering::Relaxed);
188 }
189 }
190
191 fn waitfor(c: &Arc<AtomicUsize>, target: usize, timeout: Duration) {
192 let start = Instant::now();
193 while c.load(Ordering::Relaxed) < target && start.elapsed() < timeout {
194 thread::sleep(Duration::from_millis(20));
195 }
196 }
197
198 #[test]
199 fn one_shot_fires_once() {
200 let svc = TimerEventService::new();
201 let counter = Arc::new(AtomicUsize::new(0));
202 let cb = Arc::new(CountingCallback {
203 fired: Arc::clone(&counter),
204 });
205 let _ = svc.create_one_shot(Duration::from_millis(50), cb);
206 waitfor(&counter, 1, Duration::from_secs(2));
207 assert_eq!(counter.load(Ordering::Relaxed), 1);
208 thread::sleep(Duration::from_millis(150));
210 assert_eq!(svc.active_count(), 0);
211 }
212
213 #[test]
214 fn periodic_fires_multiple_times() {
215 let svc = TimerEventService::new();
216 let counter = Arc::new(AtomicUsize::new(0));
217 let cb = Arc::new(CountingCallback {
218 fired: Arc::clone(&counter),
219 });
220 let h = svc.create_periodic(Duration::from_millis(50), cb);
221 waitfor(&counter, 3, Duration::from_secs(3));
222 assert!(counter.load(Ordering::Relaxed) >= 3);
223 svc.cancel(h);
224 }
225
226 #[test]
227 fn cancel_stops_periodic() {
228 let svc = TimerEventService::new();
229 let counter = Arc::new(AtomicUsize::new(0));
230 let cb = Arc::new(CountingCallback {
231 fired: Arc::clone(&counter),
232 });
233 let h = svc.create_periodic(Duration::from_millis(50), cb);
234 thread::sleep(Duration::from_millis(150));
235 assert!(svc.cancel(h));
236 let after = counter.load(Ordering::Relaxed);
237 thread::sleep(Duration::from_millis(200));
238 assert_eq!(counter.load(Ordering::Relaxed), after);
239 }
240
241 #[test]
242 fn cancel_unknown_returns_false() {
243 let svc = TimerEventService::new();
244 assert!(!svc.cancel(TimerHandle(9999)));
245 }
246}