aiur/
timer.rs

1//  \ O /
2//  / * \    aiur: the homeplanet for the famous executors
3// |' | '|   (c) 2020 - present, Vladimir Zvezda
4//   / \
5use std::future::Future;
6use std::pin::Pin;
7use std::task::{Context, Poll};
8use std::time::Duration;
9
10use crate::Runtime;
11use crate::TemporalReactor;
12use crate::{EventId, EventNode};
13
14/// Performs the async sleep.
15///
16/// This function requires reactor with timer operations implemented according to
17/// [TemporalReactor] trait. Sleeping used intensively in tests of this crate.
18///
19/// Panics if provided duration exceeds the maximum value (MAX_TIMER_DURATION_MS)
20pub async fn sleep<ReactorT: TemporalReactor>(rt: &Runtime<ReactorT>, duration: Duration) {
21    TimerFuture::new(rt, duration).await
22}
23
24// The rest code is private TimerFuture implementation
25
26// Possible states for the timer future.
27enum TimerState {
28    Created { duration: Duration },
29    Scheduled,
30    Done,
31}
32
33// Leaf future for timer.
34struct TimerFuture<'runtime, ReactorT: TemporalReactor> {
35    rt: &'runtime Runtime<ReactorT>,
36    state: TimerState,
37    event_node: EventNode,
38}
39
40impl<'rt, ReactorT: TemporalReactor> TimerFuture<'rt, ReactorT> {
41    fn new(rt: &'rt Runtime<ReactorT>, duration: Duration) -> Self {
42        TimerFuture {
43            rt,
44            state: TimerState::Created { duration },
45            event_node: EventNode::new(),
46        }
47    }
48
49    // Schedules the timer in the reactor.
50    fn schedule(&mut self, event_id: EventId, duration: Duration) -> Poll<()> {
51        // Timer in has to be in "Created" state, so we cannot schedule the timer twice.
52        debug_assert!(matches!(self.state, TimerState::Created { .. }));
53
54        self.state = TimerState::Scheduled;
55        self.rt.io().schedule_timer(event_id, duration);
56        Poll::Pending // always pending
57    }
58
59    // Verifies in reactor if given timer event is ready
60    fn verify(&mut self) -> Poll<()> {
61        // Assumes that timer has to be in "Scheduled" state
62        debug_assert!(matches!(self.state, TimerState::Scheduled));
63
64        if self.event_node.is_awoken_for(self.rt) {
65            self.state = TimerState::Done;
66            Poll::Ready(())
67        } else {
68            Poll::Pending
69        }
70    }
71}
72
73// Cancels timer event in the reactor.
74impl<'rt, ReactorT: TemporalReactor> Drop for TimerFuture<'rt, ReactorT> {
75    fn drop(&mut self) {
76        match self.state {
77            TimerState::Scheduled => {
78                self.event_node
79                    .on_cancel()
80                    .map(|event_id| self.rt.io().cancel_timer(event_id));
81            }
82            _ => (),
83        }
84    }
85}
86
87impl<'rt, ReactorT: TemporalReactor> Future for TimerFuture<'rt, ReactorT> {
88    type Output = ();
89
90    fn poll(self: Pin<&mut Self>, ctx: &mut Context) -> Poll<Self::Output> {
91        // Unsafe usage: this function does not moves out data from self, as required by
92        // Pin::get_unchecked_mut().
93        let this = unsafe { self.get_unchecked_mut() };
94
95        return match this.state {
96            TimerState::Created { duration } => {
97                let event_id = unsafe { this.event_node.on_pin(ctx) };
98                this.schedule(event_id, duration)
99            }
100            TimerState::Scheduled => this.verify(),
101            TimerState::Done => panic!("aiur/TimerFuture: was polled after completion."),
102        };
103    }
104}