Skip to main content

dope_runtime/executor/drive/
block.rs

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