diff --git a/examples/supervisor_clear_interval.rs b/examples/supervisor_clear_interval.rs index 3c6520e..3880c0b 100644 --- a/examples/supervisor_clear_interval.rs +++ b/examples/supervisor_clear_interval.rs @@ -1,30 +1,20 @@ -use std::time::{Duration, Instant}; +use std::time::Duration; use xactor::{message, Actor, Context, Handler}; #[derive(Debug)] -pub struct PingTimer { - last_ping: Instant, -} - -impl Default for PingTimer { - fn default() -> Self { - PingTimer { - last_ping: Instant::now(), - } - } -} +pub struct PingTimer; #[async_trait::async_trait] impl Actor for PingTimer { async fn started(&mut self, ctx: &mut Context) -> xactor::Result<()> { - println!("PingTimer:: started()"); - ctx.send_interval(Ping, Duration::from_millis(1000)); + println!("PingTimer :: started()"); + ctx.send_interval(Ping, Duration::from_millis(300)); Ok(()) } /// Called after an actor is stopped. async fn stopped(&mut self, _: &mut Context) { - println!("PingTimer:: stopped()"); + println!("PingTimer :: stopped()"); } } @@ -34,22 +24,29 @@ struct Ping; #[async_trait::async_trait] impl Handler for PingTimer { - async fn handle(&mut self, ctx: &mut Context, _msg: Ping) { - let now = Instant::now(); - let delta = (now - self.last_ping).as_millis(); - self.last_ping = now; - println!("PingTimer:: Ping {} {:?}", ctx.actor_id(), delta); + async fn handle(&mut self, _: &mut Context, _msg: Ping) { + println!("PingTimer :: Ping"); } } #[message] -struct Halt; +struct Restart; #[async_trait::async_trait] -impl Handler for PingTimer { - async fn handle(&mut self, ctx: &mut Context, _msg: Halt) { - println!("PingTimer:: received Halt"); +impl Handler for PingTimer { + async fn handle(&mut self, ctx: &mut Context, _msg: Restart) { + println!("PingTimer :: received restart"); ctx.stop(None); - println!("PingTimer:: stopped"); + } +} + +#[message] +struct Shutdown; + +#[async_trait::async_trait] +impl Handler for PingTimer { + async fn handle(&mut self, ctx: &mut Context, _msg: Shutdown) { + println!("PingTimer :: received Shutdown"); + ctx.stop_supervisor(None); } } @@ -59,29 +56,48 @@ struct Panic; #[async_trait::async_trait] impl Handler for PingTimer { async fn handle(&mut self, _: &mut Context, _msg: Panic) { - println!("PingTimer:: received Panic"); - panic!("intentional panic"); + println!("PingTimer :: received Panic"); + panic!("intentional panic: this should not occur"); } } #[xactor::main] async fn main() -> Result<(), Box> { - let service_supervisor = xactor::Supervisor::start(PingTimer::default).await?; + let service_supervisor = xactor::Supervisor::start(|| PingTimer).await?; let service_addr = service_supervisor.clone(); + let service_addr2 = service_supervisor.clone(); let supervisor_task = xactor::spawn(async { service_supervisor.wait_for_stop().await; }); - let send_halt = async { - xactor::sleep(Duration::from_millis(5_200)).await; - println!(" main :: sending Halt"); - service_addr.send(Halt).unwrap(); + let stop_actor = async { + xactor::sleep(Duration::from_millis(2_000)).await; + println!(" main :: sending Restart"); + service_addr.send(Restart).unwrap(); + }; + + let stop_supervisor = async move { + xactor::sleep(Duration::from_millis(3_000)).await; + println!(" main :: sending Shutdown"); + service_addr2.send(Shutdown).unwrap(); + }; + + let send_panic = async { + xactor::sleep(Duration::from_millis(5_000)).await; + println!(" main :: sending Panic after stop"); + if let Err(error) = service_addr.send(Panic) { + println!(" ok :: cannot send after halting, this is very much expected"); + println!(" Failing with \"{}\"", error); + } }; - let _ = futures::join!(supervisor_task, send_halt); - // run this to see that the interval is not properly stopped if the ctx is stopped - // futures::join!(supervisor_task, send_panic); // there is no panic recovery + futures::join!( + supervisor_task, + stop_actor, + stop_supervisor, + send_panic, // there is no panic recovery + ); Ok(()) } diff --git a/src/actor.rs b/src/actor.rs index 1e7b46c..5d17932 100644 --- a/src/actor.rs +++ b/src/actor.rs @@ -1,10 +1,7 @@ -use crate::addr::ActorEvent; -use crate::runtime::spawn; -use crate::{Addr, Context}; use crate::error::Result; -use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender}; -use futures::channel::oneshot; -use futures::{FutureExt, StreamExt}; +use crate::{Addr, Context}; + +use crate::lifecycle::LifeCycle; /// Represents a message that can be handled by the actor. pub trait Message: 'static + Send { @@ -109,75 +106,6 @@ pub trait Actor: Sized + Send + 'static { /// } /// ``` async fn start(self) -> Result> { - ActorManager::new().start_actor(self).await - } -} - -pub(crate) struct ActorManager { - ctx: Context, - tx: std::sync::Arc>>, - rx: UnboundedReceiver>, - tx_exit: oneshot::Sender<()>, -} - -impl ActorManager { - pub(crate) fn new() -> Self { - let (tx_exit, rx_exit) = oneshot::channel(); - let rx_exit = rx_exit.shared(); - let (ctx, rx, tx) = Context::new(Some(rx_exit)); - Self { - ctx, - rx, - tx, - tx_exit, - } - } - - pub(crate) fn address(&self) -> Addr { - self.ctx.address() - } - - pub(crate) async fn start_actor(self, mut actor: A) -> Result> { - let Self { - mut ctx, - mut rx, - tx, - tx_exit, - } = self; - - let rx_exit = ctx.rx_exit.clone(); - let actor_id = ctx.actor_id(); - - // Call started - actor.started(&mut ctx).await?; - - spawn({ - async move { - while let Some(event) = rx.next().await { - match event { - ActorEvent::Exec(f) => f(&mut actor, &mut ctx).await, - ActorEvent::Stop(_err) => break, - ActorEvent::RemoveStream(id) => { - if ctx.streams.contains(id) { - ctx.streams.remove(id); - } - } - } - } - - actor.stopped(&mut ctx).await; - - ctx.abort_streams(); - ctx.abort_intervals(); - - tx_exit.send(()).ok(); - } - }); - - Ok(Addr { - actor_id, - tx, - rx_exit, - }) + LifeCycle::new().start_actor(self).await } } diff --git a/src/addr.rs b/src/addr.rs index a81c249..cbf0d37 100644 --- a/src/addr.rs +++ b/src/addr.rs @@ -14,6 +14,7 @@ pub(crate) type ExecFn = pub(crate) enum ActorEvent { Exec(ExecFn), Stop(Option), + StopSupervisor(Option), RemoveStream(usize), } @@ -71,6 +72,14 @@ impl Addr { Ok(()) } + /// Stop the supervisor. + /// + /// this is ignored by normal actors + pub fn stop_supervisor(&mut self, err: Option) -> Result<()> { + mpsc::UnboundedSender::clone(&*self.tx).start_send(ActorEvent::StopSupervisor(err))?; + Ok(()) + } + /// Send a message `msg` to the actor and wait for the return value. pub async fn call(&self, msg: T) -> Result where diff --git a/src/context.rs b/src/context.rs index fa30be5..8f05dfc 100644 --- a/src/context.rs +++ b/src/context.rs @@ -75,6 +75,17 @@ impl Context { } } + /// Stop the supervisor. + /// + /// this is ignored by normal actors + pub fn stop_supervisor(&self, err: Option) { + if let Some(tx) = self.tx.upgrade() { + mpsc::UnboundedSender::clone(&*tx) + .start_send(ActorEvent::StopSupervisor(err)) + .ok(); + } + } + pub fn abort_intervals(&mut self) { for handle in self.intervals.drain() { handle.abort() diff --git a/src/lib.rs b/src/lib.rs index 26343e7..5d7d7d3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -67,6 +67,7 @@ mod addr; mod broker; mod caller; mod context; +mod lifecycle; mod runtime; mod service; mod supervisor; diff --git a/src/lifecycle.rs b/src/lifecycle.rs new file mode 100644 index 0000000..ee9d8ab --- /dev/null +++ b/src/lifecycle.rs @@ -0,0 +1,134 @@ +use crate::{addr::ActorEvent, error::Result, runtime::spawn, Actor, Addr, Context}; +use futures::{ + channel::{ + mpsc::{UnboundedReceiver, UnboundedSender}, + oneshot, + }, + FutureExt, StreamExt, +}; + +pub(crate) struct LifeCycle { + ctx: Context, + tx: std::sync::Arc>>, + rx: UnboundedReceiver>, + tx_exit: oneshot::Sender<()>, +} + +impl LifeCycle { + pub(crate) fn new() -> Self { + let (tx_exit, rx_exit) = oneshot::channel(); + let rx_exit = rx_exit.shared(); + let (ctx, rx, tx) = Context::new(Some(rx_exit)); + Self { + ctx, + rx, + tx, + tx_exit, + } + } + + pub(crate) fn address(&self) -> Addr { + self.ctx.address() + } + + pub(crate) async fn start_actor(self, mut actor: A) -> Result> { + let Self { + mut ctx, + mut rx, + tx, + tx_exit, + } = self; + + let rx_exit = ctx.rx_exit.clone(); + let actor_id = ctx.actor_id(); + + // Call started + actor.started(&mut ctx).await?; + + spawn({ + async move { + while let Some(event) = rx.next().await { + match event { + ActorEvent::Exec(f) => f(&mut actor, &mut ctx).await, + ActorEvent::Stop(_err) => break, + ActorEvent::StopSupervisor(_err) => {} + ActorEvent::RemoveStream(id) => { + if ctx.streams.contains(id) { + ctx.streams.remove(id); + } + } + } + } + + actor.stopped(&mut ctx).await; + + ctx.abort_streams(); + ctx.abort_intervals(); + + tx_exit.send(()).ok(); + } + }); + + Ok(Addr { + actor_id, + tx, + rx_exit, + }) + } + + pub async fn start_supervised(self, f: F) -> Result> + where + F: Fn() -> A + Send + 'static, + { + let Self { + mut ctx, + mut rx, + tx, + .. + } = self; + + let addr = Addr { + actor_id: ctx.actor_id(), + tx, + rx_exit: ctx.rx_exit.clone(), + }; + + // Create the actor + let mut actor = f(); + + // Call started + actor.started(&mut ctx).await?; + + spawn({ + async move { + 'restart_loop: loop { + 'event_loop: loop { + match rx.next().await { + None => break 'restart_loop, + Some(ActorEvent::Stop(_err)) => break 'event_loop, + Some(ActorEvent::StopSupervisor(_err)) => break 'restart_loop, + Some(ActorEvent::Exec(f)) => f(&mut actor, &mut ctx).await, + Some(ActorEvent::RemoveStream(id)) => { + if ctx.streams.contains(id) { + ctx.streams.remove(id); + } + } + } + } + + actor.stopped(&mut ctx).await; + ctx.abort_streams(); + ctx.abort_intervals(); + + actor = f(); + actor.started(&mut ctx).await.ok(); + } + actor.stopped(&mut ctx).await; + ctx.abort_streams(); + ctx.abort_intervals(); + } + }); + + Ok(addr) + } +} diff --git a/src/service.rs b/src/service.rs index bf2127d..f17b963 100644 --- a/src/service.rs +++ b/src/service.rs @@ -1,13 +1,14 @@ -use crate::actor::ActorManager; -use crate::{Actor, Addr}; -use crate::error::Result; use fnv::FnvHasher; use futures::lock::Mutex; use once_cell::sync::OnceCell; -use std::any::{Any, TypeId}; -use std::cell::RefCell; -use std::collections::HashMap; -use std::hash::BuildHasherDefault; +use std::{ + any::{Any, TypeId}, + cell::RefCell, + collections::HashMap, + hash::BuildHasherDefault, +}; + +use crate::{error::Result, lifecycle::LifeCycle, Actor, Addr}; /// Trait define a global service. /// @@ -57,12 +58,12 @@ pub trait Service: Actor + Default { match registry.get_mut(&TypeId::of::()) { Some(addr) => Ok(addr.downcast_ref::>().unwrap().clone()), None => { - let actor_manager = ActorManager::new(); + let life_cycle = LifeCycle::new(); - registry.insert(TypeId::of::(), Box::new(actor_manager.address())); + registry.insert(TypeId::of::(), Box::new(life_cycle.address())); drop(registry); - actor_manager.start_actor(Self::default()).await + life_cycle.start_actor(Self::default()).await } } } @@ -88,7 +89,7 @@ pub trait LocalService: Actor + Default { match res { Some(addr) => Ok(addr), None => { - let addr = ActorManager::new().start_actor(Self::default()).await?; + let addr = LifeCycle::new().start_actor(Self::default()).await?; LOCAL_REGISTRY.with(|registry| { registry .borrow_mut() diff --git a/src/supervisor.rs b/src/supervisor.rs index d220e70..6cee743 100644 --- a/src/supervisor.rs +++ b/src/supervisor.rs @@ -1,8 +1,6 @@ -use crate::addr::ActorEvent; -use crate::runtime::spawn; -use crate::{Actor, Addr, Context}; use crate::error::Result; -use futures::StreamExt; +use crate::lifecycle::LifeCycle; +use crate::{Actor, Addr}; /// Actor supervisor /// @@ -75,48 +73,6 @@ impl Supervisor { A: Actor, F: Fn() -> A + Send + 'static, { - let (mut ctx, mut rx, tx) = Context::new(None); - let addr = Addr { - actor_id: ctx.actor_id(), - tx, - rx_exit: ctx.rx_exit.clone(), - }; - - // Create the actor - let mut actor = f(); - - // Call started - actor.started(&mut ctx).await?; - - spawn({ - async move { - 'restart_loop: loop { - 'event_loop: loop { - match rx.next().await { - None => break 'restart_loop, - Some(ActorEvent::Stop(_err)) => break 'event_loop, - Some(ActorEvent::Exec(f)) => f(&mut actor, &mut ctx).await, - Some(ActorEvent::RemoveStream(id)) => { - if ctx.streams.contains(id) { - ctx.streams.remove(id); - } - } - } - } - - actor.stopped(&mut ctx).await; - ctx.abort_streams(); - ctx.abort_intervals(); - - actor = f(); - actor.started(&mut ctx).await.ok(); - } - actor.stopped(&mut ctx).await; - ctx.abort_streams(); - ctx.abort_intervals(); - } - }); - - Ok(addr) + LifeCycle::new().start_supervised(f).await } }