Skip to main content

dope_core/driver/schedule/clock/
mod.rs

1//! Driver-branded monotonic time sampled once per runtime turn.
2
3use std::{borrow, cell, cmp, marker, mem, ops, time};
4
5use o3::base::marker::ThreadBound;
6
7use crate::driver;
8
9mod range;
10pub mod timer;
11#[doc(hidden)]
12pub mod watchdog;
13
14pub use range::Range;
15
16#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
17#[repr(C)]
18struct Point<'d> {
19    at: time::Instant,
20    driver: (driver::Invariant<'d>, ThreadBound),
21}
22/// Read-only access to the monotonic sample installed for one driver turn.
23#[derive(Clone, Copy)]
24#[repr(transparent)]
25pub struct Clock<'turn, 'd> {
26    source: &'turn cell::Cell<time::Instant>,
27    driver: (driver::Invariant<'d>, ThreadBound),
28}
29/// Generative monotonic domain for code running outside a runtime driver.
30///
31#[derive(Clone, Copy)]
32pub struct Scope<'d> {
33    proof: (driver::Invariant<'d>, ThreadBound),
34}
35/// Owned monotonic origin that accepts no foreign time-domain input.
36#[derive(Clone, Copy, Debug, Eq, PartialEq)]
37#[repr(transparent)]
38pub struct Origin(time::Instant);
39
40/// A persistent monotonic observation belonging to exactly one driver scope.
41///
42/// Driver brands are invariant, so observations from independently generated
43/// scopes cannot be compared or used to derive one another's deadlines:
44///
45#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
46#[repr(transparent)]
47pub struct Moment<'d>(Point<'d>);
48/// A persistent absolute monotonic deadline belonging to one driver scope.
49///
50#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
51#[repr(transparent)]
52pub struct Deadline<'d>(Point<'d>);
53/// A raw-time owner attached to exactly one driver scope.
54///
55#[repr(transparent)]
56pub struct Bound<'d, T> {
57    inner: T,
58    driver: (driver::Invariant<'d>, ThreadBound),
59}
60/// A lexical view combining one attached owner with one branded observation.
61#[repr(C)]
62pub struct At<'d, A> {
63    access: A,
64    now: Moment<'d>,
65}
66
67/// One raw runtime-boundary sample waiting to be installed into a turn.
68#[derive(Clone, Copy)]
69#[repr(transparent)]
70pub struct Sample(time::Instant);
71
72impl Sample {
73    /// Wraps the raw monotonic sample for installation into a runtime turn.
74    pub const fn new(now: time::Instant) -> Self {
75        Self(now)
76    }
77
78    pub(in crate::driver) const fn into_raw(self) -> time::Instant {
79        self.0
80    }
81}
82
83impl Scope<'_> {
84    /// Runs with a fresh time domain whose observations cannot escape.
85    pub fn run<R>(run: impl for<'d> FnOnce(Scope<'d>) -> R) -> R {
86        run(Scope::new())
87    }
88}
89
90impl<'d> Scope<'d> {
91    const fn new() -> Self {
92        Self {
93            proof: (driver::Invariant(marker::PhantomData), ThreadBound::NEW),
94        }
95    }
96
97    pub fn now(self) -> Moment<'d> {
98        Moment::new(time::Instant::now(), self.proof)
99    }
100}
101
102impl Origin {
103    pub fn elapsed(self) -> time::Duration {
104        self.0.elapsed()
105    }
106}
107
108impl<'d> Point<'d> {
109    const fn new(at: time::Instant, driver: (driver::Invariant<'d>, ThreadBound)) -> Self {
110        Self { at, driver }
111    }
112
113    const fn at(self, at: time::Instant) -> Self {
114        Self::new(at, self.driver)
115    }
116
117    const fn get(self) -> time::Instant {
118        self.at
119    }
120
121    fn try_add<D: Copy + borrow::Borrow<time::Duration>>(
122        self,
123        duration: D,
124    ) -> Result<Self, Range<D>> {
125        match self.at.checked_add(*duration.borrow()) {
126            Some(at) => Ok(self.at(at)),
127            None => Err(Range::new(Origin(self.at), duration)),
128        }
129    }
130
131    fn clamp_add<D: Copy + borrow::Borrow<time::Duration>>(self, duration: D) -> Self {
132        match self.try_add(duration) {
133            Ok(point) => point,
134            Err(range) => self.at(range.furthest()),
135        }
136    }
137}
138
139impl<'turn, 'd> Clock<'turn, 'd> {
140    pub(in crate::driver) const fn new(
141        source: &'turn cell::Cell<time::Instant>,
142        driver: driver::Invariant<'d>,
143    ) -> Self {
144        Self {
145            source,
146            driver: (driver, ThreadBound::NEW),
147        }
148    }
149
150    /// Returns the one sample installed at the beginning of this turn.
151    pub fn now(self) -> Moment<'d> {
152        Moment::new(self.source.get(), self.driver)
153    }
154
155    /// Attaches a raw-time owner to this clock's driver.
156    pub fn attach<T>(self, build: impl FnOnce(time::Instant) -> T) -> Bound<'d, T> {
157        Bound::new(build(self.source.get()), self.driver)
158    }
159
160    /// Attaches a fallibly constructed raw-time owner to this driver.
161    pub fn try_attach<T, E>(
162        self,
163        build: impl FnOnce(time::Instant) -> Result<T, E>,
164    ) -> Result<Bound<'d, T>, E> {
165        build(self.source.get()).map(|inner| Bound::new(inner, self.driver))
166    }
167
168    /// Adopts an external absolute instant as this driver's deadline.
169    pub fn deadline_at(self, at: time::Instant) -> Deadline<'d> {
170        Deadline(Point::new(at, self.driver))
171    }
172
173    /// Binds lexical access to this turn's canonical sample.
174    pub fn at<A>(self, access: A) -> At<'d, A> {
175        At {
176            access,
177            now: self.now(),
178        }
179    }
180}
181
182impl<'d, T> Bound<'d, T> {
183    const fn new(inner: T, driver: (driver::Invariant<'d>, ThreadBound)) -> Self {
184        Self { inner, driver }
185    }
186
187    pub fn map<U>(self, map: impl FnOnce(T) -> U) -> Bound<'d, U> {
188        Bound::new(map(self.inner), self.driver)
189    }
190}
191
192impl<T> ops::Deref for Bound<'_, T> {
193    type Target = T;
194
195    fn deref(&self) -> &Self::Target {
196        &self.inner
197    }
198}
199
200impl<T> ops::DerefMut for Bound<'_, T> {
201    fn deref_mut(&mut self) -> &mut Self::Target {
202        &mut self.inner
203    }
204}
205
206impl<'d, A> At<'d, A> {
207    pub fn apply<R>(self, apply: impl FnOnce(A, time::Instant) -> R) -> R {
208        apply(self.access, self.now.0.get())
209    }
210
211    pub fn deadline(
212        self,
213        project: impl FnOnce(A, time::Instant) -> Option<time::Instant>,
214    ) -> Option<Deadline<'d>> {
215        project(self.access, self.now.0.get()).map(|at| Deadline(self.now.0.at(at)))
216    }
217}
218
219impl<'d, T: ?Sized> At<'d, &mut T> {
220    /// Lends the owner with its original driver observation.
221    ///
222    ///
223    pub fn apply_mut<R>(&mut self, apply: impl FnOnce(&mut T, Moment<'d>) -> R) -> R {
224        apply(&mut *self.access, self.now)
225    }
226}
227
228impl<'d> Moment<'d> {
229    const fn new(at: time::Instant, driver: (driver::Invariant<'d>, ThreadBound)) -> Self {
230        Self(Point::new(at, driver))
231    }
232
233    /// Retains this observation without its driver brand.
234    pub const fn retain(self) -> Origin {
235        Origin(self.0.get())
236    }
237
238    pub fn try_add<D: Copy + borrow::Borrow<time::Duration>>(
239        self,
240        duration: D,
241    ) -> Result<Deadline<'d>, Range<D>> {
242        Ok(Deadline(self.0.try_add(duration)?))
243    }
244
245    /// Adds a duration, clamping only at the platform's furthest instant.
246    pub fn clamp_add<D: Copy + borrow::Borrow<time::Duration>>(self, duration: D) -> Deadline<'d> {
247        Deadline(self.0.clamp_add(duration))
248    }
249
250    /// Binds lexical access to this persistent observation.
251    pub fn at<A>(self, access: A) -> At<'d, A> {
252        At { access, now: self }
253    }
254
255    pub fn moment_at(self, at: time::Instant) -> Self {
256        Self(self.0.at(at))
257    }
258
259    pub fn deadline_at(self, at: time::Instant) -> Deadline<'d> {
260        Deadline(self.0.at(at))
261    }
262
263    pub fn since(self, earlier: Self) -> time::Duration {
264        self.0.get().duration_since(earlier.0.get())
265    }
266
267    pub fn clamp_since(self, earlier: Self) -> time::Duration {
268        self.0.get().saturating_duration_since(earlier.0.get())
269    }
270
271    pub fn try_since(self, earlier: Self) -> Option<time::Duration> {
272        self.0.get().checked_duration_since(earlier.0.get())
273    }
274
275    pub fn past_due(self, earlier: Deadline<'d>) -> time::Duration {
276        self.0.get().duration_since(earlier.0.get())
277    }
278
279    /// Exposes the underlying monotonic sample at a runtime boundary.
280    pub const fn into_raw(self) -> time::Instant {
281        self.0.get()
282    }
283}
284
285impl<'d> Deadline<'d> {
286    pub fn try_add<D: Copy + borrow::Borrow<time::Duration>>(
287        self,
288        duration: D,
289    ) -> Result<Self, Range<D>> {
290        Ok(Self(self.0.try_add(duration)?))
291    }
292
293    /// Adds a duration, clamping only at the platform's furthest instant.
294    pub fn clamp_add<D: Copy + borrow::Borrow<time::Duration>>(self, duration: D) -> Self {
295        Self(self.0.clamp_add(duration))
296    }
297
298    pub fn checked_sub(self, duration: time::Duration) -> Option<Self> {
299        self.0
300            .get()
301            .checked_sub(duration)
302            .map(|at| Self(self.0.at(at)))
303    }
304
305    pub fn is_due(self, now: Moment<'d>) -> bool {
306        self.0.get() <= now.0.get()
307    }
308
309    pub fn try_left(self, now: Moment<'d>) -> Option<time::Duration> {
310        self.0.get().checked_duration_since(now.0.get())
311    }
312
313    /// Exposes the underlying monotonic deadline at a runtime boundary.
314    pub const fn into_raw(self) -> time::Instant {
315        self.0.get()
316    }
317}
318
319impl From<Moment<'_>> for time::Instant {
320    fn from(moment: Moment<'_>) -> Self {
321        moment.0.at
322    }
323}
324
325impl<'d> From<Moment<'d>> for Deadline<'d> {
326    fn from(moment: Moment<'d>) -> Self {
327        Self(moment.0)
328    }
329}
330
331impl<'d> PartialEq<Moment<'d>> for Deadline<'d> {
332    fn eq(&self, other: &Moment<'d>) -> bool {
333        self.0.get() == other.0.get()
334    }
335}
336
337impl<'d> PartialOrd<Moment<'d>> for Deadline<'d> {
338    fn partial_cmp(&self, other: &Moment<'d>) -> Option<cmp::Ordering> {
339        self.0.get().partial_cmp(&other.0.get())
340    }
341}
342
343impl<'d> PartialEq<Deadline<'d>> for Moment<'d> {
344    fn eq(&self, other: &Deadline<'d>) -> bool {
345        self.0.get() == other.0.get()
346    }
347}
348
349impl<'d> PartialOrd<Deadline<'d>> for Moment<'d> {
350    fn partial_cmp(&self, other: &Deadline<'d>) -> Option<cmp::Ordering> {
351        self.0.get().partial_cmp(&other.0.get())
352    }
353}
354
355const _: () = {
356    assert!(mem::size_of::<Scope<'static>>() == 0);
357    assert!(mem::size_of::<Origin>() == mem::size_of::<time::Instant>());
358    assert!(mem::align_of::<Origin>() == mem::align_of::<time::Instant>());
359    assert!(!mem::needs_drop::<Origin>());
360    assert!(mem::size_of::<Clock<'static, 'static>>() == mem::size_of::<usize>());
361    assert!(mem::align_of::<Clock<'static, 'static>>() == mem::align_of::<usize>());
362    assert!(mem::size_of::<Moment<'static>>() == mem::size_of::<time::Instant>());
363    assert!(mem::align_of::<Moment<'static>>() == mem::align_of::<time::Instant>());
364    assert!(mem::size_of::<Deadline<'static>>() == mem::size_of::<time::Instant>());
365    assert!(mem::align_of::<Deadline<'static>>() == mem::align_of::<time::Instant>());
366    assert!(!mem::needs_drop::<Moment<'static>>());
367    assert!(!mem::needs_drop::<Deadline<'static>>());
368    assert!(
369        mem::size_of::<At<'static, &'static mut ()>>()
370            == mem::size_of::<&mut ()>() + mem::size_of::<Moment<'static>>()
371    );
372    assert!(mem::align_of::<At<'static, &'static mut ()>>() == mem::align_of::<time::Instant>());
373    assert!(!mem::needs_drop::<At<'static, &'static mut ()>>());
374    assert!(mem::size_of::<Bound<'static, time::Instant>>() == mem::size_of::<time::Instant>());
375    assert!(mem::align_of::<Bound<'static, time::Instant>>() == mem::align_of::<time::Instant>());
376    assert!(mem::size_of::<Sample>() == mem::size_of::<time::Instant>());
377};