Skip to main content

dope_core/driver/schedule/clock/timer/
mod.rs

1use std::{mem, task, time};
2
3use o3::mem::{Lease, Return};
4
5use crate::{
6    driver,
7    driver::schedule::{
8        clock,
9        ready::{wake, wake::resident::slot},
10    },
11};
12
13pub mod heap;
14
15pub(in crate::driver) struct Tag;
16
17/// The driver's fixed timer capacity has no unleased resident slot.
18#[derive(Debug, Clone, Copy, Eq, PartialEq)]
19pub enum Exhausted {
20    Slots,
21}
22
23/// Copyable access to the fixed timer store of one exact driver generation.
24#[derive(Clone, Copy)]
25#[repr(transparent)]
26pub struct Timer<'d> {
27    pub(in crate::driver) driver: driver::Reference<'d>,
28}
29
30impl<'d> Timer<'d> {
31    pub(in crate::driver) const fn new(driver: driver::Reference<'d>) -> Self {
32        Self { driver }
33    }
34
35    pub fn earliest(self) -> Option<clock::Deadline<'d>> {
36        self.command(|cmd| cmd.peek().copied())
37            .map(|at| self.driver.clock().deadline_at(at))
38    }
39
40    pub fn try_alarm(self) -> Result<Alarm<'d>, Exhausted> {
41        let slot = self
42            .command(|mut cmd| {
43                let vacancy = cmd.vacancy()?;
44                Some(vacancy.lease(slot::Slot::default()))
45            })
46            .ok_or(Exhausted::Slots)?;
47        Ok(Alarm::new(Access(self), slot))
48    }
49}
50
51const _: () = {
52    assert!(mem::size_of::<Timer<'static>>() == mem::size_of::<usize>());
53    assert!(mem::align_of::<Timer<'static>>() == mem::align_of::<usize>());
54};
55
56#[derive(Clone, Copy)]
57#[repr(transparent)]
58pub(super) struct Access<'d>(Timer<'d>);
59
60/// Linear access to one timer store for the duration of one sealed command.
61type Command<'turn, 'd> = heap::Min<'turn, 'd, time::Instant, slot::Slot, Tag>;
62
63/// A resident alarm tied to one exact driver generation.
64///
65pub struct Alarm<'d> {
66    lease: Lease<Access<'d>>,
67}
68
69/// Exclusive proof that one alarm remains armed or fired.
70#[must_use = "an armed alarm must be polled or cancelled"]
71#[repr(transparent)]
72pub struct Armed<'a, 'd> {
73    alarm: &'a mut Alarm<'d>,
74}
75
76/// State transition after polling an exclusively armed alarm.
77#[must_use]
78pub enum Poll<'a, 'd> {
79    Pending(Armed<'a, 'd>),
80    Ready,
81}
82
83impl<'d> Command<'_, 'd> {
84    fn set_target(&mut self, slot: &heap::Lease<'d, Tag>, wake: &wake::Waker<'d>) {
85        self.resident(slot).set(*wake.key());
86    }
87
88    fn arm_alarm(
89        &mut self,
90        slot: &heap::Lease<'d, Tag>,
91        due: time::Instant,
92        wake: &wake::Waker<'d>,
93    ) {
94        self.set_target(slot, wake);
95        self.set(slot, due);
96    }
97
98    fn clear_alarm(&mut self, slot: &heap::Lease<'d, Tag>) {
99        self.clear(slot);
100        self.resident(slot).clear();
101    }
102}
103
104impl<'d> Timer<'d> {
105    fn has_alarm(self, slot: &heap::Lease<'d, Tag>) -> bool {
106        self.command(|cmd| cmd.state(slot) != heap::State::Idle)
107    }
108
109    fn arm(self, slot: &heap::Lease<'d, Tag>, due: time::Instant, target: wake::Waker<'d>) {
110        self.command(|mut cmd| cmd.arm_alarm(slot, due, &target));
111    }
112
113    fn arm_and_poll(
114        self,
115        slot: &heap::Lease<'d, Tag>,
116        due: time::Instant,
117        now: time::Instant,
118        target: wake::Waker<'d>,
119    ) -> task::Poll<()> {
120        self.command(|mut cmd| {
121            if due <= now {
122                cmd.clear_alarm(slot);
123                task::Poll::Ready(())
124            } else {
125                cmd.arm_alarm(slot, due, &target);
126                task::Poll::Pending
127            }
128        })
129    }
130
131    fn poll(
132        self,
133        slot: &heap::Lease<'d, Tag>,
134        now: time::Instant,
135        target: wake::Waker<'d>,
136    ) -> task::Poll<()> {
137        self.command(|mut cmd| {
138            let state = cmd.state(slot);
139            let due =
140                state == heap::State::Fired || cmd.priority(slot).is_some_and(|due| *due <= now);
141            if due {
142                cmd.clear_alarm(slot);
143                task::Poll::Ready(())
144            } else if state == heap::State::Armed {
145                cmd.set_target(slot, &target);
146                task::Poll::Pending
147            } else {
148                task::Poll::Pending
149            }
150        })
151    }
152
153    fn clear(self, slot: &heap::Lease<'d, Tag>) {
154        self.command(|mut cmd| cmd.clear_alarm(slot));
155    }
156
157    pub(in crate::driver) fn expire_one(self, now: clock::Moment<'d>) -> Option<()> {
158        self.command(|mut cmd| {
159            let min = cmd.minimum()?;
160            if *min.priority() > now.into_raw() {
161                return None;
162            }
163            let fired = min.fire();
164            fired.resident().wake_once(self.driver);
165            Some(())
166        })
167    }
168
169    fn command<T>(self, op: impl for<'turn> FnOnce(Command<'turn, 'd>) -> T) -> T {
170        let shared = self.driver.shared;
171        // SAFETY: this fixed owner cell supplies every `'d` lease; Command's
172        // higher-ranked exclusive loan is linear, sealed, and non-reentrant.
173        unsafe { shared.context().timer().loan(op) }
174    }
175}
176
177impl<'d> Alarm<'d> {
178    const fn new(access: Access<'d>, slot: heap::Lease<'d, Tag>) -> Self {
179        Self {
180            lease: Lease::new(access, slot),
181        }
182    }
183
184    pub fn is_armed(&self) -> bool {
185        self.lease.sink().0.has_alarm(self.slot())
186    }
187
188    pub fn arm(&self, due: clock::Deadline<'d>, target: wake::Waker<'d>) {
189        self.lease.sink().0.arm(self.slot(), due.into_raw(), target);
190    }
191
192    /// Replaces this deadline and polls it in one command, retaining the slot.
193    pub fn arm_and_poll(
194        &self,
195        due: clock::Deadline<'d>,
196        now: clock::Moment<'d>,
197        target: wake::Waker<'d>,
198    ) -> task::Poll<()> {
199        self.lease
200            .sink()
201            .0
202            .arm_and_poll(self.slot(), due.into_raw(), now.into_raw(), target)
203    }
204
205    /// Replaces and polls this deadline, retaining exclusive proof while pending.
206    pub fn arm_owned<'a>(
207        &'a mut self,
208        due: clock::Deadline<'d>,
209        now: clock::Moment<'d>,
210        target: wake::Waker<'d>,
211    ) -> Poll<'a, 'd> {
212        if self.arm_and_poll(due, now, target).is_pending() {
213            Poll::Pending(Armed { alarm: self })
214        } else {
215            Poll::Ready
216        }
217    }
218
219    pub fn poll(&self, now: clock::Moment<'d>, target: wake::Waker<'d>) -> task::Poll<()> {
220        self.lease
221            .sink()
222            .0
223            .poll(self.slot(), now.into_raw(), target)
224    }
225
226    pub fn clear(&self) {
227        self.lease.sink().0.clear(self.slot());
228    }
229
230    fn slot(&self) -> &heap::Lease<'d, Tag> {
231        self.lease.item()
232    }
233}
234
235impl<'a, 'd> Armed<'a, 'd> {
236    /// Polls without losing the proof when the alarm remains live.
237    pub fn poll(self, now: clock::Moment<'d>, target: wake::Waker<'d>) -> Poll<'a, 'd> {
238        if self.alarm.poll(now, target).is_pending() {
239            Poll::Pending(self)
240        } else {
241            Poll::Ready
242        }
243    }
244
245    /// Cancels an alarm proven not to be idle.
246    pub fn cancel(self) {
247        self.alarm.clear();
248    }
249}
250
251impl<'d> Return for Access<'d> {
252    type Item = heap::Lease<'d, Tag>;
253
254    fn return_item(self, slot: Self::Item) {
255        self.0.command(|mut cmd| {
256            cmd.release(slot);
257        });
258    }
259}
260
261const _: () = {
262    assert!(mem::size_of::<heap::Lease<'static, Tag>>() == mem::size_of::<u32>());
263    assert!(mem::align_of::<heap::Lease<'static, Tag>>() == mem::align_of::<u32>());
264    assert!(mem::size_of::<Access<'static>>() == mem::size_of::<usize>());
265    assert!(mem::size_of::<Command<'static, 'static>>() == mem::size_of::<usize>());
266    assert!(mem::size_of::<Alarm<'static>>() == 2 * mem::size_of::<usize>());
267    assert!(mem::align_of::<Alarm<'static>>() == mem::align_of::<usize>());
268    assert!(mem::size_of::<Armed<'static, 'static>>() == mem::size_of::<usize>());
269    assert!(mem::size_of::<Option<Armed<'static, 'static>>>() == mem::size_of::<usize>());
270    assert!(mem::size_of::<Poll<'static, 'static>>() == mem::size_of::<usize>());
271};