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 | 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 | |
135 | impl 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 | |
144 | impl 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 | |
153 | impl std::error::Error for JoinError {} |
154 | |
155 | impl 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 | |