1use std::{mem, ops::ControlFlow, pin, task};
2
3use dope_core::driver::{self, result::poll, route, route::indices, schedule, schedule::ready};
4use drive::{clock, turn};
5use schedule::clock::{Moment, Sample};
6use turn::{modes, routes, scope};
7
8use crate::executor::{self, boot, drive, root};
9
10#[must_use = "fixed-point evidence and its root output must be observed"]
12pub struct Fixed<'d, T> {
13 output: T,
14 now: Moment<'d>,
15}
16
17impl<'d, T> Fixed<'d, T> {
18 pub(crate) const fn new(output: T, now: Moment<'d>) -> Self {
19 Self { output, now }
20 }
21
22 pub const fn output(&self) -> &T {
23 &self.output
24 }
25
26 pub fn into_output(self) -> T {
27 self.output
28 }
29
30 pub const fn now(&self) -> Moment<'d> {
31 self.now
32 }
33}
34
35const _: () = {
36 assert!(mem::size_of::<Fixed<'_, ()>>() == mem::size_of::<Moment<'_>>());
37 assert!(mem::align_of::<Fixed<'_, ()>>() == mem::align_of::<Moment<'_>>());
38};
39
40pub(crate) struct Block<'d, T, C> {
41 state: T,
42 source: C,
43 slot: ready::Slot<'d, routes::Wake>,
44}
45
46enum Attempt<R, O> {
47 Complete(Result<O, drive::Error>),
48 Cancel(R, drive::Error),
49}
50
51enum Outcome<O, P> {
52 Complete(O, P),
53 Shutdown(Option<O>),
54}
55
56impl<O, P> Outcome<O, P> {
57 fn complete(self) -> Result<(O, P), drive::Error> {
58 match self {
59 Self::Complete(output, proof) => Ok((output, proof)),
60 Self::Shutdown(_) => Err(drive::Error::Shutdown),
61 }
62 }
63}
64
65impl<R, O> Attempt<R, O> {
66 fn finish<'d>(self) -> Result<O, drive::Error>
67 where
68 R: boot::Root<'d, Output = O>,
69 {
70 match self {
71 Self::Complete(result) => result,
72 Self::Cancel(root, source) => {
73 R::cancel(root);
74 Err(source)
75 }
76 }
77 }
78}
79
80impl<'run, 'driver, 'd, C> Block<'d, scope::Boot<'run, 'driver, 'd>, C>
81where
82 C: clock::Source,
83{
84 pub(crate) fn run_boot<R>(
85 start: Result<Self, drive::Error>,
86 root: R,
87 shutdown: impl FnMut() -> turn::Status,
88 ) -> Result<R::Output, drive::Error>
89 where
90 R: boot::Root<'d>,
91 {
92 match start {
93 Ok(block) => block.poll_boot(root, shutdown),
94 Err(source) => Attempt::Cancel(root, source),
95 }
96 .finish()
97 }
98
99 fn poll_boot<R>(
100 mut self,
101 mut root: R,
102 mut shutdown: impl FnMut() -> turn::Status,
103 ) -> Attempt<R, R::Output>
104 where
105 R: boot::Root<'d>,
106 {
107 let wake = self.slot.target();
108 let (mut polled, output) = {
109 let mut cause = boot::Cause::Start;
110 loop {
111 let tick = self.source.tick();
112 let ready = match self.state.next_root(&self.slot, tick, &mut shutdown) {
113 Ok(ready) => ready,
114 Err(source) => return Attempt::Cancel(root, source),
115 };
116 let polled = if matches!(cause, boot::Cause::Start) || ready.is_root() {
117 let (polled, output) = ready.poll_boot(root, wake, cause);
118 match output {
119 boot::Poll::Pending(next) => {
120 root = next;
121 polled
122 }
123 boot::Poll::Ready(output) => break (polled, output),
124 }
125 } else {
126 ready.skip()
127 };
128 cause = boot::Cause::Wake;
129 let parked = polled.park();
130 match parked.status() {
131 turn::Status::Active => {}
132 turn::Status::Shutdown => return Attempt::Cancel(root, drive::Error::Shutdown),
133 }
134 let resolved = parked.resolve(self.source, |idle| {
135 let state = idle.progress();
136 Ok(turn::Resolved::Wait(idle, state))
137 });
138 if let Err(source) = resolved {
139 return Attempt::Cancel(root, source);
140 }
141 }
142 };
143 Attempt::Complete(loop {
144 let parked = polled.park();
145 match parked.status() {
146 turn::Status::Active => {}
147 turn::Status::Shutdown => break Err(drive::Error::Shutdown),
148 }
149 let drained = match parked.resolve(self.source, |idle| {
150 let state = idle.progress();
151 if !matches!(state, schedule::Progress::Quiescent) {
152 return Ok(turn::Resolved::Wait(idle, state));
153 }
154 Ok(match idle.sync()? {
155 poll::Commit::Drained => turn::Resolved::Done,
156 poll::Commit::Pending => turn::Resolved::Again,
157 })
158 }) {
159 Ok(drained) => drained,
160 Err(error) => break Err(error),
161 };
162 if drained {
163 break Ok(output);
164 }
165 let tick = self.source.tick();
166 polled = match self.state.next_root(&self.slot, tick, &mut shutdown) {
167 Ok(ready) => ready.skip(),
168 Err(error) => break Err(error),
169 };
170 })
171 }
172}
173
174impl<'d, T, C> Block<'d, T, C> {
175 pub(crate) fn new(
176 driver: driver::Reference<'d>,
177 state: T,
178 source: C,
179 ) -> Result<Self, drive::Error> {
180 let target = driver
181 .targets::<routes::Wake>()
182 .bind(indices::SlotIndex::ZERO, route::Epoch::INITIAL);
183 let slot = driver.ready().make_slot(target.dispatch())?;
184 Ok(Self {
185 state,
186 source,
187 slot,
188 })
189 }
190}
191
192impl<'run, 'driver, 'app, 'd: 'app, D, S, C>
193 Block<'d, scope::Scope<'run, 'driver, 'app, 'd, D, S>, C>
194where
195 D: executor::raw::App<'d>,
196 C: clock::Time,
197{
198 pub(crate) fn run<R>(
199 self,
200 root: R,
201 tick: impl Fn(C) -> Result<Sample, drive::Error>,
202 shutdown: impl FnMut() -> turn::Status,
203 ) -> Result<R::Output, drive::Error>
204 where
205 R: root::Root<'d>,
206 {
207 let (output, ()) = self
208 .drive(root, tick, shutdown, |idle| {
209 Ok(matches!(idle.sync()?, poll::Commit::Drained).then_some(()))
210 })?
211 .complete()?;
212 Ok(output)
213 }
214
215 pub(crate) fn run_until<R>(
216 self,
217 root: R,
218 tick: impl Fn(C) -> Result<Sample, drive::Error>,
219 shutdown: impl FnMut() -> turn::Status,
220 ) -> Result<ControlFlow<R::Output>, drive::Error>
221 where
222 R: root::Root<'d>,
223 {
224 Ok(
225 match self.drive(root, tick, shutdown, |idle| {
226 Ok(matches!(idle.sync()?, poll::Commit::Drained).then_some(()))
227 })? {
228 Outcome::Complete(output, ()) | Outcome::Shutdown(Some(output)) => {
229 ControlFlow::Break(output)
230 }
231 Outcome::Shutdown(None) => ControlFlow::Continue(()),
232 },
233 )
234 }
235
236 pub(crate) fn run_fixed<R>(
237 self,
238 root: R,
239 tick: impl Fn(C) -> Result<Sample, drive::Error>,
240 shutdown: impl FnMut() -> turn::Status,
241 ) -> Result<Fixed<'d, R::Output>, drive::Error>
242 where
243 R: root::Root<'d>,
244 {
245 let (output, now) = self
246 .drive(root, tick, shutdown, |idle| {
247 let fixed = idle.is_fixed();
248 let now = idle.now();
249 let commit = idle.sync()?;
250 Ok((fixed && matches!(commit, poll::Commit::Drained)).then_some(now))
251 })?
252 .complete()?;
253 Ok(Fixed::new(output, now))
254 }
255
256 fn drive<R, P>(
257 mut self,
258 root: R,
259 tick: impl Fn(C) -> Result<Sample, drive::Error>,
260 mut shutdown: impl FnMut() -> turn::Status,
261 complete: impl for<'turn> Fn(
262 turn::Idle<'turn, 'd, modes::App<'turn, 'app, 'd, D, S>>,
263 ) -> Result<Option<P>, drive::Error>,
264 ) -> Result<Outcome<R::Output, P>, drive::Error>
265 where
266 R: root::Root<'d>,
267 {
268 let wake = self.slot.target();
269 let (mut polled, output) = {
270 let mut root = pin::pin!(root);
271 let mut first = true;
272 loop {
273 let tick = tick(self.source)?;
274 let ready = self.state.next_root(&self.slot, tick, &mut shutdown)?;
275 let poll = first || ready.is_root();
276 let (polled, output) = if poll {
277 ready.poll(root.as_mut(), wake)
278 } else {
279 (ready.skip(), task::Poll::Pending)
280 };
281 first = false;
282 match output {
283 task::Poll::Ready(output) => break (polled, output),
284 task::Poll::Pending => {
285 let parked = polled.park();
286 match parked.status() {
287 turn::Status::Active => {}
288 turn::Status::Shutdown => return Ok(Outcome::Shutdown(None)),
289 }
290 parked.resolve(self.source, |idle| {
291 let state = idle.progress();
292 Ok(turn::Resolved::Wait(idle, state))
293 })?;
294 }
295 }
296 }
297 };
298 loop {
299 let parked = polled.park();
300 match parked.status() {
301 turn::Status::Active => {}
302 turn::Status::Shutdown => return Ok(Outcome::Shutdown(Some(output))),
303 }
304 if let Some(idle) = parked.sync()?
305 && let Some(proof) = complete(idle)?
306 {
307 return Ok(Outcome::Complete(output, proof));
308 }
309 let tick = tick(self.source)?;
310 polled = self
311 .state
312 .next_root(&self.slot, tick, &mut shutdown)?
313 .skip();
314 }
315 }
316}