1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
//! Asynchronous tasks based on Theseus's native OS [task] subsystem.

use alloc::boxed::Box;
use core::{
    future::Future,
    marker::PhantomData,
    pin::Pin,
    task::{Context, Poll},
};
use task::{ExitValue, JoinableTaskRef, KillReason, PanicInfoOwned};

/// Spawns a new asynchronous task, returning a [`JoinableAsyncTaskRef`] for it.
///
/// You do not need to poll the returned object to make the async task execute;
/// it will begin running in the background immediately.
///
/// # Errors
///
/// This function will return errors generated by [`spawn::TaskBuilder::spawn()`].
pub fn spawn_async<F>(
    future: F,
) -> core::result::Result<JoinableAsyncTaskRef<F::Output>, &'static str>
where
    F: Future + Send + 'static,
    F::Output: Send,
{
    let future = Box::pin(future);
    let task = spawn::new_task_builder(crate::block_on, future).spawn()?;
    Ok(JoinableAsyncTaskRef {
        task,
        phantom_data: PhantomData,
    })
}

/// An owned permission to join an async task.
pub struct JoinableAsyncTaskRef<T> {
    pub(crate) task: JoinableTaskRef,
    pub(crate) phantom_data: PhantomData<T>,
}

impl<T> JoinableAsyncTaskRef<T> {
    /// Abort the task associated with the handle.
    ///
    /// If the cancelled task was already completed at the time it was
    /// cancelled, it will return the successful result. Otherwise, polling the
    /// handle will fail with an [`Error::Cancelled`].
    ///
    /// # Warning
    ///
    /// This uses [`Task::kill`] and so the aborted task isn't unwound.
    pub fn abort(&self) {
        let _ = self.task.kill(KillReason::Requested);
    }

    /// Returns whether the task associated with the handle has finished.
    pub fn is_finished(&self) -> bool {
        self.task.has_exited()
    }
}

impl<T> Future for JoinableAsyncTaskRef<T>
where
    T: 'static,
{
    type Output = Result<T>;

    fn poll(self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Self::Output> {
        self.task.set_waker(context.waker().clone());
        if self.is_finished() {
            Poll::Ready(match self.task.join() {
                Ok(exit_value) => match exit_value {
                    ExitValue::Completed(value) => Ok(*value.downcast().unwrap()),
                    ExitValue::Killed(reason) => match reason {
                        KillReason::Requested => Err(Error::Cancelled),
                        KillReason::Panic(info) => Err(Error::Panic(info)),
                        KillReason::Exception(num) => Err(Error::Exception(num)),
                    },
                },
                Err(s) => Err(Error::Join(s)),
            })
        } else {
            Poll::Pending
        }
    }
}

pub type Result<T> = core::result::Result<T, Error>;

/// An error returned from polling a [`JoinableAsyncTaskRef`].
#[derive(Debug)]
pub enum Error {
    Cancelled,
    Panic(PanicInfoOwned),
    /// A `Join` error should not occur; this indicates a BUG in Theseus's task mgmt.
    Join(&'static str),
    Exception(u8),
}