Skip to main content

dope_runtime/executor/drive/
clock.rs

1use std::time;
2
3use dope_core::driver::schedule::clock;
4
5pub(crate) struct Elapsed;
6
7#[derive(Clone, Copy, Eq, PartialEq, Debug)]
8pub struct Range {
9    origin: time::Instant,
10    duration: time::Duration,
11}
12
13impl Range {
14    pub(crate) fn try_add(
15        origin: time::Instant,
16        duration: time::Duration,
17    ) -> Result<time::Instant, Self> {
18        match origin.checked_add(duration) {
19            Some(deadline) => Ok(deadline),
20            None => Err(Self { origin, duration }),
21        }
22    }
23
24    pub const fn origin(self) -> time::Instant {
25        self.origin
26    }
27
28    pub const fn duration(self) -> time::Duration {
29        self.duration
30    }
31}
32
33pub(crate) trait Time: Copy {
34    fn start(self) -> clock::Sample;
35
36    fn park(self) -> time::Instant;
37
38    fn limit(self, timeout: Option<time::Duration>) -> Option<time::Duration>;
39}
40
41pub(crate) trait Source: Time {
42    fn sample(self) -> Reading;
43
44    fn tick(self) -> clock::Sample {
45        self.start()
46    }
47}
48
49pub(crate) trait Expiring: Time {
50    fn tick(self) -> Result<clock::Sample, Elapsed>;
51}
52
53pub(crate) struct Reading {
54    tick: clock::Sample,
55    wall: time::Instant,
56}
57
58#[derive(Clone, Copy)]
59pub(crate) struct Wall;
60
61impl Time for Wall {
62    fn start(self) -> clock::Sample {
63        self.sample().tick
64    }
65
66    fn park(self) -> time::Instant {
67        self.sample().wall
68    }
69
70    fn limit(self, timeout: Option<time::Duration>) -> Option<time::Duration> {
71        timeout
72    }
73}
74
75impl Source for Wall {
76    fn sample(self) -> Reading {
77        let wall = time::Instant::now();
78        Reading {
79            tick: clock::Sample::new(wall),
80            wall,
81        }
82    }
83}
84
85#[derive(Clone, Copy)]
86struct Fixed<'d> {
87    now: clock::Deadline<'d>,
88}
89
90impl Time for Fixed<'_> {
91    fn start(self) -> clock::Sample {
92        clock::Sample::new(self.now.into_raw())
93    }
94
95    fn park(self) -> time::Instant {
96        self.now.into_raw()
97    }
98
99    fn limit(self, timeout: Option<time::Duration>) -> Option<time::Duration> {
100        timeout.filter(time::Duration::is_zero)
101    }
102}
103
104impl Source for Fixed<'_> {
105    fn sample(self) -> Reading {
106        Reading {
107            tick: clock::Sample::new(self.now.into_raw()),
108            wall: time::Instant::now(),
109        }
110    }
111}
112
113pub(crate) fn wall() -> Wall {
114    Wall
115}
116
117pub(crate) fn at<'d>(now: clock::Deadline<'d>) -> impl Source + use<'d> {
118    Fixed { now }
119}
120
121#[derive(Clone, Copy)]
122pub(crate) struct Bound<S, L> {
123    source: S,
124    limit: L,
125}
126
127pub(crate) trait Limit: Copy {
128    fn left(self, wall: time::Instant) -> time::Duration;
129}
130
131#[derive(Clone, Copy)]
132pub(crate) struct Span {
133    started: time::Instant,
134    timeout: time::Duration,
135}
136
137impl Span {
138    fn new(timeout: time::Duration) -> Self {
139        Self {
140            started: time::Instant::now(),
141            timeout,
142        }
143    }
144}
145
146impl Limit for Span {
147    fn left(self, wall: time::Instant) -> time::Duration {
148        self.timeout
149            .saturating_sub(wall.saturating_duration_since(self.started))
150    }
151}
152
153#[derive(Clone, Copy)]
154pub(crate) struct End(time::Instant);
155
156impl End {
157    fn try_new(duration: time::Duration) -> Result<Self, Range> {
158        let origin = time::Instant::now();
159        Range::try_add(origin, duration).map(Self)
160    }
161}
162
163impl Limit for End {
164    fn left(self, wall: time::Instant) -> time::Duration {
165        self.0.saturating_duration_since(wall)
166    }
167}
168
169impl<S, L> Bound<S, L> {
170    const fn new(source: S, limit: L) -> Self {
171        Self { source, limit }
172    }
173}
174
175impl<S> Bound<S, Span> {
176    pub(crate) fn for_duration(source: S, timeout: time::Duration) -> Self {
177        Self::new(source, Span::new(timeout))
178    }
179}
180
181impl<S> Bound<S, End> {
182    pub(crate) fn until(source: S, timeout: time::Duration) -> Result<Self, Range> {
183        let end = End::try_new(timeout)?;
184        Ok(Self::new(source, end))
185    }
186}
187
188impl<S, L> Time for Bound<S, L>
189where
190    S: Source,
191    L: Limit,
192{
193    fn start(self) -> clock::Sample {
194        self.source.start()
195    }
196
197    fn park(self) -> time::Instant {
198        self.source.park()
199    }
200
201    fn limit(self, timeout: Option<time::Duration>) -> Option<time::Duration> {
202        let timeout = self.source.limit(timeout);
203        if timeout.is_some_and(|wait| wait.is_zero()) {
204            return timeout;
205        }
206        let left = self.limit.left(time::Instant::now());
207        Some(timeout.map_or(left, |wait| wait.min(left)))
208    }
209}
210
211impl<S, L> Expiring for Bound<S, L>
212where
213    S: Source,
214    L: Limit,
215{
216    fn tick(self) -> Result<clock::Sample, Elapsed> {
217        let reading = self.source.sample();
218        if self.limit.left(reading.wall).is_zero() {
219            Err(Elapsed)
220        } else {
221            Ok(reading.tick)
222        }
223    }
224}
225
226const _: () = {
227    assert!(std::mem::size_of::<Wall>() == 0);
228    assert!(std::mem::size_of::<Fixed<'_>>() == std::mem::size_of::<clock::Deadline<'_>>());
229    assert!(std::mem::size_of::<Range>() == std::mem::size_of::<(time::Instant, time::Duration)>());
230    assert!(std::mem::size_of::<Bound<(), End>>() == std::mem::size_of::<time::Instant>());
231    assert!(
232        std::mem::size_of::<Bound<(), Span>>()
233            == std::mem::size_of::<(time::Instant, time::Duration)>()
234    );
235};