use std::any::Any;
use std::error::Error;
use std::fmt::{self, Debug, Display};
use arty_executor::ShutdownError;
use performables::sync::mutex::Mutex;
pub struct JoinError {
kind: Kind,
}
enum Kind {
Panic(Mutex<Box<dyn Any + Send + 'static>>),
Shutdown(Option<ShutdownError>),
InvalidDestination,
}
impl JoinError {
#[must_use]
pub const fn is_panic(&self) -> bool {
matches!(&self.kind, Kind::Panic(_))
}
#[must_use]
pub const fn is_shutdown(&self) -> bool {
matches!(&self.kind, Kind::Shutdown(_))
}
#[must_use]
pub const fn is_invalid_destination(&self) -> bool {
matches!(&self.kind, Kind::InvalidDestination)
}
pub(crate) fn panicked(payload: Box<dyn Any + Send + 'static>) -> Self {
Self {
kind: Kind::Panic(Mutex::new(payload)),
}
}
pub(crate) const fn shutdown(error: Option<ShutdownError>) -> Self {
Self {
kind: Kind::Shutdown(error),
}
}
pub(crate) const fn invalid_destination() -> Self {
Self {
kind: Kind::InvalidDestination,
}
}
#[must_use]
pub fn into_panic(self) -> Option<Box<dyn Any + Send + 'static>> {
match self.kind {
Kind::Panic(payload) => Some(payload.into_inner()),
Kind::Shutdown(_) | Kind::InvalidDestination => None,
}
}
}
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())
.field("is_invalid_destination", &self.is_invalid_destination())
.finish()
}
}
impl Display for JoinError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match &self.kind {
Kind::Panic(_) => "task panicked",
Kind::Shutdown(_) => "runtime is shutting down",
Kind::InvalidDestination => "target thread is not a worker of this runtime",
})
}
}
impl Error for JoinError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match &self.kind {
Kind::Shutdown(Some(error)) => Some(error),
Kind::Panic(_) | Kind::Shutdown(None) | Kind::InvalidDestination => None,
}
}
}
#[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!(!panic.is_invalid_destination());
assert_eq!(panic.to_string(), "task panicked");
let shutdown = JoinError::shutdown(None);
assert!(!shutdown.is_panic());
assert!(shutdown.is_shutdown());
assert!(!shutdown.is_invalid_destination());
assert_eq!(shutdown.to_string(), "runtime is shutting down");
assert!(format!("{shutdown:?}").contains("is_shutdown: true"));
assert!(shutdown.source().is_none());
}
}