1use std::any::Any;
2use std::fmt;
3use std::io;
4
5use super::Id;
6use crate::util::SyncWrapper;
7cfg_rt! {
8 /// Task failed to execute to completion.
9 pub struct JoinError {
10 repr: Repr,
11 id: Id,
12 }
13}
14
15enum Repr {
16 Cancelled,
17 Panic(SyncWrapper<Box<dyn Any + Send + 'static>>),
18}
19
20impl JoinError {
21 pub(crate) fn cancelled(id: Id) -> JoinError {
22 JoinError {
23 repr: Repr::Cancelled,
24 id,
25 }
26 }
27
28 pub(crate) fn panic(id: Id, err: Box<dyn Any + Send + 'static>) -> JoinError {
29 JoinError {
30 repr: Repr::Panic(SyncWrapper::new(err)),
31 id,
32 }
33 }
34
35 /// Returns true if the error was caused by the task being cancelled.
36 pub fn is_cancelled(&self) -> bool {
37 matches!(&self.repr, Repr::Cancelled)
38 }
39
40 /// Returns true if the error was caused by the task panicking.
41 ///
42 /// # Examples
43 ///
44 /// ```
45 /// use std::panic;
46 ///
47 /// #[tokio::main]
48 /// async fn main() {
49 /// let err = tokio::spawn(async {
50 /// panic!("boom");
51 /// }).await.unwrap_err();
52 ///
53 /// assert!(err.is_panic());
54 /// }
55 /// ```
56 pub fn is_panic(&self) -> bool {
57 matches!(&self.repr, Repr::Panic(_))
58 }
59
60 /// Consumes the join error, returning the object with which the task panicked.
61 ///
62 /// # Panics
63 ///
64 /// `into_panic()` panics if the `Error` does not represent the underlying
65 /// task terminating with a panic. Use `is_panic` to check the error reason
66 /// or `try_into_panic` for a variant that does not panic.
67 ///
68 /// # Examples
69 ///
70 /// ```should_panic
71 /// use std::panic;
72 ///
73 /// #[tokio::main]
74 /// async fn main() {
75 /// let err = tokio::spawn(async {
76 /// panic!("boom");
77 /// }).await.unwrap_err();
78 ///
79 /// if err.is_panic() {
80 /// // Resume the panic on the main task
81 /// panic::resume_unwind(err.into_panic());
82 /// }
83 /// }
84 /// ```
85 #[track_caller]
86 pub fn into_panic(self) -> Box<dyn Any + Send + 'static> {
87 self.try_into_panic()
88 .expect("`JoinError` reason is not a panic.")
89 }
90
91 /// Consumes the join error, returning the object with which the task
92 /// panicked if the task terminated due to a panic. Otherwise, `self` is
93 /// returned.
94 ///
95 /// # Examples
96 ///
97 /// ```should_panic
98 /// use std::panic;
99 ///
100 /// #[tokio::main]
101 /// async fn main() {
102 /// let err = tokio::spawn(async {
103 /// panic!("boom");
104 /// }).await.unwrap_err();
105 ///
106 /// if let Ok(reason) = err.try_into_panic() {
107 /// // Resume the panic on the main task
108 /// panic::resume_unwind(reason);
109 /// }
110 /// }
111 /// ```
112 pub fn try_into_panic(self) -> Result<Box<dyn Any + Send + 'static>, JoinError> {
113 match self.repr {
114 Repr::Panic(p) => Ok(p.into_inner()),
115 _ => Err(self),
116 }
117 }
118
119 /// Returns a [task ID] that identifies the task which errored relative to
120 /// other currently spawned tasks.
121 ///
122 /// **Note**: This is an [unstable API][unstable]. The public API of this type
123 /// may break in 1.x releases. See [the documentation on unstable
124 /// features][unstable] for details.
125 ///
126 /// [task ID]: crate::task::Id
127 /// [unstable]: crate#unstable-features
128 #[cfg(tokio_unstable)]
129 #[cfg_attr(docsrs, doc(cfg(tokio_unstable)))]
130 pub fn id(&self) -> Id {
131 self.id
132 }
133}
134
135impl fmt::Display for JoinError {
136 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
137 match &self.repr {
138 Repr::Cancelled => write!(fmt, "task {} was cancelled", self.id),
139 Repr::Panic(_) => write!(fmt, "task {} panicked", self.id),
140 }
141 }
142}
143
144impl fmt::Debug for JoinError {
145 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
146 match &self.repr {
147 Repr::Cancelled => write!(fmt, "JoinError::Cancelled({:?})", self.id),
148 Repr::Panic(_) => write!(fmt, "JoinError::Panic({:?}, ...)", self.id),
149 }
150 }
151}
152
153impl std::error::Error for JoinError {}
154
155impl From<JoinError> for io::Error {
156 fn from(src: JoinError) -> io::Error {
157 io::Error::new(
158 kind:io::ErrorKind::Other,
159 error:match src.repr {
160 Repr::Cancelled => "task was cancelled",
161 Repr::Panic(_) => "task panicked",
162 },
163 )
164 }
165}
166