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#[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_drain<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}