dope_core/driver/schedule/clock/timer/
mod.rs1use std::{mem, task, time};
2
3use o3::mem::{Lease, Return, quota};
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#[derive(Debug, Clone, Copy, Eq, PartialEq)]
19pub enum Exhausted {
20 Slots,
21}
22
23#[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
60type Command<'turn, 'd> = heap::Min<'turn, 'd, time::Instant, slot::Slot, Tag>;
62
63pub struct Alarm<'d> {
66 lease: Lease<Access<'d>>,
67}
68
69#[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#[must_use]
78pub enum Poll<'a, 'd> {
79 Pending(Armed<'a, 'd>),
80 Ready,
81}
82
83impl<'d> Command<'_, 'd> {
84 fn due(
85 &mut self,
86 now: time::Instant,
87 ) -> Option<heap::Minimum<'_, 'd, time::Instant, slot::Slot, Tag>> {
88 let minimum = self.minimum()?;
89 (*minimum.priority() <= now).then_some(minimum)
90 }
91
92 fn set_target(&mut self, slot: &heap::Lease<'d, Tag>, wake: &wake::Waker<'d>) {
93 self.resident(slot).set(*wake.key());
94 }
95
96 fn arm_alarm(
97 &mut self,
98 slot: &heap::Lease<'d, Tag>,
99 due: time::Instant,
100 wake: &wake::Waker<'d>,
101 ) {
102 self.set_target(slot, wake);
103 self.set(slot, due);
104 }
105
106 fn clear_alarm(&mut self, slot: &heap::Lease<'d, Tag>) {
107 self.clear(slot);
108 self.resident(slot).clear();
109 }
110}
111
112impl<'d> Timer<'d> {
113 fn has_alarm(self, slot: &heap::Lease<'d, Tag>) -> bool {
114 self.command(|cmd| cmd.state(slot) != heap::State::Idle)
115 }
116
117 fn arm(self, slot: &heap::Lease<'d, Tag>, due: time::Instant, target: wake::Waker<'d>) {
118 self.command(|mut cmd| cmd.arm_alarm(slot, due, &target));
119 }
120
121 fn arm_and_poll(
122 self,
123 slot: &heap::Lease<'d, Tag>,
124 due: time::Instant,
125 now: time::Instant,
126 target: wake::Waker<'d>,
127 ) -> task::Poll<()> {
128 self.command(|mut cmd| {
129 if due <= now {
130 cmd.clear_alarm(slot);
131 task::Poll::Ready(())
132 } else {
133 cmd.arm_alarm(slot, due, &target);
134 task::Poll::Pending
135 }
136 })
137 }
138
139 fn poll(
140 self,
141 slot: &heap::Lease<'d, Tag>,
142 now: time::Instant,
143 target: wake::Waker<'d>,
144 ) -> task::Poll<()> {
145 self.command(|mut cmd| {
146 let state = cmd.state(slot);
147 let due =
148 state == heap::State::Fired || cmd.priority(slot).is_some_and(|due| *due <= now);
149 if due {
150 cmd.clear_alarm(slot);
151 task::Poll::Ready(())
152 } else if state == heap::State::Armed {
153 cmd.set_target(slot, &target);
154 task::Poll::Pending
155 } else {
156 task::Poll::Pending
157 }
158 })
159 }
160
161 fn clear(self, slot: &heap::Lease<'d, Tag>) {
162 self.command(|mut cmd| cmd.clear_alarm(slot));
163 }
164
165 pub(in crate::driver) fn expire(self, ledger: "a::Ledger<Tag>) {
166 let now = self.driver.clock().now().into_raw();
167 self.command(|mut cmd| {
168 let Some(first) = cmd.due(now) else { return };
169 let mut budget = quota::Lease::<Tag>::reserve_all(ledger);
170 let mut next = budget.admit_with(|| Some(first));
171 while let quota::Admission::Item(minimum) = next {
172 minimum.fire().resident().wake_once(self.driver);
173 next = budget.admit_with(|| cmd.due(now));
174 }
175 });
176 }
177
178 fn command<T>(self, op: impl for<'turn> FnOnce(Command<'turn, 'd>) -> T) -> T {
179 let shared = self.driver.shared;
180 unsafe { shared.context().timer().loan(op) }
183 }
184}
185
186impl<'d> Alarm<'d> {
187 const fn new(access: Access<'d>, slot: heap::Lease<'d, Tag>) -> Self {
188 Self {
189 lease: Lease::new(access, slot),
190 }
191 }
192
193 pub fn is_armed(&self) -> bool {
194 self.lease.sink().0.has_alarm(self.slot())
195 }
196
197 pub fn arm(&self, due: clock::Deadline<'d>, target: wake::Waker<'d>) {
198 self.lease.sink().0.arm(self.slot(), due.into_raw(), target);
199 }
200
201 pub fn arm_and_poll(
203 &self,
204 due: clock::Deadline<'d>,
205 now: clock::Moment<'d>,
206 target: wake::Waker<'d>,
207 ) -> task::Poll<()> {
208 self.lease
209 .sink()
210 .0
211 .arm_and_poll(self.slot(), due.into_raw(), now.into_raw(), target)
212 }
213
214 pub fn arm_owned<'a>(
216 &'a mut self,
217 due: clock::Deadline<'d>,
218 now: clock::Moment<'d>,
219 target: wake::Waker<'d>,
220 ) -> Poll<'a, 'd> {
221 if self.arm_and_poll(due, now, target).is_pending() {
222 Poll::Pending(Armed { alarm: self })
223 } else {
224 Poll::Ready
225 }
226 }
227
228 pub fn poll(&self, now: clock::Moment<'d>, target: wake::Waker<'d>) -> task::Poll<()> {
229 self.lease
230 .sink()
231 .0
232 .poll(self.slot(), now.into_raw(), target)
233 }
234
235 pub fn clear(&self) {
236 self.lease.sink().0.clear(self.slot());
237 }
238
239 fn slot(&self) -> &heap::Lease<'d, Tag> {
240 self.lease.item()
241 }
242}
243
244impl<'a, 'd> Armed<'a, 'd> {
245 pub fn poll(self, now: clock::Moment<'d>, target: wake::Waker<'d>) -> Poll<'a, 'd> {
247 if self.alarm.poll(now, target).is_pending() {
248 Poll::Pending(self)
249 } else {
250 Poll::Ready
251 }
252 }
253
254 pub fn cancel(self) {
256 self.alarm.clear();
257 }
258}
259
260impl<'d> Return for Access<'d> {
261 type Item = heap::Lease<'d, Tag>;
262
263 fn return_item(self, slot: Self::Item) {
264 self.0.command(|mut cmd| {
265 cmd.release(slot);
266 });
267 }
268}
269
270const _: () = {
271 assert!(mem::size_of::<heap::Lease<'static, Tag>>() == mem::size_of::<u32>());
272 assert!(mem::align_of::<heap::Lease<'static, Tag>>() == mem::align_of::<u32>());
273 assert!(mem::size_of::<Access<'static>>() == mem::size_of::<usize>());
274 assert!(mem::size_of::<Command<'static, 'static>>() == mem::size_of::<usize>());
275 assert!(mem::size_of::<Alarm<'static>>() == 2 * mem::size_of::<usize>());
276 assert!(mem::align_of::<Alarm<'static>>() == mem::align_of::<usize>());
277 assert!(mem::size_of::<Armed<'static, 'static>>() == mem::size_of::<usize>());
278 assert!(mem::size_of::<Option<Armed<'static, 'static>>>() == mem::size_of::<usize>());
279 assert!(mem::size_of::<Poll<'static, 'static>>() == mem::size_of::<usize>());
280};