Skip to main content

async_rs/util/
task.rs

1use async_trait::async_trait;
2use std::{
3    future::Future,
4    pin::Pin,
5    task::{Context, Poll},
6};
7
8/// A wrapper around implementation-specific tasks that implement the TaskImpl trait
9///
10/// Awaiting a `Task` yields the task's output. That output type leaves no room to report a
11/// failure, so on the backends which can detect one — tokio, smol and async-global-executor — a
12/// task which panicked resumes its panic in the awaiting task, and awaiting one which was
13/// canceled, or whose runtime went away, panics too. [`Noop`](crate::Noop) is the exception: it
14/// runs nothing, and its tasks simply never complete.
15///
16/// Note that `cancel` takes `&mut self`, so awaiting a `Task` after canceling it is expressible
17/// and panics rather than failing to compile. The same goes for a `cancel` which was itself
18/// dropped before it completed — in a `select!`, or a [`TryJoin`](crate::util::TryJoin) which
19/// short-circuited: it has already given up the underlying task by then, so the `Task` is spent
20/// even though no cancellation result was ever handed back.
21#[derive(Debug)]
22pub struct Task<I: TaskImpl>(I);
23
24impl<I: TaskImpl> Task<I> {
25    /// Cancel the task, returning data if it was already finished
26    ///
27    /// This gives up the underlying task, so the `Task` has nothing left to wait for: see the
28    /// type-level docs for what awaiting it afterwards does.
29    ///
30    /// `None` means the task did not complete, and does not say why: one which panicked comes back
31    /// as `None` here rather than resuming its panic the way awaiting it would. The output type
32    /// has no room to tell the two apart, on any of the backends.
33    pub async fn cancel(&mut self) -> Option<<Self as Future>::Output> {
34        self.0.cancel().await
35    }
36}
37
38impl<I: TaskImpl> From<I> for Task<I> {
39    fn from(task_impl: I) -> Self {
40        Self(task_impl)
41    }
42}
43
44impl<I: TaskImpl> Future for Task<I> {
45    type Output = <I as Future>::Output;
46
47    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
48        Pin::new(&mut self.0).poll(cx)
49    }
50}
51
52impl<I: TaskImpl> Drop for Task<I> {
53    fn drop(&mut self) {
54        self.0.detach();
55    }
56}
57
58/// A common interface to wait for a Task completion, let it run in the background or cancel it.
59#[async_trait]
60pub trait TaskImpl: Future + Send + Unpin + 'static {
61    /// Cancels the task and waits for it to stop running.
62    ///
63    /// Returns the task's output if it was completed just before it got canceled, or None if it
64    /// didn't complete.
65    async fn cancel(&mut self) -> Option<<Self as Future>::Output> {
66        None
67    }
68
69    /// "Detach" the task from the current context to let it run in the background.
70    ///
71    /// Note that this is automatically called when dropping the Task so that it doesn't get
72    /// canceled.
73    fn detach(&mut self)
74    where
75        Self: Sized,
76    {
77    }
78}