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}