Skip to main content

linera_base/
task.rs

1// Copyright (c) Zefchain Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4/*!
5Abstractions over tasks that can be used natively or on the Web.
6 */
7
8use futures::{future, Future, FutureExt as _};
9
10/// The type of a future awaiting another task.
11///
12/// On drop, the remote task will be asynchronously cancelled, but will remain
13/// alive until it reaches a yield point.
14///
15/// To wait for the task to be fully cancelled, use [`Task::cancel`].
16pub struct Task<R> {
17    abort_handle: future::AbortHandle,
18    output: future::RemoteHandle<Result<R, future::Aborted>>,
19}
20
21impl<R: 'static> Task<R> {
22    fn spawn_<F: Future<Output = R>, T>(
23        future: F,
24        spawn: impl FnOnce(future::Remote<future::Abortable<F>>) -> T,
25    ) -> Self {
26        let (abortable_future, abort_handle) = future::abortable(future);
27        let (task, output) = abortable_future.remote_handle();
28        let _ = spawn(task);
29        Self {
30            abort_handle,
31            output,
32        }
33    }
34
35    /// Spawns a new task, potentially on the current thread.
36    #[cfg(not(web))]
37    pub fn spawn<F: Future<Output = R> + Send + 'static>(future: F) -> Self
38    where
39        R: Send,
40    {
41        Self::spawn_(future, tokio::task::spawn)
42    }
43
44    /// Spawns a new task on the current thread.
45    #[cfg(web)]
46    pub fn spawn<F: Future<Output = R> + 'static>(future: F) -> Self {
47        Self::spawn_(future, wasm_bindgen_futures::spawn_local)
48    }
49
50    /// Creates a [`Task`] that is immediately ready.
51    pub fn ready(value: R) -> Self {
52        Self::spawn_(async { value }, |fut| {
53            fut.now_or_never().expect("the future is ready")
54        })
55    }
56
57    /// Cancels the task, resolving only when the wrapped future is completely dropped.
58    pub async fn cancel(self) {
59        self.abort_handle.abort();
60        let _ = self.output.await;
61    }
62
63    /// Forgets the task. The task will continue to run to completion in the
64    /// background, but will no longer be joinable or cancelable.
65    pub fn forget(self) {
66        self.output.forget();
67    }
68}
69
70impl<R: 'static> std::future::IntoFuture for Task<R> {
71    type Output = R;
72    type IntoFuture = future::Map<
73        future::RemoteHandle<Result<R, future::Aborted>>,
74        fn(Result<R, future::Aborted>) -> R,
75    >;
76
77    fn into_future(self) -> Self::IntoFuture {
78        self.output
79            .map(|result| result.expect("we have the only AbortHandle"))
80    }
81}