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