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 idle = match parked.sync() {
135                    Ok(Some(idle)) => idle,
136                    Ok(None) => continue,
137                    Err(error) => return Attempt::Cancel(root, error.into()),
138                };
139                let progress = idle.progress();
140                if let Err(error) = idle.wait(progress, self.source) {
141                    return Attempt::Cancel(root, error.into());
142                }
143            }
144        };
145        Attempt::Complete(loop {
146            let parked = polled.park();
147            match parked.status() {
148                turn::Status::Active => {}
149                turn::Status::Shutdown => break Err(drive::Error::Shutdown),
150            }
151            let idle = match parked.sync() {
152                Ok(idle) => idle,
153                Err(error) => break Err(error.into()),
154            };
155            if let Some(idle) = idle {
156                let progress = idle.progress();
157                if matches!(progress, schedule::Progress::Quiescent) {
158                    match idle.sync() {
159                        Ok(poll::Commit::Drained) => break Ok(output),
160                        Ok(poll::Commit::Pending) => {}
161                        Err(error) => break Err(error.into()),
162                    }
163                } else if let Err(error) = idle.wait(progress, self.source) {
164                    break Err(error.into());
165                }
166            }
167            let tick = self.source.tick();
168            polled = match self.state.next_root(&self.slot, tick, &mut shutdown) {
169                Ok(ready) => ready.skip(),
170                Err(error) => break Err(error),
171            };
172        })
173    }
174}
175
176impl<'d, T, C> Block<'d, T, C> {
177    pub(crate) fn new(
178        driver: driver::Reference<'d>,
179        state: T,
180        source: C,
181    ) -> Result<Self, drive::Error> {
182        let target = driver
183            .targets::<routes::Wake>()
184            .bind(indices::SlotIndex::ZERO, route::Epoch::INITIAL);
185        let slot = driver.ready().make_slot(target.dispatch())?;
186        Ok(Self {
187            state,
188            source,
189            slot,
190        })
191    }
192}
193
194impl<'run, 'driver, 'app, 'd: 'app, D, S, C>
195    Block<'d, scope::Scope<'run, 'driver, 'app, 'd, D, S>, C>
196where
197    D: executor::raw::App<'d>,
198    C: clock::Time,
199{
200    pub(crate) fn run<R>(
201        self,
202        root: R,
203        tick: impl Fn(C) -> Result<Sample, drive::Error>,
204        shutdown: impl FnMut() -> turn::Status,
205    ) -> Result<R::Output, drive::Error>
206    where
207        R: root::Root<'d>,
208    {
209        let (output, ()) = self
210            .drive(root, tick, shutdown, |idle| {
211                Ok(matches!(idle.sync()?, poll::Commit::Drained).then_some(()))
212            })?
213            .complete()?;
214        Ok(output)
215    }
216
217    pub(crate) fn run_until<R>(
218        self,
219        root: R,
220        tick: impl Fn(C) -> Result<Sample, drive::Error>,
221        shutdown: impl FnMut() -> turn::Status,
222    ) -> Result<ControlFlow<R::Output>, drive::Error>
223    where
224        R: root::Root<'d>,
225    {
226        Ok(
227            match self.drive(root, tick, shutdown, |idle| {
228                Ok(matches!(idle.sync()?, poll::Commit::Drained).then_some(()))
229            })? {
230                Outcome::Complete(output, ()) | Outcome::Shutdown(Some(output)) => {
231                    ControlFlow::Break(output)
232                }
233                Outcome::Shutdown(None) => ControlFlow::Continue(()),
234            },
235        )
236    }
237
238    pub(crate) fn run_fixed<R>(
239        self,
240        root: R,
241        tick: impl Fn(C) -> Result<Sample, drive::Error>,
242        shutdown: impl FnMut() -> turn::Status,
243    ) -> Result<Fixed<'d, R::Output>, drive::Error>
244    where
245        R: root::Root<'d>,
246    {
247        let (output, now) = self
248            .drive(root, tick, shutdown, |idle| {
249                let fixed = idle.is_fixed();
250                let now = idle.now();
251                let commit = idle.sync()?;
252                Ok((fixed && matches!(commit, poll::Commit::Drained)).then_some(now))
253            })?
254            .complete()?;
255        Ok(Fixed::new(output, now))
256    }
257
258    fn drive<R, P>(
259        mut self,
260        root: R,
261        tick: impl Fn(C) -> Result<Sample, drive::Error>,
262        mut shutdown: impl FnMut() -> turn::Status,
263        complete: impl for<'turn> Fn(
264            turn::Idle<'turn, 'd, modes::App<'turn, 'app, 'd, D, S>>,
265        ) -> Result<Option<P>, drive::Error>,
266    ) -> Result<Outcome<R::Output, P>, drive::Error>
267    where
268        R: root::Root<'d>,
269    {
270        let wake = self.slot.target();
271        let (mut polled, output) = {
272            let mut root = pin::pin!(root);
273            let mut first = true;
274            loop {
275                let tick = tick(self.source)?;
276                let ready = self.state.next_root(&self.slot, tick, &mut shutdown)?;
277                let poll = first || ready.is_root();
278                let (polled, output) = if poll {
279                    ready.poll(root.as_mut(), wake)
280                } else {
281                    (ready.skip(), task::Poll::Pending)
282                };
283                first = false;
284                match output {
285                    task::Poll::Ready(output) => break (polled, output),
286                    task::Poll::Pending => {
287                        let parked = polled.park();
288                        match parked.status() {
289                            turn::Status::Active => {}
290                            turn::Status::Shutdown => return Ok(Outcome::Shutdown(None)),
291                        }
292                        if let Some(idle) = parked.sync()? {
293                            let progress = idle.progress();
294                            idle.wait(progress, self.source)?;
295                        }
296                    }
297                }
298            }
299        };
300        loop {
301            let parked = polled.park();
302            match parked.status() {
303                turn::Status::Active => {}
304                turn::Status::Shutdown => return Ok(Outcome::Shutdown(Some(output))),
305            }
306            if let Some(idle) = parked.sync()?
307                && let Some(proof) = complete(idle)?
308            {
309                return Ok(Outcome::Complete(output, proof));
310            }
311            let tick = tick(self.source)?;
312            polled = self.state.next_app(tick, &mut shutdown)?;
313        }
314    }
315}