tyra 1.1.0

Typed Actor System
Documentation
use crate::message::actor_stop_message::ActorStopMessage;
use crate::prelude::{ActorContext, ActorPanicSource, ActorResult, SerializedMessage};
use log::error;
use std::error::Error;
use std::panic::UnwindSafe;

/// Core trait to define Actors
///
///
/// # Guaranteed Execution Order
///
/// 1.  [ActorFactory.new_actor](../prelude/trait.ActorFactory.html#tymethod.new_actor)
/// 2.  [pre_start](../prelude/trait.Actor.html#method.pre_start) only after first message has been sent
/// 3.  Start processing [Handler Implementations](../prelude/trait.Handler.html#tymethod.handle)
/// 4a. If ActorResult::Stop => Stops accepting new messages, but will continue to work through all existing messages in Mailbox before continuing to the next step
/// 4b. If ActorResult::Kill => Stops accepting new messages, skips all messages in Mailbox and will immediately continue with the next step
/// 5.  [post_stop](../prelude/trait.Actor.html#method.post_stop)
///
/// # Examples
///
/// Basic usage:
///
/// ```rust
/// use tyra::prelude::{TyraConfig, ActorSystem, ActorFactory, ActorContext, SerializedMessage, Handler, Actor};
///
/// #[derive(Actor)]
/// struct TestActor {}
/// ```
///
/// # Architecture
///
/// ## Actor Lifecycle
///
/// ```text
///        ┌──────────────────────────────────────────────────────────────────────────────────┐
///        │                                                                                  │
///        │                                        ┌───┐                                     │
///        │                                        │   │                                     │
///        │                              ┌─────────▼───┴──────────┐                          │
///        │       ┌──────────────────────┤                        │                          │
///        │       │                      │  Handler<M>::handle()  ◄───────────┐              │
///        │       │       ┌──────────────►                        │           │              │
///        │       │       │              └───────────┬────────────┤           │              │
///        │       │       │                          │            │           │              │
///        │       │       │                          │            │           │              │
///        │       │       │                          │            │           │              │
///        │       │       │                          │            │           │              │
///     ┌──▼───────▼───────┴──┐           ┌───────────▼──────────┐ │        ┌──┴──────────────┴──┐
/// ┌───►                     │           │                      │          │                    │
/// │   │  Actor::on_panic()  ├───────────►  Actor::post_stop()  ◄──────────┤  Actor::pre_start  │
/// └───┤                     │           │                      │          │                    │
///     └────────┬─▲──────────┘           └───────────▲──────────┘ │        └──▲──────────────┬──┘
///              │ │                                  │            │           │              │
///              │ │                                  │            │           │              │
///              │ │                                  │            │           │              │
///              │ │                                  │            │           │              │
///              │ │                   ┌──────────────┴────────────▼─┐         │              │
///              │ └───────────────────┤                             ├─────────┘              │
///              │                     │  ActorFactory::new_actor()  │                        │
///              └─────────────────────►                             ◄────────────────────────┘
///                                    └─────────────────────────────┘
///
///   ┌───┬────────────────────────────────┬────────────────────────┬────────────────────────┬───────────────────────────┬───────────────────────────┬───────────────────────────────┐
///   │   │                                │                        │                        │                           │                           │                               │
///   │ ┌─▼──────────────────────┐       ┌─▼──────────┐           ┌─▼────────────────┐     ┌─▼─────────────────┐       ┌─▼──────────────────┐      ┌─▼─────────────────────────┐  ┌──▼──────────────────┐
///   │ │                        │       │            │           │                  │     │                   │       │                    │      │                           │  │                     │
///   │ │  ActorResult::Restart  │       │  panic!()  │           │  Box<dyn Error>  │     │  ActorResult::Ok  │       │ ActorResult::Stop  │      │  ActorResult::Sleep(dur)  │  │  ActorResult::Kill  │
///   │ │                        │       │            │           │                  │     │                   │       │                    │      │                           │  │                     │
///   │ └─┬──────────────────────┘       └─┬──────────┘           └─┬────────────────┘     └─┬─────────────────┘       └─┬──────────────────┘      └─┬─────────────────────────┘  └──┬──────────────────┘
///   │   │                                │                        │                        │                           │                           │                               │
///   │   │                                │                        │                        │                           │                           │                               │
///   │ ┌─▼───────────────────────────┐  ┌─▼───────────────────┐  ┌─▼───────────────────┐  ┌─▼──────────────────────┐  ┌─▼──────────────────────┐  ┌─▼──────────────────────┐     ┌──▼───────────────────┐
///   │ │                             │  │                     │  │                     │  │                        │  │                        │  │                        │     │                      │
///   │ │  ActorFactory::new_actor()  │  │  Actor::on_panic()  │  │  Actor::on_error()  │  │  Handler<M>::handle()  │  │  Handler<M>::handle()  │  │  Handler<M>::handle()  │     │  Actor::post_stop()  │
///   │ │                             │  │                     │  │                     │  │                        │  │                        │  │                        │     │                      │
///   │ │  Actor::pre_start()         │  └─┬───────────────────┘  └─┬───────────────────┘  └─┬──────────────────────┘  └─┬──────────────────────┘  └─┬──────────────────────┘     └──▲───────────────────┘
///   │ │                             │    │                        │                        │                           │                           │                               │
///   │ └─┬───────────────────────────┘    │                        │                        │                           │                           │                               │
///   │   │                                │                        │                        │                         ┌─▼─────────────────┐         │                               │
///   │   │                                │                        │                        └─────────────────────────►                   ◄─────────┘                               │
///   │   │                                │                        │                                                  │  is_stopped &&    │                                         │
///   │   │                                │                        │                                                  │                   │                                         │
///   │   │                                │                        │                                                  │  mailbox.empty()  │                                         │
///   │   │                                │                        │                                                  │                   │                                         │
///   │   │                                │                        │                                                  │  false      true  │                                         │
///   │   │                                │                        │                                                  │                   │                                         │
///   │   │                                │                        │                                                  └──┬────────────┬───┘                                         │
///   │   │                                │                        │                                                     │            │                                             │
///   └─◄─┴──────────────────────────────◄─┴──────────────────────◄─┴─────────────────────────────────────────────────────┘            └─────────────────────────────────────────────┘
///
/// ```
pub trait Actor: Send + Sync + UnwindSafe + Sized {
    /// executed whenever Actor receives a [SerializedMessage](../prelude/struct.SerializedMessage.html)
    /// panic triggers `self.on_panic()` with `source = ActorPanicSource::Message`
    fn handle_serialized_message(
        &mut self,
        _msg: SerializedMessage,
        _context: &ActorContext<Self>,
    ) -> Result<ActorResult, Box<dyn Error>> {
        return Ok(ActorResult::Ok);
    }

    /// executed whenever a panic occurs within the actor
    ///
    /// determines actor behavior in case of a panic based on return value
    /// WARNING: returning `ActorResult::Restart` if `source == ActorPanicSource::Restart` can potentially result in an endless loop that will block the thread the actor is running on, until the restart was successful
    /// NOTE: if this function panics it will trigger a second time with `source == ActorPanicSource::OnPanic`, if another panic occurs in that case, the actor will be stopped immediately without finishing the mailbox!
    ///
    /// # Examples
    ///
    /// ```rust
    /// use tyra::prelude::*;
    /// use std::error::Error;
    /// use std::time::Duration;
    ///
    /// #[derive(ActorFactory)]
    /// struct TestActor {}
    /// impl TestActor {
    ///     pub fn new() -> Self {
    ///         Self {}
    ///     }
    /// }
    /// impl Actor for TestActor {
    ///     fn on_panic(&mut self, context: &ActorContext<Self>, source: ActorPanicSource) -> Result<ActorResult, Box<dyn Error>> {
    ///         context.system.stop(Duration::from_millis(5000));
    ///         return Ok(ActorResult::Kill);
    ///     }
    /// }
    ///
    /// impl Handler<ActorInitMessage> for TestActor {
    ///     fn handle(&mut self, _msg: ActorInitMessage, context: &ActorContext<Self>) -> Result<ActorResult, Box<dyn Error>> {
    ///         panic!("trigger on panic");
    ///     }
    /// }
    ///
    /// #[ntest::timeout(10000)]
    /// fn main() {
    ///     let actor_config = TyraConfig::new().unwrap();
    ///     let actor_system = ActorSystem::new(actor_config);
    ///     let actor = actor_system.builder().spawn("test", TestActor::factory()).unwrap();
    ///     actor.send(ActorInitMessage::new()).unwrap();
    ///     std::process::exit(actor_system.await_shutdown());
    /// }
    /// ```
    fn on_panic(
        &mut self,
        _context: &ActorContext<Self>,
        source: ActorPanicSource,
    ) -> Result<ActorResult, Box<dyn Error>> {
        return match source {
            ActorPanicSource::PreStart => Ok(ActorResult::Ok),
            ActorPanicSource::Message => Ok(ActorResult::Restart),
            ActorPanicSource::Restart => Ok(ActorResult::Ok),
            ActorPanicSource::OnPanic => Ok(ActorResult::Kill),
        };
    }

    /// executed whenever an error occurs within the actor
    ///
    /// determines actor behavior in case of an error based on return value
    ///
    /// # Examples
    ///
    /// ```rust
    /// use tyra::prelude::*;
    /// use std::error::Error;
    /// use std::time::Duration;
    /// use std::io::ErrorKind;
    ///
    /// #[derive(ActorFactory)]
    /// struct TestActor {}
    /// impl TestActor {
    ///     pub fn new() -> Self {
    ///         Self {}
    ///     }
    /// }
    /// impl Actor for TestActor {
    ///     fn on_error(&mut self, context: &ActorContext<Self>, err: Box<dyn Error>) -> ActorResult {
    ///         context.system.stop(Duration::from_millis(5000));
    ///         return ActorResult::Kill;
    ///     }
    /// }
    ///
    /// impl Handler<ActorInitMessage> for TestActor {
    ///     fn handle(&mut self, _msg: ActorInitMessage, context: &ActorContext<Self>) -> Result<ActorResult, Box<dyn Error>> {
    ///         let error = std::io::Error::from_raw_os_error(1337);
    ///         return Err(Box::new(error));
    ///     }
    /// }
    ///
    /// #[ntest::timeout(10000)]
    /// fn main() {
    ///     let actor_config = TyraConfig::new().unwrap();
    ///     let actor_system = ActorSystem::new(actor_config);
    ///     let actor = actor_system.builder().spawn("test", TestActor::factory()).unwrap();
    ///     actor.send(ActorInitMessage::new()).unwrap();
    ///     std::process::exit(actor_system.await_shutdown());
    /// }
    /// ```
    fn on_error(&mut self, _context: &ActorContext<Self>, err: Box<dyn Error>) -> ActorResult {
        error!("{:?}", err);
        return ActorResult::Ok;
    }

    /// executed before the first message is handled
    ///
    /// re-executed after actor restart before first message is handled
    /// panic triggers `self.on_panic()` with `source = ActorPanicSource::PreStart`
    ///
    /// # Examples
    ///
    /// ```rust
    /// use tyra::prelude::*;
    /// use std::error::Error;
    /// use std::time::Duration;
    ///
    /// #[derive(ActorFactory)]
    /// struct TestActor {}
    /// impl TestActor {
    ///     pub fn new() -> Self {
    ///         Self {}
    ///     }
    /// }
    /// impl Actor for TestActor {
    ///     fn pre_start(&mut self, context: &ActorContext<Self>) -> Result<ActorResult, Box<dyn Error>> {
    ///         context.system.stop(Duration::from_millis(5000));
    ///         return Ok(ActorResult::Kill);
    ///     }
    /// }
    ///
    /// impl Handler<ActorInitMessage> for TestActor {
    ///     fn handle(&mut self, _msg: ActorInitMessage, context: &ActorContext<Self>) -> Result<ActorResult, Box<dyn Error>> {
    ///         return Ok(ActorResult::Ok);
    ///     }
    /// }
    ///
    /// #[ntest::timeout(10000)]
    /// fn main() {
    ///     let actor_config = TyraConfig::new().unwrap();
    ///     let actor_system = ActorSystem::new(actor_config);
    ///     let actor = actor_system.builder().spawn("test", TestActor::factory()).unwrap();
    ///     actor.send(ActorInitMessage::new()).unwrap();
    ///     std::process::exit(actor_system.await_shutdown());
    /// }
    /// ```
    fn pre_start(&mut self, _context: &ActorContext<Self>) -> Result<ActorResult, Box<dyn Error>> {
        return Ok(ActorResult::Ok);
    }

    /// executed before the actor is restarted
    ///
    /// panic triggers `self.on_panic()` with `source = ActorPanicSource::Restart`
    ///
    /// # Examples
    ///
    /// ```rust
    /// use tyra::prelude::*;
    /// use std::error::Error;
    /// use std::time::Duration;
    ///
    /// #[derive(ActorFactory)]
    /// struct TestActor {}
    /// impl TestActor {
    ///     pub fn new() -> Self {
    ///         Self {}
    ///     }
    /// }
    /// impl Actor for TestActor {
    ///     fn pre_restart(&mut self, context: &ActorContext<Self>) {
    ///         context.system.stop(Duration::from_millis(5000));
    ///     }
    /// }
    ///
    /// impl Handler<ActorInitMessage> for TestActor {
    ///     fn handle(&mut self, _msg: ActorInitMessage, context: &ActorContext<Self>) -> Result<ActorResult, Box<dyn Error>> {
    ///         return Ok(ActorResult::Restart);
    ///     }
    /// }
    ///
    /// #[ntest::timeout(10000)]
    /// fn main() {
    ///     let actor_config = TyraConfig::new().unwrap();
    ///     let actor_system = ActorSystem::new(actor_config);
    ///     let actor = actor_system.builder().spawn("test", TestActor::factory()).unwrap();
    ///     actor.send(ActorInitMessage::new()).unwrap();
    ///     std::process::exit(actor_system.await_shutdown());
    /// }
    /// ```
    fn pre_restart(&mut self, _context: &ActorContext<Self>) {}

    /// executed after the last message is handled
    ///
    /// # Examples
    ///
    /// ```rust
    /// use tyra::prelude::*;
    /// use std::error::Error;
    /// use std::time::Duration;
    ///
    /// #[derive(ActorFactory)]
    /// struct TestActor {}
    /// impl TestActor {
    ///     pub fn new() -> Self {
    ///         Self {}
    ///     }
    /// }
    /// impl Actor for TestActor {
    ///     fn post_stop(&mut self, context: &ActorContext<Self>) {
    ///         context.system.stop(Duration::from_millis(5000));
    ///     }
    /// }
    ///
    /// impl Handler<ActorInitMessage> for TestActor {
    ///     fn handle(&mut self, _msg: ActorInitMessage, context: &ActorContext<Self>) -> Result<ActorResult, Box<dyn Error>> {
    ///         return Ok(ActorResult::Stop);
    ///     }
    /// }
    ///
    /// #[ntest::timeout(10000)]
    /// fn main() {
    ///     let actor_config = TyraConfig::new().unwrap();
    ///     let actor_system = ActorSystem::new(actor_config);
    ///     let actor = actor_system.builder().spawn("test", TestActor::factory()).unwrap();
    ///     actor.send(ActorInitMessage::new()).unwrap();
    ///     std::process::exit(actor_system.await_shutdown());
    /// }
    /// ```
    fn post_stop(&mut self, _context: &ActorContext<Self>) {}

    /// executed when Actor handles internal SystemStopMessage initiated by [ActorSystem.stop](../prelude/struct.ActorSystem.html#method.stop)
    ///
    /// Default behavior sends an `ActorStopMessage` to all actors which will trigger a clean shutdown
    /// panic triggers `self.on_panic()` with `source = ActorPanicSource::Message`
    ///
    /// # Examples
    ///
    /// ```rust
    /// use tyra::prelude::*;
    /// use std::error::Error;
    /// use std::time::Duration;
    ///
    /// #[derive(ActorFactory)]
    /// struct TestActor {}
    /// impl TestActor {
    ///     pub fn new() -> Self {
    ///         Self {}
    ///     }
    /// }
    /// impl Actor for TestActor {
    ///     fn on_system_stop(&mut self, context: &ActorContext<Self>) -> Result<ActorResult, Box<dyn Error>> {
    ///         context.system.stop_with_code(Duration::from_millis(1), 1337);
    ///         return Ok(ActorResult::Kill);
    ///     }
    /// }
    ///
    /// impl Handler<ActorInitMessage> for TestActor {
    ///     fn handle(&mut self, _msg: ActorInitMessage, context: &ActorContext<Self>) -> Result<ActorResult, Box<dyn Error>> {
    ///         context.system.stop(Duration::from_millis(5000));
    ///         return Ok(ActorResult::Ok);
    ///     }
    /// }
    ///
    /// #[ntest::timeout(10000)]
    /// fn main() {
    ///     let actor_config = TyraConfig::new().unwrap();
    ///     let actor_system = ActorSystem::new(actor_config);
    ///     let actor = actor_system.builder().spawn("test", TestActor::factory()).unwrap();
    ///     actor.send(ActorInitMessage::new()).unwrap();
    ///     let result = actor_system.await_shutdown();
    ///     assert_eq!(1337, result);
    /// }
    /// ```
    fn on_system_stop(
        &mut self,
        context: &ActorContext<Self>,
    ) -> Result<ActorResult, Box<dyn Error>> {
        let result = context.actor_ref.send(ActorStopMessage::new());
        if result.is_err() {
            error!(
                "Could not forward message ActorStopMessage to target {}",
                context.actor_ref.get_address().actor
            );
        }
        return Ok(ActorResult::Ok);
    }
}