Skip to main content

ntex_rt/
handle.rs

1use std::{fmt, panic::Location, pin::Pin, task::Context, task::Poll, task::ready};
2
3use crate::remote::RemoteJoin;
4
5type Task<T> = crate::rt::task::Task<T>;
6
7/// The location a task was spawned at.
8pub(crate) type SpawnLocation = &'static Location<'static>;
9
10/// A native local task handle. It is neither `Send` nor `Sync`.
11///
12/// Returned by [`crate::rt::spawn()`] and [`crate::rt::Runtime::spawn()`]. Dropping
13/// the handle detaches the task unless [`cancel_on_drop`](Self::cancel_on_drop)
14/// is enabled.
15/// Awaiting it yields the task's output, or an error if no output is available,
16/// for example after cancellation, a task panic, or runtime shutdown.
17#[derive(Debug)]
18pub struct JoinHandle<T> {
19    task: Task<T>,
20}
21
22impl<T> JoinHandle<T> {
23    pub(crate) fn new(task: Task<T>) -> Self {
24        JoinHandle { task }
25    }
26
27    /// Requests cancellation when this handle is dropped instead of detaching the task.
28    ///
29    /// Cancellation follows [`cancel`](Self::cancel) and does not wait for cleanup.
30    /// A running poll is not interrupted. The deprecated [`detach`](Self::detach)
31    /// method still overrides this setting.
32    #[must_use]
33    pub fn cancel_on_drop(self) -> Self {
34        self.task.set_cancel_on_drop(true);
35        self
36    }
37
38    /// Requests cancellation and discards the result without waiting for cleanup.
39    ///
40    /// The future is dropped when the runtime next processes the task, or
41    /// during runtime shutdown. A running poll is not interrupted.
42    pub fn cancel(self) {
43        self.task.cancel();
44    }
45
46    /// Detaches the task so it continues running independently.
47    ///
48    /// Overrides [`cancel_on_drop`](Self::cancel_on_drop) without requesting cancellation.
49    #[deprecated(
50        since = "4.5.0",
51        note = "Drop the handle instead if cancel_on_drop() is not enabled"
52    )]
53    pub fn detach(self) {
54        self.task.set_cancel_on_drop(false);
55    }
56
57    /// Returns `true` if the task completed or was closed without an output.
58    pub fn is_finished(&self) -> bool {
59        self.task.is_finished()
60    }
61}
62
63impl<T> Future for JoinHandle<T> {
64    type Output = Result<T, JoinError>;
65
66    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
67        self.task.poll_join(cx).map(|out| out.ok_or(JoinError))
68    }
69}
70
71/// A native task handle returned by [`crate::rt::Handle::spawn()`].
72///
73/// It is neither `Send` nor `Sync` and stays on the calling thread.
74/// Submissions on the runtime thread retain the local spawn fast path.
75/// Dropping the handle detaches the task.
76/// Awaiting it yields the output or an error if the task ended without one,
77/// including when submission was rejected during shutdown.
78#[derive(Debug)]
79pub struct RemoteJoinHandle<T> {
80    inner: Inner<T>,
81}
82
83#[derive(Debug)]
84enum Inner<T> {
85    Local(JoinHandle<T>),
86    Remote(RemoteJoin<T>),
87}
88
89impl<T> RemoteJoinHandle<T> {
90    pub(crate) fn local(task: JoinHandle<T>) -> Self {
91        Self {
92            inner: Inner::Local(task),
93        }
94    }
95
96    pub(crate) fn remote(join: RemoteJoin<T>) -> Self {
97        Self {
98            inner: Inner::Remote(join),
99        }
100    }
101
102    /// Requests cancellation and discards the result without waiting for cleanup.
103    ///
104    /// An accepted future is dropped on the runtime thread, which must make
105    /// progress to observe a remote cancellation request. A running poll is not
106    /// interrupted.
107    pub fn cancel(self) {
108        match self.inner {
109            Inner::Local(t) => t.cancel(),
110            Inner::Remote(t) => t.cancel(),
111        }
112    }
113
114    /// Detaches the task so it continues running independently.
115    ///
116    /// This method does nothing beyond dropping the handle; dropping it already
117    /// detaches the task without canceling it.
118    #[deprecated(since = "4.5.0", note = "Use drop(handle) instead")]
119    pub fn detach(self) {}
120
121    /// Returns `true` if the task completed or was closed without an output.
122    pub fn is_finished(&self) -> bool {
123        match &self.inner {
124            Inner::Local(t) => t.is_finished(),
125            Inner::Remote(t) => t.is_finished(),
126        }
127    }
128}
129
130impl<T> Future for RemoteJoinHandle<T> {
131    type Output = Result<T, JoinError>;
132
133    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
134        Poll::Ready(match &mut self.inner {
135            Inner::Local(t) => ready!(Pin::new(t).poll(cx)),
136            Inner::Remote(t) => ready!(t.poll_join(cx)).ok_or(JoinError),
137        })
138    }
139}
140
141/// Error returned by a native join handle when no task output is available.
142///
143/// The task may have been canceled, panicked, or ended during runtime shutdown.
144/// This error does not retain a cancellation reason or panic payload.
145#[derive(Debug, Copy, Clone)]
146pub struct JoinError;
147
148impl fmt::Display for JoinError {
149    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
150        write!(f, "JoinError")
151    }
152}
153
154impl std::error::Error for JoinError {}
155
156#[cfg(test)]
157#[path = "../tests/unit/handle.rs"]
158mod tests;