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 { cancelled(id: Id) -> JoinError21 pub(crate) fn cancelled(id: Id) -> JoinError { 22 JoinError { 23 repr: Repr::Cancelled, 24 id, 25 } 26 } 27 panic(id: Id, err: Box<dyn Any + Send + 'static>) -> JoinError28 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. is_cancelled(&self) -> bool36 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 /// ``` is_panic(&self) -> bool56 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] into_panic(self) -> Box<dyn Any + Send + 'static>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 /// ``` try_into_panic(self) -> Result<Box<dyn Any + Send + 'static>, JoinError>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)))] id(&self) -> Id130 pub fn id(&self) -> Id { 131 self.id 132 } 133 } 134 135 impl fmt::Display for JoinError { fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result136 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 { fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result145 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 { from(src: JoinError) -> io::Error156 fn from(src: JoinError) -> io::Error { 157 io::Error::new( 158 io::ErrorKind::Other, 159 match src.repr { 160 Repr::Cancelled => "task was cancelled", 161 Repr::Panic(_) => "task panicked", 162 }, 163 ) 164 } 165 } 166