subms_timer_wheel/features/
deadline_scheduler.rs1use crate::TimerWheel;
16use std::sync::OnceLock;
17use std::time::{Duration, Instant};
18
19pub trait Clock {
23 fn now_nanos(&self) -> u64;
26}
27
28#[derive(Default)]
29pub struct MonotonicClock {
30 origin: OnceLock<Instant>,
35}
36
37impl MonotonicClock {
38 pub fn new() -> Self {
39 let origin = OnceLock::new();
40 let _ = origin.set(Instant::now());
41 Self { origin }
42 }
43}
44
45impl Clock for MonotonicClock {
46 fn now_nanos(&self) -> u64 {
47 let origin = self.origin.get_or_init(Instant::now);
48 Instant::now().duration_since(*origin).as_nanos() as u64
49 }
50}
51
52pub struct TestClock {
55 now_nanos: std::cell::Cell<u64>,
56}
57
58impl Default for TestClock {
59 fn default() -> Self {
60 Self::new()
61 }
62}
63
64impl TestClock {
65 pub fn new() -> Self {
66 Self {
67 now_nanos: std::cell::Cell::new(0),
68 }
69 }
70
71 pub fn advance(&self, d: Duration) {
72 self.now_nanos
73 .set(self.now_nanos.get().saturating_add(d.as_nanos() as u64));
74 }
75}
76
77impl Clock for TestClock {
78 fn now_nanos(&self) -> u64 {
79 self.now_nanos.get()
80 }
81}
82
83pub struct DeadlineScheduler<V, C: Clock> {
84 wheel: TimerWheel<V>,
85 clock: C,
86 tick_nanos: u64,
87 consumed_nanos: u64,
90}
91
92impl<V, C: Clock> DeadlineScheduler<V, C> {
93 pub fn new(num_slots: usize, clock: C, tick: Duration) -> Self {
96 let tick_nanos = (tick.as_nanos() as u64).max(1);
97 Self {
98 wheel: TimerWheel::new(num_slots),
99 clock,
100 tick_nanos,
101 consumed_nanos: 0,
102 }
103 }
104
105 pub fn tick_nanos(&self) -> u64 {
106 self.tick_nanos
107 }
108
109 pub fn clock(&self) -> &C {
112 &self.clock
113 }
114
115 pub fn pending(&self) -> usize {
116 self.wheel.pending()
117 }
118
119 pub fn is_empty(&self) -> bool {
120 self.wheel.is_empty()
121 }
122
123 pub fn schedule_after(&mut self, delay: Duration, value: V) -> u64 {
126 let ticks = self.nanos_to_ticks(delay.as_nanos() as u64);
127 self.wheel.schedule(ticks, value)
128 }
129
130 pub fn schedule_at(&mut self, when_nanos: u64, value: V) -> u64 {
134 let now = self.clock.now_nanos();
135 let diff = when_nanos.saturating_sub(now);
136 let ticks = self.nanos_to_ticks(diff).max(1);
137 self.wheel.schedule(ticks, value)
138 }
139
140 pub fn cancel(&mut self, id: u64) -> bool {
141 self.wheel.cancel(id)
142 }
143
144 pub fn reschedule_at(&mut self, id: u64, when_nanos: u64) -> bool {
148 let now = self.clock.now_nanos();
149 let diff = when_nanos.saturating_sub(now);
150 let ticks = self.nanos_to_ticks(diff).max(1);
151 self.wheel.reschedule(id, ticks)
152 }
153
154 pub fn reschedule_after(&mut self, id: u64, delay: Duration) -> bool {
155 let ticks = self.nanos_to_ticks(delay.as_nanos() as u64).max(1);
156 self.wheel.reschedule(id, ticks)
157 }
158
159 pub fn drain(&mut self) -> Vec<V> {
161 self.wheel.drain()
162 }
163
164 pub fn poll(&mut self) -> Vec<V> {
169 let now = self.clock.now_nanos();
170 let pending = now.saturating_sub(self.consumed_nanos);
171 let ticks = (pending / self.tick_nanos) as usize;
172 self.consumed_nanos = self
173 .consumed_nanos
174 .saturating_add(ticks as u64 * self.tick_nanos);
175 let mut fired = Vec::new();
176 for _ in 0..ticks {
177 fired.extend(self.wheel.tick());
178 }
179 fired
180 }
181
182 fn nanos_to_ticks(&self, nanos: u64) -> usize {
183 nanos.div_ceil(self.tick_nanos) as usize
184 }
185}
186
187#[cfg(test)]
188#[path = "deadline_scheduler_tests.rs"]
189mod tests;