1 | use std::any::Any; |
2 | use std::fmt; |
3 | use std::io; |
4 | |
5 | use super::Id; |
6 | use crate::util::SyncWrapper; |
7 | cfg_rt! { |
8 | /// Task failed to execute to completion. |
9 | pub struct JoinError { |
10 | repr: Repr, |
11 | id: Id, |
12 | } |
13 | } |
14 | |
15 | enum Repr { |
16 | Cancelled, |
17 | Panic(SyncWrapper<Box<dyn Any + Send + 'static>>), |
18 | } |
19 | |
20 | impl 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 | |
139 | impl 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 | |
148 | impl 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 | |
157 | impl std::error::Error for JoinError {} |
158 | |
159 | impl 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 | |