pub use crate::storage::StorageError;
use cellular_raza_concepts::*;
pub use cellular_raza_concepts::{DecomposeError, IndexError};
use core::any::type_name;
use core::fmt::{Debug, Display};
use std::sync::Arc;
use std::sync::atomic::AtomicBool;
use crossbeam_channel::{RecvError, SendError};
macro_rules! impl_error_variant {
($name: ident, $(
$(#[$($tt:tt)*])?
$err_var: ident
),+) => {
impl Display for $name {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
$(
$(#[$($tt)*])?
$name::$err_var(message) => write!(f, "{}", message),
)+
}
}
}
}
}
macro_rules! impl_from_error {
($name: ident,
$($(#[$($tt:tt)*])?
($err_var: ident, $err_type: ty)
),+) => {
$(
// Implement conversion from error to errorvariant
$(#[$($tt)*])?
impl From<$err_type> for $name {
fn from(err: $err_type) -> Self {
$name::$err_var(err)
}
}
)+
}
}
#[derive(Debug)]
pub enum SimulationError {
CalcError(CalcError),
DecomposeError(DecomposeError),
TimeError(TimeError),
ControllerError(ControllerError),
DivisionError(DivisionError),
DeathError(DeathError),
BoundaryError(BoundaryError),
DrawingError(DrawingError),
IndexError(IndexError),
SendError(String),
ReceiveError(RecvError),
StorageError(StorageError),
IoError(std::io::Error),
RngError(RngError),
OtherThreadError(String),
#[cfg(feature = "pyo3")]
Pyo3Error(pyo3::PyErr),
}
impl_from_error! {SimulationError,
(ReceiveError, RecvError),
(CalcError, CalcError),
(DecomposeError, DecomposeError),
(TimeError, TimeError),
(ControllerError, ControllerError),
(DivisionError, DivisionError),
(DeathError, DeathError),
(BoundaryError, BoundaryError),
(IndexError, IndexError),
(IoError, std::io::Error),
(DrawingError, DrawingError),
(StorageError, StorageError),
(RngError, RngError),
#[cfg(feature = "pyo3")]
(Pyo3Error, pyo3::PyErr)
}
impl_error_variant! {SimulationError,
SendError,
ReceiveError,
CalcError,
DecomposeError,
TimeError,
ControllerError,
DivisionError,
DeathError,
BoundaryError,
IndexError,
IoError,
DrawingError,
StorageError,
RngError,
OtherThreadError,
#[cfg(feature = "pyo3")]
Pyo3Error
}
impl std::error::Error for SimulationError {}
impl<T> From<SendError<T>> for SimulationError {
fn from(_err: SendError<T>) -> Self {
SimulationError::SendError(format!(
"Error receiving object of type {}",
type_name::<SendError<T>>()
))
}
}
pub enum HandlingStrategy {
RevertChangeAccuracy,
Abort,
Ignore,
}
pub trait ErrorHandler: Sized {
fn spawn_multiple(n_threads: usize) -> impl IntoIterator<Item = Self>;
fn handle_event(&mut self, value: Result<(), SimulationError>);
fn determine_strategy(&self, error: SimulationError) -> HandlingStrategy {
use HandlingStrategy::*;
use SimulationError::*;
match error {
SendError(_) => Abort,
ReceiveError(_) => RevertChangeAccuracy,
CalcError(_) => RevertChangeAccuracy,
DecomposeError(_) => Abort,
TimeError(_) => Abort,
ControllerError(_) => Abort,
DivisionError(_) => RevertChangeAccuracy,
DeathError(_) => RevertChangeAccuracy,
BoundaryError(_) => Abort,
IndexError(_) => RevertChangeAccuracy,
IoError(_) => Ignore,
DrawingError(_) => Ignore,
StorageError(_) => Ignore,
RngError(_) => Abort,
OtherThreadError(_) => Abort,
#[cfg(feature = "pyo3")]
Pyo3Error(_) => Abort,
}
}
fn do_continue(&mut self) -> Result<(), SimulationError>;
}
#[allow(unused)]
pub struct DefaultHandler {
thread: usize,
errors: Vec<SimulationError>,
do_continue: Arc<AtomicBool>,
}
impl ErrorHandler for DefaultHandler {
fn spawn_multiple(n_threads: usize) -> impl IntoIterator<Item = Self> {
let do_continue = Arc::new(AtomicBool::new(true));
(0..n_threads).map(move |thread| Self {
thread,
errors: vec![],
do_continue: Arc::clone(&do_continue),
})
}
fn handle_event(&mut self, error: Result<(), SimulationError>) {
match error {
Ok(_) => (),
Err(e) => self.errors.push(e),
}
}
fn do_continue(&mut self) -> Result<(), SimulationError> {
self.errors
.iter()
.for_each(|e| println!("thread {:2} detected error: {}", self.thread, e));
match self.errors.drain(..).next() {
Some(e) => Err(e),
None => Ok(()),
}
}
}
#[cfg(feature = "pyo3")]
impl From<SimulationError> for pyo3::PyErr {
fn from(value: SimulationError) -> Self {
use SimulationError::*;
use pyo3::exceptions::*;
match value {
CalcError(e) => pyo3::PyErr::new::<PyValueError, _>(format!("cr_err: {e:?}")),
DecomposeError(e) => pyo3::PyErr::new::<PyValueError, _>(format!("cr_err: {e:?}")),
TimeError(e) => pyo3::PyErr::new::<PyValueError, _>(format!("cr_err: {e:?}")),
ControllerError(e) => pyo3::PyErr::new::<PyValueError, _>(format!("cr_err: {e:?}")),
DivisionError(e) => pyo3::PyErr::new::<PyValueError, _>(format!("cr_err: {e:?}")),
DeathError(e) => pyo3::PyErr::new::<PyValueError, _>(format!("cr_err: {e:?}")),
BoundaryError(e) => pyo3::PyErr::new::<PyValueError, _>(format!("cr_err: {e:?}")),
DrawingError(e) => pyo3::PyErr::new::<PyValueError, _>(format!("cr_err: {e:?}")),
IndexError(e) => pyo3::PyErr::new::<PyValueError, _>(format!("cr_err: {e:?}")),
SendError(e) => pyo3::PyErr::new::<PyValueError, _>(format!("cr_err: {e:?}")),
ReceiveError(e) => pyo3::PyErr::new::<PyValueError, _>(format!("cr_err: {e:?}")),
StorageError(e) => pyo3::PyErr::new::<PyValueError, _>(format!("cr_err: {e:?}")),
RngError(e) => pyo3::PyErr::new::<PyValueError, _>(format!("cr_err: {e:?}")),
IoError(e) => pyo3::PyErr::from(e),
OtherThreadError(e) => pyo3::PyErr::new::<PyValueError, _>(format!("cr_err: {e:?}")),
#[cfg(feature = "pyo3")]
Pyo3Error(e) => e,
}
}
}