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.
14/// Awaiting it yields the task's output, or an error if no output is available,
15/// for example after cancellation, a task panic, or runtime shutdown.
16#[derive(Debug)]
17pub struct JoinHandle<T> {
18    task: Task<T>,
19}
20
21impl<T> JoinHandle<T> {
22    pub(crate) fn new(task: Task<T>) -> Self {
23        JoinHandle { task }
24    }
25
26    /// Requests cancellation and discards the result without waiting for cleanup.
27    ///
28    /// The future is dropped when the runtime next processes the task, or
29    /// during runtime shutdown. A running poll is not interrupted.
30    pub fn cancel(self) {
31        self.task.cancel();
32    }
33
34    /// Detaches the task so it continues running independently.
35    pub fn detach(self) {}
36
37    /// Returns `true` if the task completed or was closed without an output.
38    pub fn is_finished(&self) -> bool {
39        self.task.is_finished()
40    }
41}
42
43impl<T> Future for JoinHandle<T> {
44    type Output = Result<T, JoinError>;
45
46    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
47        self.task.poll_join(cx).map(|out| out.ok_or(JoinError))
48    }
49}
50
51/// A native task handle returned by [`crate::rt::Handle::spawn()`].
52///
53/// It is neither `Send` nor `Sync` and stays on the calling thread.
54/// Submissions on the runtime thread retain the local spawn fast path.
55/// Dropping the handle detaches the task.
56/// Awaiting it yields the output or an error if the task ended without one,
57/// including when submission was rejected during shutdown.
58#[derive(Debug)]
59pub struct RemoteJoinHandle<T> {
60    inner: Inner<T>,
61}
62
63#[derive(Debug)]
64enum Inner<T> {
65    Local(JoinHandle<T>),
66    Remote(RemoteJoin<T>),
67}
68
69impl<T> RemoteJoinHandle<T> {
70    pub(crate) fn local(task: JoinHandle<T>) -> Self {
71        Self {
72            inner: Inner::Local(task),
73        }
74    }
75
76    pub(crate) fn remote(join: RemoteJoin<T>) -> Self {
77        Self {
78            inner: Inner::Remote(join),
79        }
80    }
81
82    /// Requests cancellation and discards the result without waiting for cleanup.
83    ///
84    /// An accepted future is dropped on the runtime thread, which must make
85    /// progress to observe a remote cancellation request. A running poll is not
86    /// interrupted.
87    pub fn cancel(self) {
88        match self.inner {
89            Inner::Local(t) => t.cancel(),
90            Inner::Remote(t) => t.cancel(),
91        }
92    }
93
94    /// Detaches the task so it continues running independently.
95    pub fn detach(self) {}
96
97    /// Returns `true` if the task completed or was closed without an output.
98    pub fn is_finished(&self) -> bool {
99        match &self.inner {
100            Inner::Local(t) => t.is_finished(),
101            Inner::Remote(t) => t.is_finished(),
102        }
103    }
104}
105
106impl<T> Future for RemoteJoinHandle<T> {
107    type Output = Result<T, JoinError>;
108
109    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
110        Poll::Ready(match &mut self.inner {
111            Inner::Local(t) => ready!(Pin::new(t).poll(cx)),
112            Inner::Remote(t) => ready!(t.poll_join(cx)).ok_or(JoinError),
113        })
114    }
115}
116
117/// Error returned by a native join handle when no task output is available.
118///
119/// The task may have been canceled, panicked, or ended during runtime shutdown.
120/// This error does not retain a cancellation reason or panic payload.
121#[derive(Debug, Copy, Clone)]
122pub struct JoinError;
123
124impl fmt::Display for JoinError {
125    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
126        write!(f, "JoinError")
127    }
128}
129
130impl std::error::Error for JoinError {}
131
132#[cfg(all(test, not(feature = "tokio"), not(feature = "compio")))]
133#[path = "../tests/unit/handle.rs"]
134mod tests;