jay_config/
tasks.rs

1//! Tools for async task management.
2
3use std::{
4    cell::Cell,
5    fmt::{Debug, Formatter},
6    future::Future,
7    pin::Pin,
8    rc::Rc,
9    task::{Context, Poll, Waker},
10};
11
12/// Spawns an asynchronous task that will run in the background.
13pub fn spawn<T, F>(f: F) -> JoinHandle<T>
14where
15    T: 'static,
16    F: Future<Output = T> + 'static,
17{
18    let slot = match try_get!() {
19        None => Rc::new(JoinSlot {
20            task_id: 0,
21            slot: Cell::new(None),
22            waker: Cell::new(None),
23        }),
24        Some(c) => c.spawn_task(f),
25    };
26    JoinHandle { slot }
27}
28
29pub(crate) struct JoinSlot<T> {
30    pub task_id: u64,
31    pub slot: Cell<Option<T>>,
32    pub waker: Cell<Option<Waker>>,
33}
34
35/// A handle to join or abort a spawned task.
36///
37/// When the handle is dropped, the task continues to run in the background.
38pub struct JoinHandle<T> {
39    slot: Rc<JoinSlot<T>>,
40}
41
42impl<T> Debug for JoinHandle<T> {
43    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
44        f.debug_struct("JoinHandle")
45            .field("task_id", &self.slot.task_id)
46            .finish_non_exhaustive()
47    }
48}
49
50impl<T> Unpin for JoinHandle<T> {}
51
52impl<T> JoinHandle<T> {
53    /// Aborts the task immediately.
54    pub fn abort(self) {
55        get!().abort_task(self.slot.task_id);
56    }
57}
58
59impl<T> Future for JoinHandle<T> {
60    type Output = T;
61
62    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
63        if let Some(t) = self.slot.slot.take() {
64            return Poll::Ready(t);
65        }
66        self.slot.waker.set(Some(cx.waker().clone()));
67        Poll::Pending
68    }
69}