Skip to main content

dope_runtime/executor/drive/
block.rs

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/// Evidence that one root and its installed application reached a fixed point.
11#[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}