dstar_gateway_core/session/
timer_wheel.rs1use std::collections::HashMap;
9use std::time::Instant;
10
11pub(crate) type TimerId = &'static str;
15
16#[derive(Debug, Default)]
18pub(crate) struct TimerWheel {
19 timers: HashMap<TimerId, Instant>,
20}
21
22impl TimerWheel {
23 pub(crate) fn new() -> Self {
24 Self::default()
25 }
26
27 pub(crate) fn set(&mut self, name: TimerId, deadline: Instant) {
29 let _previous = self.timers.insert(name, deadline);
30 }
31
32 pub(crate) fn clear(&mut self, name: TimerId) {
34 let _previous = self.timers.remove(name);
35 }
36
37 pub(crate) fn is_expired(&self, name: TimerId, now: Instant) -> bool {
39 self.timers.get(name).is_some_and(|&d| now >= d)
40 }
41
42 pub(crate) fn next_deadline(&self) -> Option<Instant> {
45 self.timers.values().copied().min()
46 }
47}
48
49#[cfg(test)]
50mod tests {
51 use super::*;
52 use std::time::Duration;
53
54 #[test]
55 fn empty_wheel_has_no_deadline() {
56 let w = TimerWheel::new();
57 assert!(w.next_deadline().is_none());
58 }
59
60 #[test]
61 fn set_and_check_expiry() {
62 let mut w = TimerWheel::new();
63 let now = Instant::now();
64 w.set("keepalive", now + Duration::from_secs(1));
65 assert!(!w.is_expired("keepalive", now));
66 assert!(w.is_expired("keepalive", now + Duration::from_secs(2)));
67 }
68
69 #[test]
70 fn next_deadline_is_earliest() {
71 let mut w = TimerWheel::new();
72 let now = Instant::now();
73 w.set("a", now + Duration::from_secs(5));
74 w.set("b", now + Duration::from_secs(2));
75 w.set("c", now + Duration::from_secs(10));
76 assert_eq!(w.next_deadline(), Some(now + Duration::from_secs(2)));
77 }
78
79 #[test]
80 fn clear_removes_timer() {
81 let mut w = TimerWheel::new();
82 let now = Instant::now();
83 w.set("a", now + Duration::from_secs(1));
84 w.clear("a");
85 assert!(w.next_deadline().is_none());
86 }
87}