1 use std::any::Any;
2 use std::fmt;
3 use std::io;
4 
5 use crate::util::SyncWrapper;
6 
7 cfg_rt! {
8     /// Task failed to execute to completion.
9     pub struct JoinError {
10         repr: Repr,
11     }
12 }
13 
14 enum Repr {
15     Cancelled,
16     Panic(SyncWrapper<Box<dyn Any + Send + 'static>>),
17 }
18 
19 impl JoinError {
cancelled() -> JoinError20     pub(crate) fn cancelled() -> JoinError {
21         JoinError {
22             repr: Repr::Cancelled,
23         }
24     }
25 
panic(err: Box<dyn Any + Send + 'static>) -> JoinError26     pub(crate) fn panic(err: Box<dyn Any + Send + 'static>) -> JoinError {
27         JoinError {
28             repr: Repr::Panic(SyncWrapper::new(err)),
29         }
30     }
31 
32     /// Returns true if the error was caused by the task being cancelled
is_cancelled(&self) -> bool33     pub fn is_cancelled(&self) -> bool {
34         matches!(&self.repr, Repr::Cancelled)
35     }
36 
37     /// Returns true if the error was caused by the task panicking
38     ///
39     /// # Examples
40     ///
41     /// ```
42     /// use std::panic;
43     ///
44     /// #[tokio::main]
45     /// async fn main() {
46     ///     let err = tokio::spawn(async {
47     ///         panic!("boom");
48     ///     }).await.unwrap_err();
49     ///
50     ///     assert!(err.is_panic());
51     /// }
52     /// ```
is_panic(&self) -> bool53     pub fn is_panic(&self) -> bool {
54         matches!(&self.repr, Repr::Panic(_))
55     }
56 
57     /// Consumes the join error, returning the object with which the task panicked.
58     ///
59     /// # Panics
60     ///
61     /// `into_panic()` panics if the `Error` does not represent the underlying
62     /// task terminating with a panic. Use `is_panic` to check the error reason
63     /// or `try_into_panic` for a variant that does not panic.
64     ///
65     /// # Examples
66     ///
67     /// ```should_panic
68     /// use std::panic;
69     ///
70     /// #[tokio::main]
71     /// async fn main() {
72     ///     let err = tokio::spawn(async {
73     ///         panic!("boom");
74     ///     }).await.unwrap_err();
75     ///
76     ///     if err.is_panic() {
77     ///         // Resume the panic on the main task
78     ///         panic::resume_unwind(err.into_panic());
79     ///     }
80     /// }
81     /// ```
into_panic(self) -> Box<dyn Any + Send + 'static>82     pub fn into_panic(self) -> Box<dyn Any + Send + 'static> {
83         self.try_into_panic()
84             .expect("`JoinError` reason is not a panic.")
85     }
86 
87     /// Consumes the join error, returning the object with which the task
88     /// panicked if the task terminated due to a panic. Otherwise, `self` is
89     /// returned.
90     ///
91     /// # Examples
92     ///
93     /// ```should_panic
94     /// use std::panic;
95     ///
96     /// #[tokio::main]
97     /// async fn main() {
98     ///     let err = tokio::spawn(async {
99     ///         panic!("boom");
100     ///     }).await.unwrap_err();
101     ///
102     ///     if let Ok(reason) = err.try_into_panic() {
103     ///         // Resume the panic on the main task
104     ///         panic::resume_unwind(reason);
105     ///     }
106     /// }
107     /// ```
try_into_panic(self) -> Result<Box<dyn Any + Send + 'static>, JoinError>108     pub fn try_into_panic(self) -> Result<Box<dyn Any + Send + 'static>, JoinError> {
109         match self.repr {
110             Repr::Panic(p) => Ok(p.into_inner()),
111             _ => Err(self),
112         }
113     }
114 }
115 
116 impl fmt::Display for JoinError {
fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result117     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
118         match &self.repr {
119             Repr::Cancelled => write!(fmt, "cancelled"),
120             Repr::Panic(_) => write!(fmt, "panic"),
121         }
122     }
123 }
124 
125 impl fmt::Debug for JoinError {
fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result126     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
127         match &self.repr {
128             Repr::Cancelled => write!(fmt, "JoinError::Cancelled"),
129             Repr::Panic(_) => write!(fmt, "JoinError::Panic(...)"),
130         }
131     }
132 }
133 
134 impl std::error::Error for JoinError {}
135 
136 impl From<JoinError> for io::Error {
from(src: JoinError) -> io::Error137     fn from(src: JoinError) -> io::Error {
138         io::Error::new(
139             io::ErrorKind::Other,
140             match src.repr {
141                 Repr::Cancelled => "task was cancelled",
142                 Repr::Panic(_) => "task panicked",
143             },
144         )
145     }
146 }
147