dope_runtime/executor/drive/
clock.rs1use 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};