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