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