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;