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    ) -> Result<R::Output, drive::Error>
88    where
89        R: boot::Root<'d>,
90    {
91        match start {
92            Ok(block) => block.poll_boot(root),
93            Err(source) => Attempt::Cancel(root, source),
94        }
95        .finish()
96    }
97
98    fn poll_boot<R>(mut self, mut root: R) -> Attempt<R, R::Output>
99    where
100        R: boot::Root<'d>,
101    {
102        let wake = self.slot.target();
103        let (mut polled, output) = {
104            let mut cause = boot::Cause::Start;
105            loop {
106                let tick = self.source.tick();
107                let ready = match self.state.next_root(&self.slot, tick) {
108                    Ok(ready) => ready,
109                    Err(source) => return Attempt::Cancel(root, source),
110                };
111                let polled = if matches!(cause, boot::Cause::Start) || ready.is_root() {
112                    let (polled, output) = ready.poll_boot(root, wake, cause);
113                    match output {
114                        boot::Poll::Pending(next) => {
115                            root = next;
116                            polled
117                        }
118                        boot::Poll::Ready(output) => break (polled, output),
119                    }
120                } else {
121                    ready.skip()
122                };
123                cause = boot::Cause::Wake;
124                let parked = polled.park();
125                match parked.status() {
126                    turn::Status::Active => {}
127                    turn::Status::Shutdown => return Attempt::Cancel(root, drive::Error::Shutdown),
128                }
129                let resolved = parked.resolve(self.source, |idle| {
130                    let state = idle.progress();
131                    Ok(turn::Resolved::Wait(idle, state))
132                });
133                if let Err(source) = resolved {
134                    return Attempt::Cancel(root, source);
135                }
136            }
137        };
138        Attempt::Complete(loop {
139            let parked = polled.park();
140            match parked.status() {
141                turn::Status::Active => {}
142                turn::Status::Shutdown => break Err(drive::Error::Shutdown),
143            }
144            let drained = match parked.resolve(self.source, |idle| {
145                let state = idle.progress();
146                if !matches!(state, schedule::Progress::Quiescent) {
147                    return Ok(turn::Resolved::Wait(idle, state));
148                }
149                Ok(match idle.sync()? {
150                    poll::Commit::Drained => turn::Resolved::Done,
151                    poll::Commit::Pending => turn::Resolved::Again,
152                })
153            }) {
154                Ok(drained) => drained,
155                Err(error) => break Err(error),
156            };
157            if drained {
158                break Ok(output);
159            }
160            let tick = self.source.tick();
161            polled = match self.state.next_root(&self.slot, tick) {
162                Ok(ready) => ready.skip(),
163                Err(error) => break Err(error),
164            };
165        })
166    }
167}
168
169impl<'d, T, C> Block<'d, T, C> {
170    pub(crate) fn new(
171        driver: driver::Reference<'d>,
172        state: T,
173        source: C,
174    ) -> Result<Self, drive::Error> {
175        let target = driver
176            .targets::<routes::Wake>()
177            .bind(indices::SlotIndex::ZERO, route::Epoch::INITIAL);
178        let slot = driver.ready().make_slot(target.dispatch())?;
179        Ok(Self {
180            state,
181            source,
182            slot,
183        })
184    }
185}
186
187impl<'run, 'driver, 'app, 'd: 'app, D, S, C>
188    Block<'d, scope::Scope<'run, 'driver, 'app, 'd, D, S>, C>
189where
190    D: executor::raw::App<'d>,
191    C: clock::Time,
192{
193    pub(crate) fn run<R>(
194        self,
195        root: R,
196        tick: impl Fn(C) -> Result<Sample, drive::Error>,
197    ) -> Result<R::Output, drive::Error>
198    where
199        R: root::Root<'d>,
200    {
201        let (output, ()) = self
202            .drive(root, tick, |idle| {
203                Ok(matches!(idle.sync()?, poll::Commit::Drained).then_some(()))
204            })?
205            .complete()?;
206        Ok(output)
207    }
208
209    pub(crate) fn run_until<R>(
210        self,
211        root: R,
212        tick: impl Fn(C) -> Result<Sample, drive::Error>,
213    ) -> Result<ControlFlow<R::Output>, drive::Error>
214    where
215        R: root::Root<'d>,
216    {
217        Ok(
218            match self.drive(root, tick, |idle| {
219                Ok(matches!(idle.sync()?, poll::Commit::Drained).then_some(()))
220            })? {
221                Outcome::Complete(output, ()) | Outcome::Shutdown(Some(output)) => {
222                    ControlFlow::Break(output)
223                }
224                Outcome::Shutdown(None) => ControlFlow::Continue(()),
225            },
226        )
227    }
228
229    pub(crate) fn run_fixed<R>(
230        self,
231        root: R,
232        tick: impl Fn(C) -> Result<Sample, drive::Error>,
233    ) -> Result<Fixed<'d, R::Output>, drive::Error>
234    where
235        R: root::Root<'d>,
236    {
237        let (output, now) = self
238            .drive(root, tick, |idle| {
239                let fixed = idle.is_fixed();
240                let now = idle.now();
241                let commit = idle.sync()?;
242                Ok((fixed && matches!(commit, poll::Commit::Drained)).then_some(now))
243            })?
244            .complete()?;
245        Ok(Fixed::new(output, now))
246    }
247
248    fn drive<R, P>(
249        mut self,
250        root: R,
251        tick: impl Fn(C) -> Result<Sample, drive::Error>,
252        complete: impl for<'turn> Fn(
253            turn::Idle<'turn, 'd, modes::App<'turn, 'app, 'd, D, S>>,
254        ) -> Result<Option<P>, drive::Error>,
255    ) -> Result<Outcome<R::Output, P>, drive::Error>
256    where
257        R: root::Root<'d>,
258    {
259        let wake = self.slot.target();
260        let (mut polled, output) = {
261            let mut root = pin::pin!(root);
262            let mut first = true;
263            loop {
264                let tick = tick(self.source)?;
265                let ready = self.state.next_root(&self.slot, tick)?;
266                let poll = first || ready.is_root();
267                let (polled, output) = if poll {
268                    ready.poll(root.as_mut(), wake)
269                } else {
270                    (ready.skip(), task::Poll::Pending)
271                };
272                first = false;
273                match output {
274                    task::Poll::Ready(output) => break (polled, output),
275                    task::Poll::Pending => {
276                        let parked = polled.park();
277                        match parked.status() {
278                            turn::Status::Active => {}
279                            turn::Status::Shutdown => return Ok(Outcome::Shutdown(None)),
280                        }
281                        parked.resolve(self.source, |idle| {
282                            let state = idle.progress();
283                            Ok(turn::Resolved::Wait(idle, state))
284                        })?;
285                    }
286                }
287            }
288        };
289        loop {
290            let parked = polled.park();
291            match parked.status() {
292                turn::Status::Active => {}
293                turn::Status::Shutdown => return Ok(Outcome::Shutdown(Some(output))),
294            }
295            if let Some(idle) = parked.sync()?
296                && let Some(proof) = complete(idle)?
297            {
298                return Ok(Outcome::Complete(output, proof));
299            }
300            let tick = tick(self.source)?;
301            polled = self.state.next_root(&self.slot, tick)?.skip();
302        }
303    }
304}