pub mod error; /// Public export as the oneshot channel is in the `Actor` trait signature pub use tokio::sync::oneshot; use tokio::{ sync::{mpsc}, task::JoinHandle, }; use tokio_util::sync::CancellationToken; use crate::error::{Result, convert_err}; const DEFAULT_CHANNEL_SIZE: usize = 100; /// Combined 'JoinHandle' to await the actor process and 'Sender' for communication pub struct Handle where A: Actor, { cancel_token: CancellationToken, tx: mpsc::Sender<(A::Message, oneshot::Sender)>, _handle: JoinHandle>, // to prevent accidental swaps, strongly type the handle _type: std::marker::PhantomData, } impl Handle { /// Send a message to the spawned `Actor` task and get a response corresponding to the `Actor::Response` type pub async fn send(&self, msg: A::Message) -> Result { let (tx, rx) = oneshot::channel::(); self.tx.send((msg, tx)).await.map_err(convert_err)?; rx.await.map_err(convert_err) } } impl Drop for Handle { fn drop(&mut self) { self.cancel_token.cancel(); } } /// Primary trait defining an `Actor` capable of receiving, processing, and transmitting messages pub trait Actor: Send + Sized + Unpin + 'static { /// The type for messages received by this `Actor` type Message: Send; /// The type for responses given by this actor when called from `Handle::send(..)` type Response: Send; /// Inner method that defines actor behavior fn handle_message(&self, msg: Self::Message, tx: oneshot::Sender); /// Spawns the `Actor` utilizing the given runtime context /// Only `tokio` runtime is accepted for now fn spawn(self, ctx: &tokio::runtime::Runtime) -> Handle { let cancel_token = CancellationToken::new(); let cancel = cancel_token.clone(); let (tx, mut rx) = mpsc::channel::<(Self::Message, oneshot::Sender)>(DEFAULT_CHANNEL_SIZE); let handle = ctx.spawn(async move { let mut res = Ok(()); loop { tokio::select! { _ = cancel.cancelled() => { break; }, msg = rx.recv() => { match msg { Some(m) => { self.handle_message(m.0, m.1); }, None => {res = Err(format!("Sender handle was dropped without calling cancel!").into()); break; }, } } }; } res }); Handle { cancel_token, _handle: handle, tx, _type: std::marker::PhantomData::, } } }