executor_core/
async_task.rs

1//! Integration with the `async-task` crate.
2//!
3//! This module provides a unified wrapper around the `async-task` crate that can be used
4//! by different executor implementations. It offers task spawning utilities and a
5//! task wrapper that implements the [`Task`] trait.
6
7use crate::{Error, Task};
8use core::{
9    future::Future,
10    panic::AssertUnwindSafe,
11    pin::Pin,
12    task::{Context, Poll},
13};
14
15use alloc::boxed::Box;
16pub use async_task::{Runnable, Task as RawTask};
17
18#[cfg(feature = "std")]
19use crate::catch_unwind;
20
21#[cfg(not(feature = "std"))]
22fn catch_unwind<F, R>(f: F) -> Result<R, Error>
23where
24    F: FnOnce() -> R,
25{
26    // In no-std environments (like WASM), we can't catch panics
27    // so we just execute the function directly
28    Ok(f())
29}
30
31/// A task wrapper that implements the [`Task`] trait.
32///
33/// This provides panic safety and proper error handling for tasks created
34/// with the `async-task` crate.
35pub struct AsyncTask<T>(async_task::Task<T>);
36
37impl<T> AsyncTask<T> {
38    /// Await the result of the task, returning a [`Result`].
39    pub async fn result(self) -> Result<T, Box<dyn core::any::Any + Send>> {
40        crate::Task::result(self).await
41    }
42
43    /// Cancel the task, waiting for it to finish cancelling.
44    pub async fn cancel(self) {
45        self.0.cancel().await;
46    }
47}
48
49impl<T> core::fmt::Debug for AsyncTask<T> {
50    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
51        f.debug_struct("AsyncTask").finish_non_exhaustive()
52    }
53}
54
55impl<T> From<async_task::Task<T>> for AsyncTask<T> {
56    fn from(task: async_task::Task<T>) -> Self {
57        Self(task)
58    }
59}
60
61impl<T> Future for AsyncTask<T> {
62    type Output = T;
63
64    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
65        Pin::new(&mut self.0).poll(cx)
66    }
67}
68
69impl<T> Task<T> for AsyncTask<T> {
70    fn poll_result(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<T, Error>> {
71        match catch_unwind(AssertUnwindSafe(|| Pin::new(&mut self.0).poll(cx))) {
72            Ok(Poll::Ready(value)) => Poll::Ready(Ok(value)),
73            Ok(Poll::Pending) => Poll::Pending,
74            Err(error) => Poll::Ready(Err(error)),
75        }
76    }
77}
78
79/// Spawn a future with a custom scheduler using `async_task`.
80///
81/// This function creates a task that will be scheduled using the provided scheduler function.
82/// The scheduler receives a [`Runnable`] that should be executed to make progress on the task.
83///
84/// Returns a tuple of (runnable, task) where:
85/// - `runnable` should be scheduled immediately to start the task
86/// - `task` is an [`AsyncTask`] that can be awaited for the result
87pub fn spawn<F, S>(future: F, scheduler: S) -> (Runnable, AsyncTask<F::Output>)
88where
89    F: Future + Send + 'static,
90    F::Output: Send + 'static,
91    S: Fn(Runnable) + Send + Sync + 'static,
92{
93    let (runnable, task) = async_task::spawn(future, scheduler);
94    (runnable, AsyncTask::from(task))
95}
96
97/// Spawn a local (non-Send) future with a custom scheduler using `async_task`.
98///
99/// This is similar to [`spawn`] but works with futures that are not `Send`.
100/// It uses `async_task::spawn_local` internally.
101///
102/// This function is only available when the `std` feature is enabled.
103#[cfg(feature = "std")]
104pub fn spawn_local<F, S>(future: F, scheduler: S) -> (Runnable, AsyncTask<F::Output>)
105where
106    F: Future + 'static,
107    S: Fn(Runnable) + Send + Sync + 'static,
108{
109    let (runnable, task) = async_task::spawn_local(future, scheduler);
110    (runnable, AsyncTask::from(task))
111}