Skip to main content

ax_task/future/
time.rs

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    // Once IRQ processing publishes the due head to the timer worker, the
21    // logical queue must stop advertising it to the physical clockevent.
22    // The worker owns clearing this state after its bounded drain pass.
23    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/// Future returned by `sleep` and `sleep_until`.
123#[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
140/// Waits until `duration` has elapsed.
141pub async fn sleep(duration: Duration) {
142    sleep_until(monotonic_time() + duration).await
143}
144
145/// Waits until the monotonic `deadline` is reached.
146pub 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/// Error returned by [`timeout`] and [`timeout_at`].
153#[derive(Clone, Copy, Debug, PartialEq, Eq, thiserror::Error)]
154#[error("task deadline elapsed")]
155pub struct Elapsed(());
156
157/// Requires a `Future` to complete before the specified duration has elapsed.
158pub 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
169/// Requires a `Future` to complete before the specified monotonic deadline.
170pub 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
184/// Requires a `Future` to complete before the specified wall-clock deadline.
185pub 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}