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 ) -> 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}