1use alloc::collections::BTreeMap;
2use core::{
3 pin::Pin,
4 task::{Context, Poll, Waker},
5 time::Duration,
6};
7
8use ax_hal::time::{TimeValue, monotonic_time, wall_time};
9use futures_util::{FutureExt, select_biased};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
12pub(crate) struct TimerKey {
13 deadline: TimeValue,
14 key: u64,
15}
16
17pub(crate) struct TimerRuntime {
18 key: u64,
19 wheel: BTreeMap<TimerKey, Waker>,
20 due_work_published: bool,
24}
25
26impl TimerRuntime {
27 pub(crate) const fn new() -> Self {
28 TimerRuntime {
29 key: 0,
30 wheel: BTreeMap::new(),
31 due_work_published: false,
32 }
33 }
34
35 pub(crate) fn add(&mut self, deadline: TimeValue) -> Option<TimerKey> {
36 if deadline <= monotonic_time() {
37 return None;
38 }
39
40 let key = TimerKey {
41 deadline,
42 key: self.key,
43 };
44 self.wheel.insert(key, Waker::noop().clone());
45 self.key += 1;
46
47 Some(key)
48 }
49
50 pub(crate) fn poll(&mut self, key: &TimerKey, cx: &mut Context<'_>) -> Poll<()> {
51 if let Some(w) = self.wheel.get_mut(key) {
52 *w = cx.waker().clone();
53 Poll::Pending
54 } else {
55 Poll::Ready(())
56 }
57 }
58
59 pub(crate) fn cancel(&mut self, key: &TimerKey) {
60 self.wheel.remove(key);
61 }
62
63 pub(crate) fn next_deadline(&self) -> Option<TimeValue> {
64 if self.due_work_published {
65 return None;
66 }
67 self.wheel.keys().next().map(|key| key.deadline)
68 }
69
70 pub(crate) fn publish_due_work(&mut self, now: TimeValue) -> bool {
71 self.due_work_published |= self
72 .wheel
73 .keys()
74 .next()
75 .is_some_and(|key| key.deadline <= now);
76 self.due_work_published
77 }
78
79 pub(crate) fn finish_due_work(&mut self, now: TimeValue) -> bool {
80 self.due_work_published = self
81 .wheel
82 .keys()
83 .next()
84 .is_some_and(|key| key.deadline <= now);
85 self.due_work_published
86 }
87
88 pub(crate) fn expire_one(&mut self, now: TimeValue) -> Option<Waker> {
89 let key = self
90 .wheel
91 .first_key_value()
92 .and_then(|(key, _)| (key.deadline <= now).then_some(*key))?;
93 self.wheel.remove(&key)
94 }
95}
96
97#[derive(Clone, Copy, Debug, Eq, PartialEq)]
98pub(crate) struct FutureTimerHandle {
99 owner_cpu: usize,
100 key: TimerKey,
101}
102
103impl FutureTimerHandle {
104 pub(crate) const fn new(owner_cpu: usize, key: TimerKey) -> Self {
105 Self { owner_cpu, key }
106 }
107
108 pub(crate) const fn owner_cpu(self) -> usize {
109 self.owner_cpu
110 }
111
112 pub(crate) const fn key(self) -> TimerKey {
113 self.key
114 }
115
116 #[cfg(test)]
117 const fn new_for_test(owner_cpu: usize, key: TimerKey) -> Self {
118 Self::new(owner_cpu, key)
119 }
120}
121
122#[must_use = "futures do nothing unless you `.await` or poll them"]
124pub struct TimerFuture(FutureTimerHandle);
125
126impl Future for TimerFuture {
127 type Output = ();
128
129 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
130 crate::timers::poll_future_timer(self.0, cx)
131 }
132}
133
134impl Drop for TimerFuture {
135 fn drop(&mut self) {
136 crate::timers::cancel_future_timer(self.0);
137 }
138}
139
140pub async fn sleep(duration: Duration) {
142 sleep_until(monotonic_time() + duration).await
143}
144
145pub async fn sleep_until(deadline: TimeValue) {
147 if let Some(handle) = crate::timers::register_future_timer(deadline) {
148 TimerFuture(handle).await;
149 }
150}
151
152#[derive(Clone, Copy, Debug, PartialEq, Eq, thiserror::Error)]
154#[error("task deadline elapsed")]
155pub struct Elapsed(());
156
157pub async fn timeout<F: IntoFuture>(
159 duration: Option<Duration>,
160 f: F,
161) -> Result<F::Output, Elapsed> {
162 timeout_at(
163 duration.and_then(|x| x.checked_add(ax_hal::time::monotonic_time())),
164 f,
165 )
166 .await
167}
168
169pub async fn timeout_at<F: IntoFuture>(
171 deadline: Option<TimeValue>,
172 f: F,
173) -> Result<F::Output, Elapsed> {
174 if let Some(deadline) = deadline {
175 select_biased! {
176 res = f.into_future().fuse() => Ok(res),
177 _ = sleep_until(deadline).fuse() => Err(Elapsed(())),
178 }
179 } else {
180 Ok(f.await)
181 }
182}
183
184pub async fn timeout_at_wall<F: IntoFuture>(
186 deadline: Option<TimeValue>,
187 f: F,
188) -> Result<F::Output, Elapsed> {
189 timeout_at(deadline.map(wall_deadline_to_monotonic), f).await
190}
191
192fn wall_deadline_to_monotonic(deadline: TimeValue) -> TimeValue {
193 let now_wall = wall_time();
194 let now_mono = monotonic_time();
195 if deadline <= now_wall {
196 now_mono
197 } else {
198 now_mono
199 .checked_add(deadline - now_wall)
200 .unwrap_or(TimeValue::MAX)
201 }
202}
203
204#[cfg(test)]
205mod timer_regression_tests {
206 use super::*;
207
208 fn poll_registered_timer_for_test(
209 runtimes: [&mut TimerRuntime; 2],
210 _current_cpu: usize,
211 handle: &FutureTimerHandle,
212 context: &mut Context<'_>,
213 ) -> Poll<()> {
214 runtimes[handle.owner_cpu()].poll(&handle.key(), context)
215 }
216
217 fn cancel_registered_timer_for_test(
218 runtimes: [&mut TimerRuntime; 2],
219 _current_cpu: usize,
220 handle: &FutureTimerHandle,
221 ) {
222 runtimes[handle.owner_cpu()].cancel(&handle.key());
223 }
224
225 #[test]
226 fn future_timer_poll_uses_the_registration_cpu_after_migration() {
227 let deadline = monotonic_time() + Duration::from_secs(60);
228 let mut owner = TimerRuntime::new();
229 let mut current = TimerRuntime::new();
230 let key = owner.add(deadline).expect("future timer must be pending");
231 let handle = FutureTimerHandle::new_for_test(0, key);
232 let waker = Waker::noop();
233 let mut context = Context::from_waker(waker);
234
235 let result =
236 poll_registered_timer_for_test([&mut owner, &mut current], 1, &handle, &mut context);
237
238 assert_eq!(result, Poll::Pending);
239 assert!(owner.wheel.contains_key(&key));
240 assert!(current.wheel.is_empty());
241 }
242
243 #[test]
244 fn future_timer_drop_cancels_the_registration_cpu_after_migration() {
245 let deadline = monotonic_time() + Duration::from_secs(60);
246 let mut owner = TimerRuntime::new();
247 let mut current = TimerRuntime::new();
248 let key = owner.add(deadline).expect("future timer must be pending");
249 let handle = FutureTimerHandle::new_for_test(0, key);
250
251 cancel_registered_timer_for_test([&mut owner, &mut current], 1, &handle);
252
253 assert!(owner.wheel.is_empty());
254 assert!(current.wheel.is_empty());
255 }
256
257 #[test]
258 fn due_future_work_is_not_republished_as_a_clockevent_deadline() {
259 let mut runtime = TimerRuntime::new();
260 let deadline = monotonic_time() + Duration::from_secs(60);
261 runtime.add(deadline).expect("future timer must be pending");
262
263 assert!(runtime.publish_due_work(deadline));
264 assert_eq!(runtime.next_deadline(), None);
265 }
266
267 #[test]
268 fn future_deadline_is_republished_after_the_due_pass_finishes() {
269 let mut runtime = TimerRuntime::new();
270 let deadline = monotonic_time() + Duration::from_secs(60);
271 let later_deadline = deadline + Duration::from_secs(1);
272 runtime.add(deadline).expect("future timer must be pending");
273 runtime
274 .add(later_deadline)
275 .expect("later future timer must be pending");
276
277 assert!(runtime.publish_due_work(deadline));
278 assert!(runtime.expire_one(deadline).is_some());
279 assert!(!runtime.finish_due_work(deadline));
280 assert_eq!(runtime.next_deadline(), Some(later_deadline));
281 }
282}