use std::any::Any;
use std::error::Error;
use std::fmt::{self, Debug, Display};
use performables::sync::mutex::Mutex;
pub struct JoinError {
panic: Option<Mutex<Box<dyn Any + Send + 'static>>>,
}
impl JoinError {
#[must_use]
pub const fn is_panic(&self) -> bool {
self.panic.is_some()
}
#[must_use]
pub const fn is_shutdown(&self) -> bool {
self.panic.is_none()
}
pub(crate) fn panicked(payload: Box<dyn Any + Send + 'static>) -> Self {
Self {
panic: Some(Mutex::new(payload)),
}
}
pub(crate) const fn shutdown() -> Self {
Self { panic: None }
}
#[must_use]
pub fn into_panic(self) -> Option<Box<dyn Any + Send + 'static>> {
self.panic.map(Mutex::into_inner)
}
}
impl JoinError {
pub(crate) fn from_executor(error: arty_executor::JoinError) -> Self {
match error.into_panic() {
Some(payload) => Self::panicked(payload),
None => Self::shutdown(),
}
}
}
impl Debug for JoinError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct(std::any::type_name::<Self>())
.field("is_panic", &self.is_panic())
.field("is_shutdown", &self.is_shutdown())
.finish()
}
}
impl Display for JoinError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(if self.is_panic() {
"task panicked"
} else {
"runtime is shutting down"
})
}
}
impl Error for JoinError {}
#[cfg(test)]
#[cfg_attr(coverage_nightly, coverage(off))]
mod tests {
use super::*;
#[test]
fn failure_kinds_are_distinct_and_thread_safe() {
static_assertions::assert_impl_all!(JoinError: Error, Send, Sync);
let panic = JoinError::panicked(Box::new(std::cell::Cell::new(42)));
assert!(panic.is_panic());
assert!(!panic.is_shutdown());
assert_eq!(panic.to_string(), "task panicked");
let shutdown = JoinError::shutdown();
assert!(!shutdown.is_panic());
assert!(shutdown.is_shutdown());
assert_eq!(shutdown.to_string(), "runtime is shutting down");
assert!(format!("{shutdown:?}").contains("is_shutdown: true"));
assert!(shutdown.source().is_none());
}
}