use std::any::Any;
use std::fmt;
use log::warn;
use crate::actor::SyncActor;
use crate::actor::{Actor, NewActor};
pub trait Supervisor<NA>
where
NA: NewActor,
{
fn decide(&mut self, error: <NA::Actor as Actor>::Error) -> SupervisorStrategy<NA::Argument>;
fn decide_on_restart_error(&mut self, error: NA::Error) -> SupervisorStrategy<NA::Argument>;
fn second_restart_error(&mut self, error: NA::Error);
fn decide_on_panic(
&mut self,
panic: Box<dyn Any + Send + 'static>,
) -> SupervisorStrategy<NA::Argument> {
drop(panic);
SupervisorStrategy::Stop
}
}
impl<F, NA> Supervisor<NA> for F
where
F: FnMut(<NA::Actor as Actor>::Error) -> SupervisorStrategy<NA::Argument>,
NA: NewActor<Error = !>,
{
fn decide(&mut self, err: <NA::Actor as Actor>::Error) -> SupervisorStrategy<NA::Argument> {
(self)(err)
}
fn decide_on_restart_error(&mut self, _: !) -> SupervisorStrategy<NA::Argument> {
SupervisorStrategy::Stop
}
fn second_restart_error(&mut self, _: !) {
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum SupervisorStrategy<Arg> {
Restart(Arg),
Stop,
}
pub trait SyncSupervisor<A>
where
A: SyncActor,
{
fn decide(&mut self, error: A::Error) -> SupervisorStrategy<A::Argument>;
}
impl<F, A> SyncSupervisor<A> for F
where
F: FnMut(A::Error) -> SupervisorStrategy<A::Argument>,
A: SyncActor,
{
fn decide(&mut self, err: A::Error) -> SupervisorStrategy<A::Argument> {
(self)(err)
}
}
#[derive(Copy, Clone, Debug)]
pub struct NoSupervisor;
impl<NA> Supervisor<NA> for NoSupervisor
where
NA: NewActor<Error = !>,
NA::Actor: Actor<Error = !>,
{
fn decide(&mut self, _: !) -> SupervisorStrategy<NA::Argument> {
SupervisorStrategy::Stop
}
fn decide_on_restart_error(&mut self, _: !) -> SupervisorStrategy<NA::Argument> {
SupervisorStrategy::Stop
}
fn second_restart_error(&mut self, _: !) {
}
}
impl<A> SyncSupervisor<A> for NoSupervisor
where
A: SyncActor<Error = !>,
{
fn decide(&mut self, _: !) -> SupervisorStrategy<A::Argument> {
SupervisorStrategy::Stop
}
}
#[derive(Copy, Clone, Debug)]
pub struct StopSupervisor(&'static str);
impl StopSupervisor {
pub const fn for_actor(actor_name: &'static str) -> StopSupervisor {
StopSupervisor(actor_name)
}
}
impl<NA> Supervisor<NA> for StopSupervisor
where
NA: NewActor,
NA::Error: std::fmt::Display,
<NA::Actor as Actor>::Error: fmt::Display,
{
fn decide(&mut self, err: <NA::Actor as Actor>::Error) -> SupervisorStrategy<NA::Argument> {
warn!("{} failed, stopping it: {}", self.0, err);
SupervisorStrategy::Stop
}
fn decide_on_restart_error(&mut self, err: NA::Error) -> SupervisorStrategy<NA::Argument> {
warn!("{} failed to restart, stopping it: {}", self.0, err);
SupervisorStrategy::Stop
}
fn second_restart_error(&mut self, err: NA::Error) {
warn!(
"{} failed to restart a second time, stopping it: {}",
self.0, err
);
}
}
impl<A> SyncSupervisor<A> for StopSupervisor
where
A: SyncActor,
A::Error: std::fmt::Display,
{
fn decide(&mut self, err: A::Error) -> SupervisorStrategy<A::Argument> {
warn!("{} failed, stopping it: {}", self.0, err);
SupervisorStrategy::Stop
}
}
#[macro_export]
macro_rules! restart_supervisor {
($vis: vis $supervisor_name: ident, $actor_name: expr $(,)*) => {
$crate::__heph_restart_supervisor_impl!($vis $supervisor_name, $actor_name, (), 5, std::time::Duration::from_secs(5), "",);
};
($vis: vis $supervisor_name: ident, $actor_name: expr, () $(,)*) => {
$crate::__heph_restart_supervisor_impl!($vis $supervisor_name, $actor_name, (), 5, std::time::Duration::from_secs(5), "",);
};
($vis: vis $supervisor_name: ident, $actor_name: expr, ( $( $arg: ty),* ) $(,)*) => {
$crate::__heph_restart_supervisor_impl!($vis $supervisor_name, $actor_name, ( $( $arg ),* ), 5, std::time::Duration::from_secs(5), "",);
};
($vis: vis $supervisor_name: ident, $actor_name: expr, $arg: ty $(,)*) => {
$crate::__heph_restart_supervisor_impl!($vis $supervisor_name, $actor_name, ( $arg ), 5, std::time::Duration::from_secs(5), "",);
};
($vis: vis $supervisor_name: ident, $actor_name: expr, (), $max_restarts: expr, $max_duration: expr $(,)*) => {
$crate::__heph_restart_supervisor_impl!($vis $supervisor_name, $actor_name, (), $max_restarts, $max_duration, "",);
};
($vis: vis $supervisor_name: ident, $actor_name: expr, ( $( $arg: ty ),* ), $max_restarts: expr, $max_duration: expr $(,)*) => {
$crate::__heph_restart_supervisor_impl!($vis $supervisor_name, $actor_name, ( $( $arg ),* ), $max_restarts, $max_duration, "",);
};
($vis: vis $supervisor_name: ident, $actor_name: expr, $arg: ty, $max_restarts: expr, $max_duration: expr $(,)*) => {
$crate::__heph_restart_supervisor_impl!($vis $supervisor_name, $actor_name, ( $arg ), $max_restarts, $max_duration, "",);
};
($vis: vis $supervisor_name: ident, $actor_name: expr, (), $max_restarts: expr, $max_duration: expr, $log_extra: expr, $( args $(. $log_arg_field: tt )* ),* $(,)*) => {
$crate::__heph_restart_supervisor_impl!($vis $supervisor_name, $actor_name, (), $max_restarts, $max_duration, $log_extra, $( args $(. $log_arg_field )* ),*);
};
($vis: vis $supervisor_name: ident, $actor_name: expr, ( $( $arg: ty ),* ), $max_restarts: expr, $max_duration: expr, $log_extra: expr, $( args $(. $log_arg_field: tt )* ),* $(,)*) => {
$crate::__heph_restart_supervisor_impl!($vis $supervisor_name, $actor_name, ( $( $arg ),* ), $max_restarts, $max_duration, $log_extra, $( args $(. $log_arg_field )* ),*);
};
($vis: vis $supervisor_name: ident, $actor_name: expr, $arg: ty, $max_restarts: expr, $max_duration: expr, $log_extra: expr, $( args $(. $log_arg_field: tt )* ),* $(,)*) => {
$crate::__heph_restart_supervisor_impl!($vis $supervisor_name, $actor_name, ( $arg ), $max_restarts, $max_duration, $log_extra, $( args $(. $log_arg_field )* ),*);
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __heph_restart_supervisor_impl {
(
$vis: vis
$supervisor_name: ident,
$actor_name: expr,
( $( $arg: ty ),* ),
$max_restarts: expr,
$max_duration: expr,
$log_extra: expr,
$( args $(. $log_arg_field: tt )* ),*
$(,)*
) => {
$crate::__heph_doc!(
std::concat!(
"Supervisor for ", $actor_name, ".\n\n",
"Maximum number of restarts: `", stringify!($max_restarts), "`, ",
"within a duration of: `", stringify!($max_duration), "`.",
),
#[derive(Debug)]
#[allow(unused_qualifications)]
$vis struct $supervisor_name {
restarts_left: std::primitive::usize,
last_restart: std::option::Option<std::time::Instant>,
args: ( $( $arg ),* ),
}
);
#[allow(unused_qualifications)]
impl $supervisor_name {
$vis const MAX_RESTARTS: std::primitive::usize = $max_restarts;
$vis const MAX_DURATION: std::time::Duration = $max_duration;
$crate::__heph_restart_supervisor_impl!(impl_new $vis $supervisor_name, ( $( $arg ),* ));
}
impl<NA> $crate::supervisor::Supervisor<NA> for $supervisor_name
where
NA: $crate::NewActor<Argument = ( $( $arg ),* )>,
NA::Error: std::fmt::Display,
<NA::Actor as $crate::Actor>::Error: std::fmt::Display,
{
fn decide(&mut self, err: <NA::Actor as $crate::Actor>::Error) -> $crate::SupervisorStrategy<NA::Argument> {
$crate::__heph_restart_supervisor_impl!{decide_impl self, err, $actor_name, $max_restarts, $log_extra, $( args $(. $log_arg_field )* ),*}
}
fn decide_on_restart_error(&mut self, err: NA::Error) -> $crate::SupervisorStrategy<NA::Argument> {
self.last_restart = Some(std::time::Instant::now());
if self.restarts_left >= 1 {
self.restarts_left -= 1;
::log::warn!(
std::concat!($actor_name, " actor failed to restart, trying again ({}/{} restarts left): {}", $log_extra),
self.restarts_left, $max_restarts, err, $( self.args $(. $log_arg_field )* ),*
);
$crate::SupervisorStrategy::Restart(self.args.clone())
} else {
::log::warn!(
std::concat!($actor_name, " actor failed to restart, stopping it (no restarts left): {}", $log_extra),
err, $( self.args $(. $log_arg_field )* ),*
);
$crate::SupervisorStrategy::Stop
}
}
fn second_restart_error(&mut self, err: NA::Error) {
::log::warn!(
std::concat!($actor_name, " actor failed to restart a second time, stopping it: {}", $log_extra),
err, $( self.args $(. $log_arg_field )* ),*
);
}
}
impl<A> $crate::supervisor::SyncSupervisor<A> for $supervisor_name
where
A: $crate::actor::SyncActor<Argument = ( $( $arg ),* )>,
A::Error: std::fmt::Display,
{
fn decide(&mut self, err: A::Error) -> $crate::SupervisorStrategy<A::Argument> {
$crate::__heph_restart_supervisor_impl!{decide_impl self, err, $actor_name, $max_restarts, $log_extra, $( args $(. $log_arg_field )* ),*}
}
}
};
(
decide_impl
$self: ident,
$err: ident,
$actor_name: expr,
$max_restarts: expr,
$log_extra: expr,
$( args $(. $log_arg_field: tt )* ),*
$(,)*
) => {
let now = std::time::Instant::now();
let last_restart = $self.last_restart.replace(now);
if let Some(last_restart) = last_restart {
let duration_since_last_crash = now - last_restart;
if duration_since_last_crash > Self::MAX_DURATION {
$self.restarts_left = Self::MAX_RESTARTS;
}
}
if $self.restarts_left >= 1 {
$self.restarts_left -= 1;
::log::warn!(
std::concat!($actor_name, " failed, restarting it ({}/{} restarts left): {}", $log_extra),
$self.restarts_left, $max_restarts, $err, $( $self.args $(. $log_arg_field )* ),*
);
$crate::SupervisorStrategy::Restart($self.args.clone())
} else {
::log::warn!(
std::concat!($actor_name, " failed, stopping it (no restarts left): {}", $log_extra),
$err, $( $self.args $(. $log_arg_field )* ),*
);
$crate::SupervisorStrategy::Stop
}
};
(impl_new $vis: vis $supervisor_name: ident, ()) => {
$crate::__heph_doc!(
std::concat!("Create a new `", stringify!($supervisor_name), "`."),
#[allow(dead_code)]
$vis const fn new() -> $supervisor_name {
$supervisor_name {
restarts_left: Self::MAX_RESTARTS,
last_restart: None,
args: (),
}
}
);
};
(impl_new $vis: vis $supervisor_name: ident, ( $arg: ty )) => {
$crate::__heph_doc!(
std::concat!("Create a new `", stringify!($supervisor_name), "`."),
#[allow(dead_code)]
$vis const fn new(arg: $arg) -> $supervisor_name {
$supervisor_name {
restarts_left: Self::MAX_RESTARTS,
last_restart: None,
args: (arg),
}
}
);
};
(impl_new $vis: vis $supervisor_name: ident, ($arg0: ty, $arg1: ty)) => {
$crate::__heph_doc!(
std::concat!("Create a new `", stringify!($supervisor_name), "`."),
#[allow(dead_code)]
$vis const fn new(arg0: $arg0, arg1: $arg1) -> $supervisor_name {
$supervisor_name {
restarts_left: Self::MAX_RESTARTS,
last_restart: None,
args: (arg0, arg1),
}
}
);
};
(impl_new $vis: vis $supervisor_name: ident, ($arg0: ty, $arg1: ty, $arg2: ty)) => {
$crate::__heph_doc!(
std::concat!("Create a new `", stringify!($supervisor_name), "`."),
#[allow(dead_code)]
$vis const fn new(arg0: $arg0, arg1: $arg1, arg2: $arg2) -> $supervisor_name {
$supervisor_name {
restarts_left: Self::MAX_RESTARTS,
last_restart: None,
args: (arg0, arg1, arg2),
}
}
);
};
(impl_new $vis: vis $supervisor_name: ident, ($arg0: ty, $arg1: ty, $arg2: ty, $arg3: ty)) => {
$crate::__heph_doc!(
std::concat!("Create a new `", stringify!($supervisor_name), "`."),
#[allow(dead_code)]
$vis const fn new(arg0: $arg0, arg1: $arg1, arg2: $arg2, arg3: $arg3) -> $supervisor_name {
$supervisor_name {
restarts_left: Self::MAX_RESTARTS,
last_restart: None,
args: (arg0, arg1, arg2, arg3),
}
}
);
};
(impl_new $vis: vis $supervisor_name: ident, ($arg0: ty, $arg1: ty, $arg2: ty, $arg3: ty, $arg4: ty)) => {
$crate::__heph_doc!(
std::concat!("Create a new `", stringify!($supervisor_name), "`."),
#[allow(dead_code)]
$vis const fn new(arg0: $arg0, arg1: $arg1, arg2: $arg2, arg3: $arg3, arg4: $arg4) -> $supervisor_name {
$supervisor_name {
restarts_left: Self::MAX_RESTARTS,
last_restart: None,
args: (arg0, arg1, arg2, arg3, arg4),
}
}
);
};
(impl_new $vis: vis $supervisor_name: ident, ($arg0: ty, $arg1: ty, $arg2: ty, $arg3: ty, $arg4: ty, $arg5: ty)) => {
$crate::__heph_doc!(
std::concat!("Create a new `", stringify!($supervisor_name), "`."),
#[allow(dead_code)]
$vis const fn new(arg0: $arg0, arg1: $arg1, arg2: $arg2, arg3: $arg3, arg4: $arg4, arg5: $arg5) -> $supervisor_name {
$supervisor_name {
restarts_left: Self::MAX_RESTARTS,
last_restart: None,
args: (arg0, arg1, arg2, arg3, arg4, arg5),
}
}
);
};
(impl_new $vis: vis $supervisor_name: ident, ( $( $arg: ty ),* )) => {
$crate::__heph_doc!(
std::concat!("Create a new `", stringify!($supervisor_name), "`."),
#[allow(dead_code)]
$vis const fn new(args: ( $( $arg ),* )) -> $supervisor_name {
$supervisor_name {
restarts_left: Self::MAX_RESTARTS,
last_restart: None,
args,
}
}
);
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __heph_doc {
($doc: expr, $( $tt: tt )*) => {
#[doc = $doc]
$($tt)*
};
}