Skip to main content

oxframe_core/
flow.rs

1use std::cell::Cell;
2use std::future::Future;
3use std::pin::Pin;
4use std::rc::Rc;
5use std::task::{Context, Poll};
6
7use crate::runtime;
8use crate::tween::progress;
9
10/// A boxed scene future, e.g. for [`sequence`].
11pub type BoxFuture<T = ()> = Pin<Box<dyn Future<Output = T>>>;
12
13/// Polls `fut` with its own virtual time and writes the time back.
14fn poll_scoped<F: Future + ?Sized>(
15    fut: Pin<&mut F>,
16    time: &mut f64,
17    cx: &mut Context<'_>,
18) -> Poll<F::Output> {
19    runtime::with(|rt| {
20        let outer = rt.task_time.replace(*time);
21        // `with` holds only a shared borrow of the thread-local, so nested
22        // runtime calls from inside the poll are fine.
23        let r = fut.poll(cx);
24        *time = rt.task_time.replace(outer);
25        r
26    })
27}
28
29// ---------------------------------------------------------------- timers
30
31/// Waits `seconds` of scene time.
32pub fn wait_for(seconds: f64) -> WaitFor {
33    WaitFor {
34        duration: seconds.max(0.0),
35        start: None,
36    }
37}
38
39#[must_use = "futures do nothing unless awaited"]
40pub struct WaitFor {
41    duration: f64,
42    start: Option<f64>,
43}
44
45impl Future for WaitFor {
46    type Output = ();
47
48    fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<()> {
49        let start = *self.start.get_or_insert_with(runtime::time);
50        match progress(start, self.duration) {
51            None => Poll::Ready(()),
52            Some(_) => Poll::Pending,
53        }
54    }
55}
56
57/// Waits until marker `name` from `meta.json`. On first encounter the
58/// current time becomes the marker's default, so it does not wait at all.
59pub fn wait_until(name: &str) -> WaitUntil {
60    WaitUntil {
61        name: name.to_owned(),
62        inner: None,
63    }
64}
65
66#[must_use = "futures do nothing unless awaited"]
67pub struct WaitUntil {
68    name: String,
69    inner: Option<WaitFor>,
70}
71
72impl Future for WaitUntil {
73    type Output = ();
74
75    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
76        let this = &mut *self;
77        let inner = this.inner.get_or_insert_with(|| {
78            let target = runtime::marker(&this.name);
79            wait_for(target - runtime::time())
80        });
81        Pin::new(inner).poll(cx)
82    }
83}
84
85/// Yields until the next frame.
86pub fn next_frame() -> NextFrame {
87    NextFrame { frame: None }
88}
89
90#[must_use = "futures do nothing unless awaited"]
91pub struct NextFrame {
92    frame: Option<u32>,
93}
94
95impl Future for NextFrame {
96    type Output = ();
97
98    fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<()> {
99        let current = runtime::frame();
100        let first = *self.frame.get_or_insert(current);
101        if current > first {
102            runtime::with(|rt| rt.task_time.set(rt.task_time.get().max(rt.now())));
103            Poll::Ready(())
104        } else {
105            Poll::Pending
106        }
107    }
108}
109
110// ---------------------------------------------------------------- all
111
112/// One branch of [`all`]: a future with its own virtual time.
113pub struct Branch<F: Future> {
114    fut: Option<Pin<Box<F>>>,
115    out: Option<F::Output>,
116    time: f64,
117}
118
119impl<F: Future> Branch<F> {
120    fn new(fut: F) -> Self {
121        Self {
122            fut: Some(Box::pin(fut)),
123            out: None,
124            time: f64::NAN,
125        }
126    }
127
128    /// Returns true when finished.
129    fn poll(&mut self, start: f64, cx: &mut Context<'_>) -> bool {
130        if self.time.is_nan() {
131            self.time = start;
132        }
133        if let Some(fut) = self.fut.as_mut()
134            && let Poll::Ready(v) = poll_scoped(fut.as_mut(), &mut self.time, cx)
135        {
136            self.out = Some(v);
137            self.fut = None;
138        }
139        self.fut.is_none()
140    }
141
142    fn take(&mut self) -> F::Output {
143        self.out.take().expect("branch output taken twice")
144    }
145}
146
147/// Runs futures concurrently and completes when all of them have. Takes a
148/// tuple of up to 12 futures and returns a tuple of their outputs.
149///
150/// Every branch starts at the same time; afterwards the task continues from
151/// the latest end time among them.
152pub fn all<T: IntoAll>(futures: T) -> All<T::Branches> {
153    All {
154        branches: futures.into_branches(),
155        start: None,
156    }
157}
158
159#[must_use = "futures do nothing unless awaited"]
160/// Future returned by [`all`].
161pub struct All<B> {
162    branches: B,
163    start: Option<f64>,
164}
165
166pub trait IntoAll {
167    type Branches: Branches;
168    fn into_branches(self) -> Self::Branches;
169}
170
171pub trait Branches {
172    type Output;
173    /// Polls unfinished branches; returns the max end time once all are done.
174    fn poll_all(&mut self, start: f64, cx: &mut Context<'_>) -> Option<f64>;
175    fn take(&mut self) -> Self::Output;
176}
177
178impl<B: Branches + Unpin> Future for All<B> {
179    type Output = B::Output;
180
181    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<B::Output> {
182        let this = &mut *self;
183        let start = *this.start.get_or_insert_with(runtime::time);
184        match this.branches.poll_all(start, cx) {
185            Some(end) => {
186                runtime::with(|rt| rt.task_time.set(end));
187                Poll::Ready(this.branches.take())
188            }
189            None => Poll::Pending,
190        }
191    }
192}
193
194macro_rules! impl_all {
195    ($($F:ident $i:tt),+) => {
196        impl<$($F: Future),+> IntoAll for ($($F,)+) {
197            type Branches = ($(Branch<$F>,)+);
198            fn into_branches(self) -> Self::Branches {
199                ($(Branch::new(self.$i),)+)
200            }
201        }
202
203        impl<$($F: Future),+> Branches for ($(Branch<$F>,)+) {
204            type Output = ($($F::Output,)+);
205
206            fn poll_all(&mut self, start: f64, cx: &mut Context<'_>) -> Option<f64> {
207                let mut done = true;
208                $( done &= self.$i.poll(start, cx); )+
209                done.then(|| {
210                    let mut end = start;
211                    $( end = end.max(self.$i.time); )+
212                    end
213                })
214            }
215
216            fn take(&mut self) -> Self::Output {
217                ($(self.$i.take(),)+)
218            }
219        }
220    };
221}
222
223impl_all!(A 0);
224impl_all!(A 0, B 1);
225impl_all!(A 0, B 1, C 2);
226impl_all!(A 0, B 1, C 2, D 3);
227impl_all!(A 0, B 1, C 2, D 3, E 4);
228impl_all!(A 0, B 1, C 2, D 3, E 4, F 5);
229impl_all!(A 0, B 1, C 2, D 3, E 4, F 5, G 6);
230impl_all!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7);
231impl_all!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8);
232impl_all!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9);
233impl_all!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10);
234impl_all!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11);
235
236/// [`all`] for a dynamic number of futures.
237pub fn all_vec<T: 'static>(futures: Vec<BoxFuture<T>>) -> All<VecBranches<T>> {
238    All {
239        branches: VecBranches(futures.into_iter().map(Branch::new).collect()),
240        start: None,
241    }
242}
243
244pub struct VecBranches<T>(Vec<Branch<BoxFuture<T>>>);
245
246impl<T> Branches for VecBranches<T> {
247    type Output = Vec<T>;
248
249    fn poll_all(&mut self, start: f64, cx: &mut Context<'_>) -> Option<f64> {
250        let mut done = true;
251        for b in &mut self.0 {
252            done &= b.poll(start, cx);
253        }
254        done.then(|| self.0.iter().fold(start, |end, b| end.max(b.time)))
255    }
256
257    fn take(&mut self) -> Vec<T> {
258        self.0.iter_mut().map(Branch::take).collect()
259    }
260}
261
262/// Starts the futures one after another with `delay` seconds between their
263/// starts; they then run concurrently. Completes when all have.
264pub fn sequence(delay: f64, futures: Vec<BoxFuture>) -> All<VecBranches<()>> {
265    all_vec(
266        futures
267            .into_iter()
268            .enumerate()
269            .map(|(i, f)| -> BoxFuture {
270                let offset = delay * i as f64;
271                Box::pin(async move {
272                    wait_for(offset).await;
273                    f.await;
274                })
275            })
276            .collect(),
277    )
278}
279
280// ---------------------------------------------------------------- spawn
281
282pub(crate) struct TaskState {
283    pub finished: Cell<bool>,
284    pub cancelled: Cell<bool>,
285}
286
287pub(crate) struct Task {
288    pub fut: BoxFuture,
289    pub time: f64,
290    pub state: Rc<TaskState>,
291}
292
293impl Task {
294    /// Returns true when the task is done (finished or cancelled).
295    pub fn poll(&mut self, cx: &mut Context<'_>) -> bool {
296        if self.state.cancelled.get() {
297            return true;
298        }
299        if poll_scoped(self.fut.as_mut(), &mut self.time, cx).is_ready() {
300            self.state.finished.set(true);
301        }
302        self.state.finished.get()
303    }
304}
305
306/// Runs `fut` in the background, starting this frame. Background tasks stop
307/// when the scene ends.
308pub fn spawn(fut: impl Future<Output = ()> + 'static) -> JoinHandle {
309    let state = Rc::new(TaskState {
310        finished: Cell::new(false),
311        cancelled: Cell::new(false),
312    });
313    runtime::with(|rt| {
314        rt.spawned.borrow_mut().push(Task {
315            fut: Box::pin(fut),
316            time: rt.task_time.get(),
317            state: state.clone(),
318        });
319    });
320    JoinHandle { state }
321}
322
323/// Awaiting it waits for the spawned task to finish.
324pub struct JoinHandle {
325    state: Rc<TaskState>,
326}
327
328impl JoinHandle {
329    /// Whether the task has completed or was cancelled.
330    pub fn is_finished(&self) -> bool {
331        self.state.finished.get()
332    }
333
334    /// Stops the task at its next await point.
335    pub fn cancel(&self) {
336        self.state.cancelled.set(true);
337    }
338}
339
340impl Future for JoinHandle {
341    type Output = ();
342
343    fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<()> {
344        if self.state.finished.get() || self.state.cancelled.get() {
345            Poll::Ready(())
346        } else {
347            Poll::Pending
348        }
349    }
350}