├── .gitignore ├── zestors-codegen ├── README.md ├── Cargo.toml └── src │ ├── handler.rs │ ├── message.rs │ ├── lib.rs │ └── envelope.rs ├── supervision ├── src │ ├── lib.rs │ ├── supervision │ │ ├── supervisor2.rs │ │ ├── combinators │ │ │ ├── mod.rs │ │ │ ├── on_start_spec.rs │ │ │ └── ref_sender.rs │ │ ├── restart_limiter.rs │ │ ├── traits.rs │ │ ├── child_spec │ │ │ ├── mod.rs │ │ │ └── child_start_spec.rs │ │ ├── traits_ext.rs │ │ ├── handler_spec.rs │ │ ├── mod.rs │ │ └── supervisor.rs │ ├── future_map.rs │ ├── stream_map.rs │ └── supervision2 │ │ ├── box_supervisee.rs │ │ └── mod.rs └── Cargo.toml ├── Cargo.toml ├── zestors ├── src │ ├── distribution │ │ └── mod.rs │ ├── actor_type │ │ ├── errors.rs │ │ ├── actor_id.rs │ │ ├── dyn_actor.rs │ │ ├── dyn_types │ │ │ └── mod.rs │ │ ├── halter │ │ │ ├── mod.rs │ │ │ └── channel.rs │ │ ├── mod.rs │ │ ├── multi_halter │ │ │ ├── mod.rs │ │ │ └── channel.rs │ │ ├── actor_type.rs │ │ └── channel.rs │ ├── supervision │ │ └── mod.rs │ ├── _test │ │ └── mod.rs │ ├── handler │ │ ├── event_loop.rs │ │ ├── handler_ext.rs │ │ ├── state.rs │ │ ├── scheduler.rs │ │ ├── mod.rs │ │ └── handler.rs │ ├── spawning │ │ ├── errors.rs │ │ ├── mod.rs │ │ ├── link.rs │ │ ├── functions.rs │ │ └── capacity.rs │ ├── actor_reference │ │ ├── child_type.rs │ │ ├── mod.rs │ │ └── address.rs │ ├── runtime │ │ └── mod.rs │ ├── messaging │ │ ├── box_payload.rs │ │ ├── protocol.rs │ │ ├── envelope.rs │ │ ├── accepts.rs │ │ ├── errors.rs │ │ ├── mod.rs │ │ ├── request.rs │ │ └── message.rs │ └── lib.rs ├── Cargo.lock ├── tests │ ├── macro.rs │ └── lib.rs ├── Cargo.toml └── examples │ ├── minimal.rs │ ├── spawning.rs │ ├── actor_type.rs │ ├── actor_reference.rs │ ├── messaging.rs │ └── handler.rs └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /zestors-codegen/README.md: -------------------------------------------------------------------------------- 1 | Code-generation for https://github.com/Zestors/zestors. -------------------------------------------------------------------------------- /supervision/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod future_map; 2 | mod stream_map; 3 | pub use self::future_map::*; 4 | pub use stream_map::*; -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "zestors", 4 | "zestors-codegen", 5 | # "supervision" 6 | ] 7 | -------------------------------------------------------------------------------- /zestors/src/distribution/mod.rs: -------------------------------------------------------------------------------- 1 | //! # TODO 2 | //! 3 | //! | __<--__ [`supervision`](crate::supervision) | `?` __-->__ | 4 | //! |---|---| 5 | #[allow(unused)] 6 | use crate::*; 7 | -------------------------------------------------------------------------------- /zestors/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "dynamic-messaging" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /supervision/src/supervision/supervisor2.rs: -------------------------------------------------------------------------------- 1 | use super::Specification; 2 | 3 | pub struct Supervisor { 4 | specification: S 5 | } 6 | 7 | // impl Supervisor { 8 | // pub async fn spawn(self) -> 9 | // } -------------------------------------------------------------------------------- /supervision/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "supervision" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | futures = "0.3.26" 10 | -------------------------------------------------------------------------------- /supervision/src/supervision/combinators/mod.rs: -------------------------------------------------------------------------------- 1 | pub(super) use super::*; 2 | 3 | mod ref_sender; 4 | mod box_spec; 5 | mod on_start_spec; 6 | mod one_for_one; 7 | pub use on_start_spec::*; 8 | pub use one_for_one::*; 9 | pub use ref_sender::*; 10 | pub use box_spec::*; -------------------------------------------------------------------------------- /zestors/src/actor_type/errors.rs: -------------------------------------------------------------------------------- 1 | /// Error returned when trying to add a process to an actor. 2 | #[derive(Debug, thiserror::Error, PartialEq, Eq)] 3 | pub enum AddProcessError { 4 | #[error("The actor has exited so no more processes may be added.")] 5 | ActorHasExited, 6 | #[error("The actor has a channel that does not support multiple inboxes.")] 7 | SingleProcessOnly, 8 | } 9 | -------------------------------------------------------------------------------- /zestors/src/supervision/mod.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | # TODO 3 | Supervision is the current focus area. I have tried a lot of solutions, but nothing I am truly happy 4 | with yet. 5 | 6 | (It is already possible to build supervision-trees using [children](`Child`) with something like [`tokio::select`], 7 | and manually restarting the process upon exit.) 8 | 9 | | __<--__ [`runtime`](crate::runtime) | [`distribution`](crate::distribution) __-->__ | 10 | |---|---| 11 | */ 12 | 13 | #[allow(unused)] 14 | use crate::all::*; -------------------------------------------------------------------------------- /zestors-codegen/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zestors-codegen" 3 | version = "0.1.1" 4 | edition = "2021" 5 | 6 | license = "MIT OR Apache-2.0" 7 | description = "A fast and flexible actor-framework for building fault-tolerant Rust applications" 8 | repository = "https://github.com/Zestors/zestors" 9 | homepage = "https://github.com/Zestors/zestors" 10 | keywords = ["actor", "actor-system", "tokio", "pool"] 11 | categories = ["concurrency"] 12 | authors = ["jvdwrf"] 13 | readme = "README.md" 14 | 15 | [dependencies] 16 | syn = { version = "1", features = ["full"] } 17 | quote = "1" 18 | proc-macro2 = "1" 19 | heck = "0.4" 20 | 21 | [lib] 22 | proc-macro = true 23 | -------------------------------------------------------------------------------- /zestors/tests/macro.rs: -------------------------------------------------------------------------------- 1 | use zestors::{ 2 | actor_reference::{ActorRefExt, Address}, 3 | DynActor, 4 | }; 5 | use zestors_codegen::{Envelope, Message}; 6 | 7 | #[allow(unused)] 8 | #[derive(Message, Envelope, Debug)] 9 | #[request(u64)] 10 | #[envelope(SayHelloEnvelope, say_hello)] 11 | pub struct SayHello { 12 | field_a: u32, 13 | field_b: String, 14 | } 15 | 16 | #[allow(unused)] 17 | async fn test(address: Address) { 18 | address 19 | .request(SayHello { 20 | field_a: 10, 21 | field_b: String::from("hi"), 22 | }) 23 | .await 24 | .unwrap(); 25 | address 26 | .envelope(SayHello { 27 | field_a: 10, 28 | field_b: String::from("hi"), 29 | }) 30 | .request() 31 | .await 32 | .unwrap(); 33 | address 34 | .say_hello(10, String::from("hi")) 35 | .request() 36 | .await 37 | .unwrap(); 38 | } 39 | -------------------------------------------------------------------------------- /zestors/src/_test/mod.rs: -------------------------------------------------------------------------------- 1 | extern crate self as zestors; 2 | use crate::protocol; 3 | 4 | macro_rules! basic_actor { 5 | () => { 6 | $crate::_test::basic_actor!(()) 7 | }; 8 | ($ty:ty) => { 9 | |mut inbox: crate::all::Inbox<$ty>| async move { 10 | loop { 11 | match inbox.recv().await { 12 | Ok(_) => (), 13 | Err(e) => break e, 14 | } 15 | } 16 | } 17 | }; 18 | } 19 | pub(crate) use basic_actor; 20 | 21 | macro_rules! pooled_basic_actor { 22 | () => { 23 | crate::_test::pooled_basic_actor!(()) 24 | }; 25 | ($ty:ty) => { 26 | |_, mut inbox: crate::all::Inbox<$ty>| async move { 27 | loop { 28 | match inbox.recv().await { 29 | Ok(_) => (), 30 | Err(e) => break e, 31 | } 32 | } 33 | } 34 | }; 35 | } 36 | pub(crate) use pooled_basic_actor; 37 | 38 | #[protocol] 39 | pub(crate) enum U32Protocol { 40 | U32(u32), 41 | } 42 | -------------------------------------------------------------------------------- /zestors/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "zestors" 4 | version = "0.1.2" 5 | 6 | license = "MIT OR Apache-2.0" 7 | description = "A fast and flexible actor-framework for building fault-tolerant Rust applications" 8 | repository = "https://github.com/Zestors/zestors" 9 | homepage = "https://github.com/Zestors/zestors" 10 | keywords = ["actor", "actor-system", "tokio", "pool"] 11 | categories = ["concurrency"] 12 | authors = ["jvdwrf"] 13 | readme = "../README.md" 14 | 15 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 16 | 17 | [dependencies] 18 | async-trait = "0.1" 19 | futures = "0.3" 20 | thiserror = "1" 21 | tokio = {version = "1", features = ["full"]} 22 | bincode = "1" 23 | pin-project = "1" 24 | serde = {version = "1", features = ["derive"]} 25 | tokio-util = {version = "0.6", features = ["codec"]} 26 | event-listener = "2" 27 | tokio-test = "0.4" 28 | concurrent-queue = "2" 29 | eyre = "0.6" 30 | # uuid = { version = "1.0", features = ["v4"] } 31 | 32 | zestors-codegen = { path = "../zestors-codegen", version = "0.1" } 33 | 34 | 35 | [dev-dependencies] 36 | -------------------------------------------------------------------------------- /zestors/src/actor_type/actor_id.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | fmt::{Debug, Display}, 3 | sync::atomic::{AtomicU64, Ordering}, 4 | }; 5 | 6 | /// An actor-id is an incrementally-generated id, unique per actor. 7 | #[derive(PartialEq, Eq, Debug, PartialOrd, Ord, Hash, Clone, Copy)] 8 | pub struct ActorId(u64); 9 | 10 | impl ActorId { 11 | /// Generate a new unique actor-id. 12 | /// 13 | /// This is the only way to create actor-id's. (Except for Clone/Copy) 14 | pub fn generate() -> Self { 15 | static NEXT_ACTOR_ID: AtomicU64 = AtomicU64::new(0); 16 | ActorId(NEXT_ACTOR_ID.fetch_add(1, Ordering::AcqRel)) 17 | } 18 | 19 | /// Convert the actor-id to a u64. 20 | pub fn as_u64(self) -> u64 { 21 | self.0 22 | } 23 | } 24 | 25 | impl Display for ActorId { 26 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 27 | ::fmt(&self, f) 28 | } 29 | } 30 | 31 | #[cfg(test)] 32 | mod test { 33 | use super::*; 34 | 35 | #[test] 36 | fn actor_ids_increase() { 37 | let mut old_id = ActorId::generate(); 38 | for _ in 0..100 { 39 | let id = ActorId::generate(); 40 | assert!(id > old_id); 41 | old_id = id; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /zestors/src/handler/event_loop.rs: -------------------------------------------------------------------------------- 1 | use crate::all::*; 2 | 3 | /// Runs the event-loop of the handler. 4 | pub(crate) async fn event_loop(mut handler: H, mut state: H::State) -> H::Exit { 5 | let mut dead_events_in_a_row = 0; 6 | 7 | loop { 8 | let handler_item = state.next_handler_item().await; 9 | 10 | if let HandlerItem::Event(Event::Dead) = &handler_item { 11 | if dead_events_in_a_row > 5 { 12 | panic!("Process should exit when receiving an `Event::Dead`!") 13 | } 14 | dead_events_in_a_row += 1; 15 | } else { 16 | dead_events_in_a_row = 0; 17 | } 18 | 19 | let handler_res = handler_item.handle_with(&mut handler, &mut state).await; 20 | 21 | let exit_res = match handler_res { 22 | Ok(Flow::Continue) => ExitFlow::Continue(handler), 23 | Ok(Flow::ExitDirectly(exit)) => ExitFlow::Exit(exit), 24 | Ok(Flow::Stop(stop)) => handler.handle_exit(&mut state, Ok(stop)).await, 25 | Err(exception) => handler.handle_exit(&mut state, Err(exception)).await, 26 | }; 27 | 28 | handler = match exit_res { 29 | ExitFlow::Exit(exit) => break exit, 30 | ExitFlow::Continue(handler) => handler, 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /zestors/src/spawning/errors.rs: -------------------------------------------------------------------------------- 1 | use thiserror::Error; 2 | #[allow(unused)] 3 | use crate::all::*; 4 | 5 | /// An error returned when trying to spawn additional processes onto a dynamic [`Child`]. 6 | #[derive(Clone, PartialEq, Eq, Hash, Error)] 7 | pub enum TrySpawnError { 8 | /// The actor has exited. 9 | #[error("Couldn't spawn process because the actor has exited")] 10 | Exited(T), 11 | /// The spawned inbox does not have the correct type 12 | #[error("Couldn't spawn process because the given inbox-type is incorrect")] 13 | WrongInbox(T), 14 | } 15 | 16 | impl std::fmt::Debug for TrySpawnError { 17 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 18 | match self { 19 | Self::Exited(_) => f.debug_tuple("Exited").finish(), 20 | Self::WrongInbox(_) => f.debug_tuple("WrongInbox").finish(), 21 | } 22 | } 23 | } 24 | 25 | /// An error returned when trying to spawn additional processes onto a [`Child`]. 26 | #[derive(Clone, PartialEq, Eq, Hash, Error)] 27 | #[error("Couldn't spawn process because the channel has exited")] 28 | pub struct SpawnError(pub T); 29 | 30 | impl std::fmt::Debug for SpawnError { 31 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 32 | f.debug_tuple("SpawnError").finish() 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /supervision/src/future_map.rs: -------------------------------------------------------------------------------- 1 | use futures::{FutureExt, Stream}; 2 | use std::{ 3 | future::Future, 4 | pin::Pin, 5 | task::{Context, Poll}, 6 | }; 7 | 8 | pub struct FutureIdMap { 9 | next_id: usize, 10 | futures: Vec<(u64, F)>, 11 | } 12 | 13 | impl FutureIdMap { 14 | pub fn new() -> Self { 15 | Self { 16 | next_id: 0, 17 | futures: Vec::new(), 18 | } 19 | } 20 | } 21 | 22 | impl Unpin for FutureIdMap {} 23 | 24 | impl Stream for FutureIdMap { 25 | type Item = (u64, F::Output); 26 | 27 | fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 28 | let result = self 29 | .futures 30 | .iter_mut() 31 | .enumerate() 32 | .find_map(|(index, future)| { 33 | if let Poll::Ready(output) = future.1.poll_unpin(cx) { 34 | Some((index, output)) 35 | } else { 36 | None 37 | } 38 | }); 39 | 40 | match result { 41 | Some((index, output)) => { 42 | let (fut_id, _future) = self.futures.swap_remove(index); 43 | Poll::Ready(Some((fut_id, output))) 44 | } 45 | None => { 46 | if self.futures.len() > 0 { 47 | Poll::Pending 48 | } else { 49 | Poll::Ready(None) 50 | } 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /supervision/src/supervision/restart_limiter.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | use tokio::time::Instant; 3 | 4 | #[derive(Debug)] 5 | pub(crate) struct RestartLimiter { 6 | limit: usize, 7 | within: Duration, 8 | values: Vec, 9 | triggered: bool, 10 | } 11 | 12 | impl RestartLimiter { 13 | /// Create a new restart_limiter, with a given limit within the duration. 14 | pub fn new(limit: usize, within: Duration) -> Self { 15 | Self { 16 | limit, 17 | within, 18 | values: Vec::new(), 19 | triggered: false, 20 | } 21 | } 22 | 23 | /// Sets a new limit. 24 | pub fn set_limit(&mut self, limit: usize) { 25 | self.limit = limit; 26 | } 27 | 28 | /// Sets a new duration. 29 | pub fn set_within(&mut self, within: Duration) { 30 | self.within = within; 31 | } 32 | 33 | pub fn reset(&mut self) { 34 | self.triggered = false; 35 | self.values.drain(..); 36 | } 37 | 38 | /// Adds a restart and then checks whether the restart is within the limit. 39 | pub fn within_limit(&mut self) -> bool { 40 | if !self.triggered() { 41 | self.values.push(Instant::now()); 42 | self.values 43 | .retain(|instant| instant.elapsed() < self.within); 44 | 45 | if self.values.len() <= self.limit { 46 | self.triggered = true 47 | } 48 | } 49 | 50 | self.triggered() 51 | } 52 | 53 | pub fn triggered(&self) -> bool { 54 | self.triggered 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /zestors/src/actor_reference/child_type.rs: -------------------------------------------------------------------------------- 1 | #[allow(unused)] 2 | use crate::all::*; 3 | use tokio::task::JoinHandle; 4 | 5 | /// The parameter `C` in a [`Child<_, _, C>`] that specifies what kind of child it is: 6 | /// - [`SingleProcess`] -> The actor consists of a single process: [`Child<_, _>`]. 7 | /// - [`MultiProcess`] -> The actor consists of multiple processes: [`ChildPool<_, _>`]. 8 | pub trait ChildType { 9 | type JoinHandles: Send + 'static; 10 | fn abort(handles: &Self::JoinHandles); 11 | fn is_finished(handles: &Self::JoinHandles) -> bool; 12 | } 13 | 14 | /// The default [`ChildType`]. 15 | #[derive(Debug)] 16 | pub struct SingleProcess; 17 | 18 | impl ChildType for SingleProcess { 19 | type JoinHandles = JoinHandle; 20 | 21 | fn abort(handles: &Self::JoinHandles) { 22 | handles.abort() 23 | } 24 | 25 | fn is_finished(handles: &Self::JoinHandles) -> bool { 26 | handles.is_finished() 27 | } 28 | } 29 | 30 | /// The pooled [`ChildType`]. 31 | #[derive(Debug)] 32 | pub struct MultiProcess; 33 | 34 | impl ChildType for MultiProcess { 35 | type JoinHandles = Vec>; 36 | 37 | fn abort(handles: &Self::JoinHandles) { 38 | for handle in handles { 39 | handle.abort() 40 | } 41 | } 42 | 43 | fn is_finished(handles: &Self::JoinHandles) -> bool { 44 | handles.iter().all(|handle| handle.is_finished()) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /supervision/src/stream_map.rs: -------------------------------------------------------------------------------- 1 | use futures::{Stream, StreamExt}; 2 | use std::{ 3 | pin::Pin, 4 | task::{Context, Poll}, 5 | }; 6 | 7 | pub struct StreamMap { 8 | next_id: usize, 9 | streams: Vec<(u64, F)>, 10 | } 11 | 12 | impl StreamMap {} 13 | 14 | impl Unpin for StreamMap {} 15 | 16 | impl Stream for StreamMap { 17 | type Item = (u64, F::Item); 18 | 19 | fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 20 | loop { 21 | let result = self 22 | .streams 23 | .iter_mut() 24 | .enumerate() 25 | .find_map(|(index, stream)| { 26 | if let Poll::Ready(item) = stream.1.poll_next_unpin(cx) { 27 | Some((index, stream.0, item)) 28 | } else { 29 | None 30 | } 31 | }); 32 | 33 | match result { 34 | Some((index, stream_id, item)) => match item { 35 | Some(item) => { 36 | break Poll::Ready(Some((stream_id, item))); 37 | } 38 | None => { 39 | let _ = self.streams.swap_remove(index); 40 | } 41 | }, 42 | None => { 43 | if self.streams.len() > 0 { 44 | break Poll::Pending; 45 | } else { 46 | break Poll::Ready(None); 47 | } 48 | } 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /zestors/src/runtime/mod.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | # Overview 3 | 4 | This module currently just exposes two functions: 5 | - [`set_default_shutdown_time`] and [`get_default_shutdown_time`]. 6 | 7 | As supervision and distribution get implemented this module will fill further. 8 | 9 | | __<--__ [`handler`](crate::handler) | [`supervision`](crate::supervision) __-->__ | 10 | |---|---| 11 | */ 12 | #[allow(unused)] 13 | use crate::all::*; 14 | 15 | use std::{ 16 | sync::atomic::{AtomicU32, AtomicU64, Ordering}, 17 | time::Duration, 18 | }; 19 | 20 | static DEFAULT_SHUTDOWN_TIME_NANOS: AtomicU32 = AtomicU32::new(0); 21 | static DEFAULT_SHUTDOWN_TIME_SECS: AtomicU64 = AtomicU64::new(1); 22 | 23 | /// Set the default shutdown time-limit. 24 | /// 25 | /// This limit is the limit applied to a default [`Link`], and changes how long these processes 26 | /// have to exit before 27 | pub fn set_default_shutdown_time(duration: Duration) { 28 | DEFAULT_SHUTDOWN_TIME_NANOS.store(duration.subsec_nanos(), Ordering::Release); 29 | DEFAULT_SHUTDOWN_TIME_SECS.store(duration.as_secs(), Ordering::Release); 30 | } 31 | 32 | /// Get the default shutdown time-limit. 33 | pub fn get_default_shutdown_time() -> Duration { 34 | Duration::new( 35 | DEFAULT_SHUTDOWN_TIME_SECS.load(Ordering::Acquire), 36 | DEFAULT_SHUTDOWN_TIME_NANOS.load(Ordering::Acquire), 37 | ) 38 | } 39 | 40 | #[cfg(test)] 41 | mod test { 42 | use super::*; 43 | #[test] 44 | fn default_shutdown_time() { 45 | assert_eq!(get_default_shutdown_time(), Duration::from_secs(1)); 46 | set_default_shutdown_time(Duration::from_secs(2)); 47 | assert_eq!(get_default_shutdown_time(), Duration::from_secs(2)); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /zestors/examples/minimal.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate zestors; 3 | use zestors::{messaging::RecvError, prelude::*}; 4 | 5 | // Let's define a single request .. 6 | #[derive(Message, Envelope, Debug)] 7 | #[request(u32)] 8 | struct MyRequest { 9 | param: String, 10 | } 11 | 12 | // .. and create a protocol that accepts this request. 13 | #[protocol] 14 | enum MyProtocol { 15 | MyRequest(MyRequest), 16 | String(String), 17 | } 18 | 19 | #[tokio::main] 20 | async fn main() { 21 | // Now we can spawn a simple actor .. 22 | let (child, address) = spawn(|mut inbox: Inbox| async move { 23 | loop { 24 | match inbox.recv().await { 25 | Ok(msg) => match msg { 26 | MyProtocol::MyRequest((request, tx)) => { 27 | println!("Received request: {:?}", request.param); 28 | tx.send(100).unwrap(); 29 | } 30 | MyProtocol::String(string) => { 31 | println!("Received message: {:?}", string); 32 | } 33 | }, 34 | Err(e) => match e { 35 | RecvError::Halted => break "Halted", 36 | RecvError::ClosedAndEmpty => break "Closed", 37 | }, 38 | } 39 | } 40 | }); 41 | 42 | // .. and send it some messages! 43 | address.send("Hi".to_string()).await.unwrap(); 44 | 45 | let response = address 46 | .request(MyRequest { 47 | param: "Hi".to_string(), 48 | }) 49 | .await 50 | .unwrap(); 51 | assert_eq!(response, 100); 52 | 53 | let response = address 54 | .my_request("Hi".to_string()) 55 | .request() 56 | .await 57 | .unwrap(); 58 | assert_eq!(response, 100); 59 | 60 | child.halt(); 61 | assert_eq!(child.await.unwrap(), "Halted"); 62 | } 63 | -------------------------------------------------------------------------------- /supervision/src/supervision/traits.rs: -------------------------------------------------------------------------------- 1 | use super::FatalError; 2 | #[allow(unused)] 3 | use crate::all::*; 4 | use async_trait::async_trait; 5 | use std::{ 6 | pin::Pin, 7 | task::{Context, Poll}, 8 | time::Duration, 9 | }; 10 | 11 | #[async_trait] 12 | /// Specifies how [`Specification`] is started and supervised as a [`Supervisee`]. 13 | pub trait Specification: Send + Sized + 'static { 14 | /// The reference passed on when the supervisee is started 15 | type Ref: Send + 'static; 16 | 17 | /// The [`Supervisee`] returned after starting succesfully. 18 | type Supervisee: Supervisee; 19 | 20 | /// Start the supervisee. 21 | async fn start_supervised(self) -> StartResult; 22 | } 23 | 24 | #[derive(thiserror::Error)] 25 | pub enum StartError { 26 | /// Starting the supervisee has failed, but it may be retried. 27 | #[error("")] 28 | StartFailed(S), 29 | /// Starting the supervisee has failed, with no way to retry. 30 | #[error("")] 31 | Fatal(FatalError), 32 | /// The supervisee is completed and does not need to be restarted. 33 | #[error("")] 34 | Completed, 35 | } 36 | 37 | /// Returned when starting a [`Specification`]. 38 | pub type StartResult = 39 | Result<(::Supervisee, ::Ref), StartError>; 40 | 41 | /// Specifies how a [`Specification`] is supervised. 42 | #[async_trait] 43 | pub trait Supervisee: Send + Sized { 44 | type Spec: Specification; 45 | 46 | fn poll_supervise( 47 | self: Pin<&mut Self>, 48 | cx: &mut Context, 49 | ) -> Poll>; 50 | 51 | fn shutdown_time(self: Pin<&Self>) -> Duration; 52 | 53 | fn halt(self: Pin<&mut Self>); 54 | 55 | fn abort(self: Pin<&mut Self>); 56 | } 57 | 58 | /// Returned when a [`Supervisee`] exits. 59 | pub type SupervisionResult = Result, FatalError>; 60 | -------------------------------------------------------------------------------- /zestors/src/handler/handler_ext.rs: -------------------------------------------------------------------------------- 1 | use super::event_loop::event_loop; 2 | use crate::all::*; 3 | use async_trait::async_trait; 4 | 5 | /// An extension to [`Handler`] with many useful functions. 6 | #[async_trait] 7 | pub trait HandlerExt: Handler { 8 | /// Run the event-loop of this [`Handler`]. 9 | async fn run(self, state: Self::State) -> Self::Exit { 10 | event_loop(self, state).await 11 | } 12 | 13 | fn spawn_with( 14 | self, 15 | link: Link, 16 | cfg: HandlerConfig, 17 | ) -> ( 18 | Child>, 19 | Address>, 20 | ) { 21 | spawn_with(link, cfg, |inbox| async move { 22 | let state = >::from_inbox(inbox); 23 | self.run(state).await 24 | }) 25 | } 26 | 27 | fn spawn( 28 | self, 29 | ) -> ( 30 | Child>, 31 | Address>, 32 | ) { 33 | self.spawn_with(Self::default_link(), Self::default_config()) 34 | } 35 | 36 | // fn start_with<'a, I>( 37 | // init: I, 38 | // link: Link, 39 | // cfg: HandlerConfig, 40 | // ) -> BoxFuture<'a, Result<(Child>, Self::Ref), Self::InitError>> 41 | // where 42 | // Self: HandleInit + 'a, 43 | // { 44 | // Self::initialize(SpawnConfig::new(link, cfg), init) 45 | // } 46 | 47 | // fn start<'a, I>( 48 | // init: I, 49 | // ) -> BoxFuture<'a, Result<(Child>, Self::Ref), Self::InitError>> 50 | // where 51 | // Self: HandleInit + 'a, 52 | // { 53 | // Self::initialize(SpawnConfig::default(), init) 54 | // } 55 | 56 | // fn create_spec(init: I) -> HandlerSpec 57 | // where 58 | // Self: HandleRestart, 59 | // I: Send + 'static, 60 | // { 61 | // HandlerSpec::new(init) 62 | // } 63 | } 64 | impl HandlerExt for T {} 65 | -------------------------------------------------------------------------------- /zestors/src/messaging/box_payload.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use std::any::Any; 3 | 4 | /// A wrapper-type around a `Box` for a [`Message::Payload`]. 5 | #[derive(Debug)] 6 | pub struct BoxPayload(Box); 7 | 8 | impl BoxPayload { 9 | /// Create a new [`BoxPayload`] from the [`Message::Payload`]. 10 | pub fn new(sent: M::Payload) -> Self 11 | where 12 | M: Message, 13 | M::Payload: Send + 'static, 14 | { 15 | Self(Box::new(sent)) 16 | } 17 | 18 | /// Downcast the [`BoxPayload`] into a [`Message::Payload`]. 19 | pub fn downcast(self) -> Result 20 | where 21 | M: Message, 22 | M::Payload: 'static, 23 | { 24 | match self.0.downcast() { 25 | Ok(cast) => Ok(*cast), 26 | Err(boxed) => Err(Self(boxed)), 27 | } 28 | } 29 | 30 | pub(crate) fn downcast_and_cancel(self, returned: M::Returned) -> Result 31 | where 32 | M: Message, 33 | M::Payload: 'static, 34 | { 35 | match self.downcast::() { 36 | Ok(sends) => Ok(M::cancel(sends, returned)), 37 | Err(boxed) => Err(boxed), 38 | } 39 | } 40 | } 41 | 42 | #[cfg(test)] 43 | mod test { 44 | use super::*; 45 | 46 | #[test] 47 | fn boxed_msg() { 48 | struct Msg1; 49 | struct Msg2; 50 | 51 | impl Message for Msg1 { 52 | type Payload = Self; 53 | type Returned = (); 54 | fn create(self) -> (Self::Payload, Self::Returned) { 55 | (self, ()) 56 | } 57 | fn cancel(sent: Self::Payload, _returned: Self::Returned) -> Self { 58 | sent 59 | } 60 | } 61 | 62 | impl Message for Msg2 { 63 | type Payload = Self; 64 | type Returned = (); 65 | fn create(self) -> (Self::Payload, Self::Returned) { 66 | (self, ()) 67 | } 68 | fn cancel(sent: Self::Payload, _returned: Self::Returned) -> Self { 69 | sent 70 | } 71 | } 72 | 73 | let boxed = BoxPayload::new::(Msg1); 74 | assert!(boxed.downcast::().is_ok()); 75 | 76 | let boxed = BoxPayload::new::(Msg1); 77 | assert!(boxed.downcast::().is_err()); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /zestors/src/actor_type/dyn_actor.rs: -------------------------------------------------------------------------------- 1 | #[allow(unused)] 2 | use crate::all::*; 3 | 4 | /// Macro for writing a [`struct@DynActor`]: 5 | /// 6 | /// - `DynActor!()` = `DynActor` 7 | /// - `DynActor!(u32, u64)` = `DynActor` 8 | #[macro_export] 9 | macro_rules! DynActor { 10 | () => { 11 | $crate::actor_type::DynActor 12 | }; 13 | ($ty1:ty) => { 14 | $crate::actor_type::DynActor> 15 | }; 16 | ($ty1:ty, $ty2:ty) => { 17 | $crate::actor_type::DynActor> 18 | }; 19 | ($ty1:ty, $ty2:ty, $ty3:ty) => { 20 | $crate::actor_type::DynActor> 21 | }; 22 | ($ty1:ty, $ty2:ty, $ty3:ty, $ty4:ty) => { 23 | $crate::actor_type::DynActor> 24 | }; 25 | ($ty1:ty, $ty2:ty, $ty3:ty, $ty4:ty, $ty5:ty) => { 26 | $crate::actor_type::DynActor> 27 | }; 28 | ($ty1:ty, $ty2:ty, $ty3:ty, $ty4:ty, $ty5:ty, $ty6:ty) => { 29 | $crate::actor_type::DynActor> 30 | }; 31 | ($ty1:ty, $ty2:ty, $ty3:ty, $ty4:ty, $ty5:ty, $ty6:ty, $ty7:ty) => { 32 | $crate::actor_type::DynActor> 33 | }; 34 | ($ty1:ty, $ty2:ty, $ty3:ty, $ty4:ty, $ty5:ty, $ty6:ty, $ty7:ty, $ty8:ty) => { 35 | $crate::actor_type::DynActor> 36 | }; 37 | ($ty1:ty, $ty2:ty, $ty3:ty, $ty4:ty, $ty5:ty, $ty6:ty, $ty7:ty, $ty8:ty, $ty9:ty) => { 38 | $crate::actor_type::DynActor> 39 | }; 40 | ($ty1:ty, $ty2:ty, $ty3:ty, $ty4:ty, $ty5:ty, $ty6:ty, $ty7:ty, $ty8:ty, $ty9:ty, $ty10:ty) => { 41 | $crate::actor_type::DynActor> 42 | }; 43 | } 44 | pub use DynActor; 45 | -------------------------------------------------------------------------------- /supervision/src/supervision/child_spec/mod.rs: -------------------------------------------------------------------------------- 1 | //! # Child specifications: 2 | //! 3 | //! ### Spawn specification 1: 4 | //! - spawn_fn: async fn(D, Inbox) -> E 5 | //! - exit_fn: async fn(ExitResult) -> Result, BoxError> 6 | //! - data: D 7 | //! - inbox_config: Inbox::Config (default) 8 | //! - abort_time: FnMut() -> Duration 9 | //! 10 | //! ### Spawn specification 2: (Clonable data) 11 | //! - spawn_fn: async fn(D, Inbox) -> Exit 12 | //! - exit_fn: async fn(ExitResult) -> Result 13 | //! - data: D 14 | //! - inbox_config: Inbox::Config (default) 15 | //! - abort_time: FnMut() -> Duration 16 | //! 17 | //! ### Start specification 18 | //! - start_fn: async fn(D) -> Result<(Child, Ref), StartError> 19 | //! - exit_fn: async fn(ExitResult) -> Result, BoxError> 20 | //! - data: D 21 | //! - abort_time: FnMut() -> Duration 22 | //! 23 | //! ### Start specification 2: 24 | 25 | pub(super) use super::*; 26 | use std::time::Duration; 27 | mod child; 28 | mod spawn; 29 | mod child_start_spec; 30 | use async_trait::async_trait; 31 | use futures::future::BoxFuture; 32 | 33 | #[async_trait] 34 | pub trait RootSpecification: 'static + Sized + Send { 35 | type ActorType: ActorType; 36 | type ProcessExit: Send + 'static; 37 | type Address; 38 | type With; 39 | 40 | async fn start( 41 | &mut self, 42 | with: Self::With, 43 | ) -> Result<(Child, Self::Address), StartError>; 44 | 45 | async fn on_exit( 46 | &mut self, 47 | exit: Result, 48 | ) -> Result, FatalError>; 49 | 50 | fn start_timeout(&self) -> Duration { 51 | Duration::from_secs(1) 52 | } 53 | 54 | fn into_spec(self, with: Self::With) -> RootSpec { 55 | RootSpec { 56 | spec: Box::new(self), 57 | with, 58 | } 59 | } 60 | } 61 | 62 | pub struct RootSpec { 63 | spec: Box, 64 | with: S::With, 65 | } 66 | 67 | pub struct RootSpecFut { 68 | spec: *mut Box, 69 | fut: BoxFuture< 70 | 'static, 71 | Result<(Child, S::Address), StartError>, 72 | >, 73 | } 74 | 75 | pub struct RootSupervisee { 76 | child: Child, 77 | spec: Box, 78 | } 79 | -------------------------------------------------------------------------------- /zestors/src/actor_type/dyn_types/mod.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | Module containing all dynamic [actor types](ActorType). 3 | */ 4 | use crate::all::*; 5 | use std::{any::TypeId, sync::Arc}; 6 | 7 | macro_rules! create_dynamic_actor_types { 8 | ($($actor_ty:ident $(<$( $msg:ident ),*>)?),*) => {$( 9 | // creates the dynamic actor-type trait which implements FromPayload<..> as a marker-trait. 10 | /// Used as an argument to a [`DynActor`](struct@DynActor). 11 | pub trait $actor_ty< $($($msg: Message,)?)*>: std::fmt::Debug + $($( FromPayload<$msg> + )?)* {} 12 | 13 | // Implement the dynamic actor-type for DynActor 14 | impl<$($($msg: Message + 'static,)?)*> DynActorType for DynActor> { 15 | fn msg_ids() -> Box<[TypeId]> { 16 | Box::new([$($(TypeId::of::<$msg>(),)?)*]) 17 | } 18 | } 19 | 20 | // Any sized channel can transform into this DynActor, as long as it impl FromPayload 21 | // for all the messages 22 | impl TransformInto>> for DynActor 23 | where 24 | D: ?Sized $($( + FromPayload<$msg> )?)* 25 | { 26 | fn transform_into(channel: Arc) -> Arc { 27 | channel 28 | } 29 | } 30 | 31 | // Any sized channel can transform into this DynActor, as long as it implements 32 | // Accept all the messages 33 | impl TransformInto>> for I 34 | where 35 | I: InboxType $($( + Accepts<$msg> )?)*, 36 | I::Channel: Sized 37 | { 38 | fn transform_into(channel: Arc) -> Arc { 39 | ::into_dyn(channel) 40 | } 41 | } 42 | )*}; 43 | } 44 | 45 | create_dynamic_actor_types! { 46 | AcceptsNone, 47 | AcceptsOne, 48 | AcceptsTwo, 49 | AcceptsThree, 50 | AcceptsFour, 51 | AcceptsFive, 52 | AcceptsSix, 53 | AcceptsSeven, 54 | AcceptsEight, 55 | AcceptsNine, 56 | AcceptsTen 57 | } 58 | -------------------------------------------------------------------------------- /zestors/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A fast and flexible actor-framework for building fault-tolerant Rust applications. 2 | //! 3 | //! # Documentation 4 | //! All items are self-documented, but for a new user it is recommended to go skim through the docs in 5 | //! the following order. Every module gives an overview of what is contained inside, introduces some 6 | //! new concepts and then gives an example on how to use it. 7 | //! - [`messaging`] - Defining a protocol in order to send messages. 8 | //! - [`actor_reference`] - Interacting with an actor through it's child or address. 9 | //! - [`actor_type`] - Specifying the type of an actor statically and dynamically. 10 | //! - [`spawning`] - Spawning of actors. 11 | //! - [`handler`] - A simpler way to write your actors. 12 | //! - [`runtime`] - Runtime configuration. 13 | //! - [`supervision`] - (Not yet implemented) 14 | //! - [`distribution`] - (Not yet implemented) 15 | //! 16 | //! # Minimal example 17 | //! ``` 18 | #![doc = include_str!("../examples/minimal.rs")] 19 | //! ``` 20 | 21 | pub mod actor_reference; 22 | pub mod actor_type; 23 | pub mod distribution; 24 | pub mod handler; 25 | pub mod messaging; 26 | pub mod runtime; 27 | pub mod spawning; 28 | pub mod supervision; 29 | 30 | extern crate self as zestors; 31 | 32 | pub mod prelude { 33 | pub use crate::actor_reference::{ActorRefExt, Address, Child, ChildPool, Transformable}; 34 | pub use crate::actor_type::{ActorId, Halter, Inbox, MultiHalter}; 35 | pub use crate::handler::{ 36 | action, Action, Event, ExitFlow, Flow, HandleMessage, Handler, HandlerExt, HandlerResult, 37 | RestartReason, Scheduler, 38 | }; 39 | pub use crate::messaging::{Accepts, Envelope, Message, Rx, Tx}; 40 | pub use crate::runtime::{get_default_shutdown_time, set_default_shutdown_time}; 41 | pub use crate::spawning::{ 42 | spawn, spawn_many, spawn_many_with, spawn_with, BackPressure, Capacity, Link, 43 | }; 44 | } 45 | 46 | mod all { 47 | pub use crate::actor_reference::*; 48 | pub use crate::actor_type::*; 49 | pub use crate::distribution::*; 50 | pub use crate::handler::*; 51 | pub use crate::messaging::*; 52 | pub use crate::runtime::*; 53 | pub use crate::spawning::*; 54 | pub use crate::supervision::*; 55 | } 56 | #[cfg(test)] 57 | pub(crate) mod _test; 58 | 59 | pub mod export { 60 | pub use async_trait::async_trait; 61 | pub use eyre::Report; 62 | } 63 | 64 | pub use zestors_codegen::{protocol, Envelope, Handler, Message}; 65 | -------------------------------------------------------------------------------- /zestors-codegen/src/handler.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | use syn::{parse2, Error, Item, Type}; 3 | 4 | pub fn derive_handler(item: TokenStream) -> Result { 5 | let (ident, generics, attrs) = match parse2::(item)? { 6 | Item::Enum(item) => (item.ident, item.generics, item.attrs), 7 | Item::Struct(item) => (item.ident, item.generics, item.attrs), 8 | Item::Union(item) => (item.ident, item.generics, item.attrs), 9 | item => Err(Error::new_spanned(item, "Must be an Enum, Struct or Union"))?, 10 | }; 11 | 12 | let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); 13 | 14 | let Some(state) = attrs.iter().find(|attr| attr.path.is_ident("state")) else { 15 | return Err(Error::new_spanned(ident, "No #[state(..)] attribute found"))? 16 | }; 17 | let state = state.parse_args::()?; 18 | 19 | Ok(quote! { 20 | #[::zestors::export::async_trait] 21 | impl #impl_generics ::zestors::handler::Handler for #ident #ty_generics #where_clause 22 | { 23 | type State = #state; 24 | type Exception = ::zestors::export::Report; 25 | type Stop = (); 26 | type Exit = Result; 27 | 28 | async fn handle_exit( 29 | self, 30 | state: &mut Self::State, 31 | reason: Result, 32 | ) -> ::zestors::handler::ExitFlow { 33 | match reason { 34 | Ok(()) => ::zestors::handler::ExitFlow::Exit(Ok(self)), 35 | Err(exception) => ::zestors::handler::ExitFlow::Exit(Err(exception)) 36 | } 37 | } 38 | 39 | async fn handle_event( 40 | &mut self, 41 | state: &mut Self::State, 42 | event: ::zestors::handler::Event 43 | ) -> ::zestors::handler::HandlerResult { 44 | match event { 45 | ::zestors::handler::Event::Halted => { 46 | state.close(); 47 | Ok(::zestors::handler::Flow::Continue) 48 | } 49 | ::zestors::handler::Event::ClosedAndEmpty => Ok(::zestors::handler::Flow::Stop(())), 50 | ::zestors::handler::Event::Dead => Ok(::zestors::handler::Flow::Stop(())), 51 | } 52 | } 53 | } 54 | }) 55 | } 56 | -------------------------------------------------------------------------------- /zestors/src/spawning/mod.rs: -------------------------------------------------------------------------------- 1 | //! # Spawn functions 2 | //! When spawning an actor, there are a couple of options to choose from: 3 | //! - [`spawn(FnOnce)`](spawn) - This is the simplest way to spawn an actor and uses a default [`Link`] and 4 | //! [`InboxType::Config`]. 5 | //! - [`spawn_with(link, cfg, FnOnce)`](spawn_with) - Same as `spawn`, but allows for a custom link and config. 6 | //! - [`spawn_many(iter, FnOnce)`](spawn_many) - Same as `spawn`, but instead spawns many processes using 7 | //! the iterator as the first argument to the function. 8 | //! - [`spawn_many_with(iter, link, cfg, FnOnce)`](spawn_many_with) - Same as `spawn_many`, but allows for a 9 | //! custom link and config. 10 | //! 11 | //! It is also possible to spawn more processes onto an actor that is already running with 12 | //! [`ChildPool::spawn_onto`] and [`ChildPool::try_spawn_onto`]. 13 | //! 14 | //! # Link 15 | //! Every actor is spawned with a [`Link`] that indicates whether the actor is attached 16 | //! or detached. By default a [`Link`] is attached with an abort-timer of 1 second; this means that when the 17 | //! [`Child`] is dropped, the actor has 1 second to halt before it is aborted. If the link is detached, then 18 | //! the child can be dropped without halting or aborting the actor. 19 | //! 20 | //! # Inbox configuration 21 | //! Actors can be spawned with any [`InboxType`], where the [`InboxType::Config`] specifies the configuration-options 22 | //! for that inbox. The config for an [`Inbox`] is [`Capacity`] and for a [`Halter`] it is `()`. 23 | //! 24 | //! The [`Capacity`] of an inbox can be one of three options: 25 | //! - [`Capacity::Bounded(size)`](`Capacity::Bounded) --> An inbox that doesn't accept new messages after the 26 | //! given size has been reached. 27 | //! - [`Capacity::Unbounded`](Capacity::Unbounded) --> An inbox that grows in size infinitely when new messages 28 | //! are received. 29 | //! - [`Capacity::BackPressure(BackPressure)`](Capacity::BackPressure) (default) --> An unbounded inbox with 30 | //! a [`BackPressure`] mechanic. An overflow of messages is handled by increasing the delay for sending a message. 31 | //! 32 | //! | __<--__ [`actor_type`](crate::actor_type) | [`handler`](crate::handler) __-->__ | 33 | //! |---|---| 34 | //! 35 | //! # Example 36 | //! ``` 37 | #![doc = include_str!("../../examples/spawning.rs")] 38 | //! ``` 39 | 40 | mod capacity; 41 | mod errors; 42 | mod functions; 43 | mod link; 44 | #[allow(unused)] 45 | use crate::all::*; 46 | pub use {capacity::*, errors::*, functions::*, link::*}; 47 | -------------------------------------------------------------------------------- /supervision/src/supervision/traits_ext.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | pin::Pin, 3 | task::{Context, Poll}, 4 | }; 5 | 6 | use futures::Future; 7 | use pin_project::pin_project; 8 | use tokio::sync::mpsc; 9 | 10 | use crate::all::*; 11 | 12 | pub trait SpecificationExt: Specification { 13 | // /// Returns a future that can be awaited to supervise the supervisee. 14 | // fn start_supervisor(self) -> SupervisorFut { 15 | // SupervisorFut::new(self) 16 | // } 17 | 18 | // /// Spawns an actor that supervises the supervisee. 19 | // fn spawn_under_supervisor(self) -> (Child>, SupervisorRef) 20 | // where 21 | // Self: Send + 'static, 22 | // Self::Supervisee: Send, 23 | // { 24 | // self.start_supervisor().spawn_supervisor() 25 | // } 26 | 27 | // / Creates a new spec, which supervises by spawning a new actor (supervisor). 28 | // fn into_supervisor_spec(self) -> SupervisorSpec { 29 | // SupervisorSpec::new(self) 30 | // } 31 | 32 | // /// Whenever the supervisee is started, the ref is sent to the receiver. 33 | // fn send_refs_with(self, sender: mpsc::UnboundedSender) -> RefSenderSpec { 34 | // RefSenderSpec::new_with_channel(self, sender) 35 | // } 36 | 37 | /// Whenever the supervisee is started, the map function is called on the reference. 38 | fn on_start(self, map: F) -> OnStartSpec 39 | where 40 | F: FnMut(Self::Ref) -> T, 41 | { 42 | OnStartSpec::new(self, map) 43 | } 44 | 45 | fn into_dyn(self) -> BoxSpec 46 | where 47 | Self: Send + 'static, 48 | Self::Supervisee: Send, 49 | { 50 | BoxSpec::new(self) 51 | } 52 | } 53 | impl SpecificationExt for T {} 54 | 55 | pub trait SuperviseeExt: Supervisee { 56 | fn supervise(self) -> SupervisionFuture { 57 | SupervisionFuture(self) 58 | } 59 | } 60 | impl SuperviseeExt for S {} 61 | 62 | #[pin_project] 63 | pub struct SupervisionFuture(#[pin] S::Supervisee); 64 | 65 | impl SupervisionFuture { 66 | pub fn cancel(self) -> S::Supervisee { 67 | self.0 68 | } 69 | } 70 | 71 | impl Future for SupervisionFuture { 72 | type Output = SupervisionResult; 73 | 74 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 75 | self.project().0.poll_supervise(cx) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /zestors/examples/spawning.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | use tokio::time::sleep; 3 | use zestors::{messaging::RecvError, prelude::*}; 4 | 5 | // Let's start by writing an actor that receives messages until it is halted .. 6 | async fn inbox_actor(mut inbox: Inbox<()>) { 7 | loop { 8 | if let Err(RecvError::Halted) = inbox.recv().await { 9 | break (); 10 | } 11 | } 12 | } 13 | 14 | // .. and an actor that simply waits until it is halted. 15 | async fn halter_actor(halter: Halter) { 16 | halter.await 17 | } 18 | 19 | #[tokio::main] 20 | async fn main() { 21 | // The `Halter` takes `()` as its config .. 22 | let _ = spawn_with(Link::default(), (), halter_actor); 23 | // .. while an `Inbox` takes a `Capacity`. 24 | let _ = spawn_with(Link::default(), Capacity::default(), inbox_actor); 25 | 26 | // Let's spawn an actor with default parameters .. 27 | let (child, address) = spawn(inbox_actor); 28 | drop(child); 29 | sleep(Duration::from_millis(10)).await; 30 | // .. and when the child is dropped, the actor is aborted. 31 | assert!(address.has_exited()); 32 | 33 | // But if we spawn a detached child .. 34 | let (child, address) = spawn_with(Link::Detached, Capacity::default(), inbox_actor); 35 | drop(child); 36 | sleep(Duration::from_millis(10)).await; 37 | // .. the actor does not get halted. 38 | assert!(!address.has_exited()); 39 | 40 | // We can also spawn a multi-process actor with the `Inbox` .. 41 | let (mut child_pool, _address) = spawn_many(0..10, |i, inbox| async move { 42 | println!("Spawning process nr {i}"); 43 | inbox_actor(inbox).await 44 | }); 45 | // .. but that does not compile with a `Halter`. (use the `MultiHalter` instead) 46 | // let _ = spawn_many(0..10, |i, halter| async move { 47 | // println!("Spawning process nr {i}"); 48 | // halter_actor(halter).await 49 | // }); 50 | 51 | // We can now spawn additional processes onto the actor .. 52 | child_pool 53 | .spawn_onto(|_inbox: Inbox<()>| async move { () }) 54 | .unwrap(); 55 | 56 | // .. and that is also possible with a dynamic child-pool .. 57 | let mut child_pool = child_pool.into_dyn(); 58 | child_pool 59 | .try_spawn_onto(|_inbox: Inbox<()>| async move { () }) 60 | .unwrap(); 61 | 62 | // .. but fails if given the wrong inbox-type. 63 | child_pool 64 | .try_spawn_onto(|_inbox: MultiHalter| async move { unreachable!() }) 65 | .unwrap_err(); 66 | } 67 | -------------------------------------------------------------------------------- /zestors/src/spawning/link.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | 3 | #[allow(unused)] 4 | use crate::all::*; 5 | 6 | /// The [`Link`] of an actor specifies how it should behave when the [`Child`] is dropped. 7 | /// - If the link is [`Link::Detached`], then the actor will continue execution. 8 | /// - If the link is [`Link::Attached(Duration)`], then the actor will be shut-down with the 9 | /// given duration. 10 | /// 11 | /// # Default 12 | /// The default value for a link is `Link::Attached(get_default_shutdown_time)` 13 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 14 | pub enum Link { 15 | Detached, 16 | Attached(Duration), 17 | } 18 | 19 | impl Link { 20 | /// Set the link to [`Link::Attached(duration)`]. Returns the previous shutdown-time if 21 | /// it was attached before. 22 | pub fn attach(&mut self, mut duration: Duration) -> Option { 23 | match self { 24 | Link::Detached => { 25 | *self = Link::Attached(duration); 26 | None 27 | } 28 | Link::Attached(old_time) => { 29 | std::mem::swap(old_time, &mut duration); 30 | Some(duration) 31 | } 32 | } 33 | } 34 | 35 | /// Set the link to [`Link::Detached`]. Returns the shutdown-time if it was attached before. 36 | pub fn detach(&mut self) -> Option { 37 | match self { 38 | Link::Detached => { 39 | *self = Link::Detached; 40 | None 41 | } 42 | Link::Attached(_) => { 43 | let mut link = Link::Detached; 44 | std::mem::swap(self, &mut link); 45 | match link { 46 | Link::Attached(time) => Some(time), 47 | Link::Detached => unreachable!(), 48 | } 49 | } 50 | } 51 | } 52 | 53 | /// Whether the link is attached. 54 | pub fn is_attached(&self) -> bool { 55 | matches!(self, Link::Attached(_)) 56 | } 57 | 58 | pub fn into_duration_or_default(self) -> Duration { 59 | match self { 60 | Link::Detached => get_default_shutdown_time(), 61 | Link::Attached(duration) => duration, 62 | } 63 | } 64 | } 65 | 66 | impl Default for Link { 67 | fn default() -> Self { 68 | Link::Attached(get_default_shutdown_time()) 69 | } 70 | } 71 | 72 | impl From for Link { 73 | fn from(value: Duration) -> Self { 74 | Self::Attached(value.into()) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /zestors/src/actor_type/halter/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::all::*; 2 | use event_listener::EventListener; 3 | use futures::{ready, Future, FutureExt}; 4 | use std::{sync::Arc, task::Poll}; 5 | 6 | mod channel; 7 | pub use channel::*; 8 | 9 | /// A halter can be used for processes that do not handle any messages, but that should still be 10 | /// supervisable. The halter can be awaited and resolves when the task should halt. 11 | /// 12 | /// For a halter that can spawn multiple processes, see [`MultiHalter`]. 13 | pub struct Halter { 14 | channel: Arc, 15 | halt_event_listener: Option, 16 | } 17 | 18 | impl Halter { 19 | /// Whether this process should halt. 20 | pub fn halted(&self) -> bool { 21 | self.channel.halted() 22 | } 23 | } 24 | 25 | impl ActorType for Halter { 26 | type Channel = HalterChannel; 27 | } 28 | 29 | impl InboxType for Halter { 30 | type Config = (); 31 | 32 | fn init_single_inbox( 33 | _config: (), 34 | address_count: usize, 35 | actor_id: ActorId, 36 | ) -> (Arc, Self) { 37 | let channel = Arc::new(HalterChannel::new(address_count, actor_id)); 38 | ( 39 | channel.clone(), 40 | Self { 41 | channel, 42 | halt_event_listener: None, 43 | }, 44 | ) 45 | } 46 | } 47 | 48 | impl ActorRef for Halter { 49 | type ActorType = Self; 50 | fn channel_ref(this: &Self) -> &Arc<::Channel> { 51 | &this.channel 52 | } 53 | } 54 | 55 | impl Future for Halter { 56 | type Output = (); 57 | 58 | fn poll( 59 | mut self: std::pin::Pin<&mut Self>, 60 | cx: &mut std::task::Context<'_>, 61 | ) -> std::task::Poll { 62 | loop { 63 | // If we have already halted before, then return immeadeately.. 64 | if self.halted() { 65 | return Poll::Ready(()); 66 | } 67 | 68 | // Acquire a halt listener if not set. 69 | let listener = if let Some(listener) = &mut self.halt_event_listener { 70 | listener 71 | } else { 72 | self.halt_event_listener = Some(self.channel.get_halt_listener()); 73 | self.halt_event_listener.as_mut().unwrap() 74 | }; 75 | 76 | // If it is pending return, otherwise remove the listener. 77 | ready!(listener.poll_unpin(cx)); 78 | self.halt_event_listener = None; 79 | } 80 | } 81 | } 82 | 83 | impl Drop for Halter { 84 | fn drop(&mut self) { 85 | self.channel.exit(); 86 | } 87 | } 88 | 89 | impl Unpin for Halter {} 90 | -------------------------------------------------------------------------------- /zestors/examples/actor_type.rs: -------------------------------------------------------------------------------- 1 | use futures::stream::StreamExt; 2 | use zestors::{actor_reference::ExitError, messaging::RecvError, prelude::*}; 3 | 4 | // Let's start by creating a simple event-loop for our actor. 5 | async fn my_actor(mut inbox: Inbox<()>) -> &'static str { 6 | // This actor receives a single event only. 7 | match inbox.recv().await { 8 | Err(RecvError::ClosedAndEmpty) => "Closed and empty", 9 | Err(RecvError::Halted) => "Halt properly handled", 10 | Ok(_msg) => { 11 | panic!(r"\('o')/ This actor panics upon receiving a message!") 12 | } 13 | } 14 | } 15 | 16 | // We will now spawn the actor a bunch of times, but do different things with it to 17 | // show of different functionalities. 18 | #[tokio::main] 19 | async fn main() { 20 | // Halting an actor: 21 | let (child, address) = spawn(my_actor); 22 | child.halt(); 23 | assert!(matches!(child.await, Ok("Halt properly handled"))); 24 | assert_eq!(address.await, ()); 25 | 26 | // Shutting down an actor: 27 | let (mut child, address) = spawn(my_actor); 28 | child.shutdown(); 29 | assert!(matches!(child.await, Ok("Halt properly handled"))); 30 | assert_eq!(address.await, ()); 31 | 32 | // Aborting an actor: 33 | let (mut child, address) = spawn(my_actor); 34 | child.abort(); 35 | assert!(matches!(child.await, Err(ExitError::Abort))); 36 | assert_eq!(address.await, ()); 37 | 38 | // Closing the inbox: 39 | let (child, address) = spawn(my_actor); 40 | child.close(); 41 | assert!(matches!(child.await, Ok("Closed and empty"))); 42 | assert_eq!(address.await, ()); 43 | 44 | // Making it panic by sending a message: 45 | let (child, address) = spawn(my_actor); 46 | child.send(()).await.unwrap(); 47 | assert!(matches!(child.await, Err(ExitError::Panic(_)))); 48 | assert_eq!(address.await, ()); 49 | 50 | // Dropping the child: 51 | let (child, address) = spawn(my_actor); 52 | drop(child); 53 | assert_eq!(address.await, ()); 54 | 55 | // Halting a child-pool: 56 | let (child_pool, address) = spawn_many(0..10, |_, inbox| async move { my_actor(inbox).await }); 57 | address.halt(); 58 | child_pool 59 | .for_each(|process_exit| async move { 60 | assert!(matches!(process_exit, Ok("Halt properly handled"))); 61 | }) 62 | .await; 63 | assert_eq!(address.await, ()); 64 | 65 | // Shutting down a child-pool 66 | let (mut child_pool, address) = 67 | spawn_many(0..10, |_, inbox| async move { my_actor(inbox).await }); 68 | child_pool 69 | .shutdown() 70 | .for_each(|process_exit| async move { 71 | assert!(matches!(process_exit, Ok("Halt properly handled"))); 72 | }) 73 | .await; 74 | assert_eq!(address.await, ()); 75 | } 76 | -------------------------------------------------------------------------------- /zestors/src/messaging/protocol.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | 3 | use super::*; 4 | use std::any::TypeId; 5 | 6 | /// A [`Protocol`] defines exactly which [messages](Message) an actor [`Accepts`]. Normally this is derived with 7 | /// the [`macro@protocol`] macro. 8 | /// 9 | /// [`Protocol`] is automatically implemented for `()`, which accepts only `()`. 10 | pub trait Protocol: Send + 'static { 11 | /// Take out the payload as a [`BoxPayload`]. 12 | fn into_boxed_payload(self) -> BoxPayload; 13 | 14 | /// Attempt to create the [`Protocol`] from a message. 15 | /// 16 | /// # Implementation 17 | /// This should succeed if and only if the protocol implements [`FromPayload`]. 18 | fn try_from_boxed_payload(payload: BoxPayload) -> Result 19 | where 20 | Self: Sized; 21 | 22 | /// Whether the [`Protocol`] accepts the type-id of a [`Message`]. 23 | /// 24 | /// # Implementation 25 | /// Should return true if and only if the protocol implements [`FromPayload`]. 26 | fn accepts_msg(msg_id: &TypeId) -> bool 27 | where 28 | Self: Sized; 29 | } 30 | 31 | /// Specifies that a [`Protocol`] can be created from the [`Message::Payload`] of `M`. 32 | pub trait FromPayload: Protocol { 33 | /// Create the [`Protocol`] from the [`Message::Payload`]. 34 | fn from_payload(payload: M::Payload) -> Self 35 | where 36 | Self: Sized; 37 | 38 | /// Attempt to convert the protocol into the [`Message::Payload`] of `M`. 39 | fn try_into_payload(self) -> Result 40 | where 41 | Self: Sized; 42 | } 43 | 44 | //------------------------------------------------------------------------------------------------ 45 | // Protocol: `()` 46 | //------------------------------------------------------------------------------------------------ 47 | 48 | impl Protocol for () { 49 | fn into_boxed_payload(self) -> BoxPayload { 50 | BoxPayload::new::<()>(()) 51 | } 52 | 53 | fn try_from_boxed_payload(payload: BoxPayload) -> Result { 54 | payload.downcast::<()>() 55 | } 56 | 57 | fn accepts_msg(msg_id: &std::any::TypeId) -> bool { 58 | *msg_id == TypeId::of::<()>() 59 | } 60 | } 61 | 62 | #[async_trait] 63 | impl HandledBy for () 64 | where 65 | H: HandleMessage<()>, 66 | { 67 | async fn handle_with( 68 | self, 69 | handler: &mut H, 70 | state: &mut ::State, 71 | ) -> HandlerResult { 72 | handler.handle_msg(state, self).await 73 | } 74 | } 75 | 76 | impl FromPayload<()> for () { 77 | fn from_payload(payload: ()) -> Self 78 | where 79 | Self: Sized, 80 | { 81 | payload 82 | } 83 | 84 | fn try_into_payload(self) -> Result<(), Self> 85 | where 86 | Self: Sized, 87 | { 88 | Ok(self) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /zestors/examples/actor_reference.rs: -------------------------------------------------------------------------------- 1 | use zestors::{prelude::*, messaging::RecvError, actor_reference::ExitError}; 2 | use futures::stream::StreamExt; 3 | 4 | // Let's start by creating a simple event-loop for our actor. 5 | async fn my_actor(mut inbox: Inbox<()>) -> &'static str { 6 | // This actor receives a single event only. 7 | match inbox.recv().await { 8 | Err(RecvError::ClosedAndEmpty) => { 9 | "Closed and empty" 10 | } 11 | Err(RecvError::Halted) => { 12 | "Halt properly handled" 13 | } 14 | Ok(_msg) => { 15 | panic!(r"\('o')/ This actor panics upon receiving a message!") 16 | } 17 | } 18 | } 19 | 20 | // We will now spawn the actor a bunch of times, but do different things with it to 21 | // show of different functionalities. 22 | #[tokio::main] 23 | async fn main() { 24 | // Halting an actor: 25 | let (child, address) = spawn(my_actor); 26 | child.halt(); 27 | assert!(matches!(child.await, Ok("Halt properly handled"))); 28 | assert_eq!(address.await, ()); 29 | 30 | // Shutting down an actor: 31 | let (mut child, address) = spawn(my_actor); 32 | child.shutdown(); 33 | assert!(matches!(child.await, Ok("Halt properly handled"))); 34 | assert_eq!(address.await, ()); 35 | 36 | // Aborting an actor: 37 | let (mut child, address) = spawn(my_actor); 38 | child.abort(); 39 | assert!(matches!(child.await, Err(ExitError::Abort))); 40 | assert_eq!(address.await, ()); 41 | 42 | // Closing the inbox: 43 | let (child, address) = spawn(my_actor); 44 | child.close(); 45 | assert!(matches!(child.await, Ok("Closed and empty"))); 46 | assert_eq!(address.await, ()); 47 | 48 | // Making it panic by sending a message: 49 | let (child, address) = spawn(my_actor); 50 | child.send(()).await.unwrap(); 51 | assert!(matches!(child.await, Err(ExitError::Panic(_)))); 52 | assert_eq!(address.await, ()); 53 | 54 | // Dropping the child: 55 | let (child, address) = spawn(my_actor); 56 | drop(child); 57 | assert_eq!(address.await, ()); 58 | 59 | // Halting a child-pool: 60 | let (child_pool, address) = spawn_many(0..10, |_, inbox| async move { 61 | my_actor(inbox).await 62 | }); 63 | address.halt(); 64 | child_pool 65 | .for_each(|process_exit| async move { 66 | assert!(matches!(process_exit, Ok("Halt properly handled"))); 67 | }) 68 | .await; 69 | assert_eq!(address.await, ()); 70 | 71 | // Shutting down a child-pool 72 | let (mut child_pool, address) = spawn_many(0..10, |_, inbox| async move { 73 | my_actor(inbox).await 74 | }); 75 | child_pool 76 | .shutdown() 77 | .for_each(|process_exit| async move { 78 | assert!(matches!(process_exit, Ok("Halt properly handled"))); 79 | }) 80 | .await; 81 | assert_eq!(address.await, ()); 82 | } -------------------------------------------------------------------------------- /zestors/src/messaging/envelope.rs: -------------------------------------------------------------------------------- 1 | use crate::all::*; 2 | use futures::{future::BoxFuture, Future}; 3 | use std::sync::Arc; 4 | 5 | /// A [`Message`] that is ready to be sent. 6 | pub struct Envelope<'a, A: ActorType, M> { 7 | channel: &'a Arc, 8 | msg: M, 9 | } 10 | 11 | impl<'a, A, M> Envelope<'a, A, M> 12 | where 13 | A: ActorType + Accepts, 14 | M: Message, 15 | { 16 | /// Create a new envelope from the message and channel. 17 | pub fn new(channel: &'a Arc, msg: M) -> Self { 18 | Self { channel, msg } 19 | } 20 | 21 | /// Take out the inner message. 22 | pub fn into_msg(self) -> M { 23 | self.msg 24 | } 25 | 26 | /// Attempt to send the message. 27 | /// 28 | /// See [`crate::messaging`] for more information. 29 | pub fn try_send(self) -> Result> { 30 | >::try_send(&self.channel, self.msg) 31 | } 32 | 33 | /// Attempt to send the message. 34 | /// 35 | /// See [`crate::messaging`] for more information. 36 | pub fn force_send(self) -> Result> { 37 | >::force_send(&self.channel, self.msg) 38 | } 39 | 40 | /// Attempt to send the message. 41 | /// 42 | /// See [`crate::messaging`] for more information. 43 | pub fn send_blocking(self) -> Result> { 44 | >::send_blocking(&self.channel, self.msg) 45 | } 46 | 47 | /// Attempt to send the message. 48 | /// 49 | /// See [`crate::messaging`] for more information. 50 | pub fn send(self) -> >::SendFut<'a> { 51 | >::send(&self.channel, self.msg) 52 | } 53 | 54 | /// Attempt to send the message. 55 | /// 56 | /// See [`crate::messaging`] for more information. 57 | pub fn try_request(self) -> BoxFuture<'a, Result>> 58 | where 59 | M: Message + Send + 'static, 60 | F: Future> + Send, 61 | { 62 | >::try_request(&self.channel, self.msg) 63 | } 64 | 65 | /// Attempt to send the message. 66 | /// 67 | /// See [`crate::messaging`] for more information. 68 | pub fn force_request(self) -> BoxFuture<'a, Result>> 69 | where 70 | M: Message + Send + 'static, 71 | F: Future> + Send, 72 | { 73 | >::force_request(&self.channel, self.msg) 74 | } 75 | 76 | /// Attempt to send the message. 77 | /// 78 | /// See [`crate::messaging`] for more information. 79 | pub fn request(self) -> BoxFuture<'a, Result>> 80 | where 81 | M: Message + Send + 'static, 82 | F: Future> + Send, 83 | { 84 | >::request(&self.channel, self.msg) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /zestors-codegen/src/message.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::{Ident, TokenStream}; 2 | use quote::quote; 3 | use syn::{parse::Parse, parse2, Attribute, Error, Generics, Item, Type}; 4 | 5 | struct DeriveMessage { 6 | ident: Ident, 7 | attrs: Vec, 8 | generics: Generics, 9 | } 10 | 11 | impl Parse for DeriveMessage { 12 | fn parse(input: syn::parse::ParseStream) -> syn::Result { 13 | match input.parse::()? { 14 | Item::Enum(item) => Ok(DeriveMessage { 15 | ident: item.ident, 16 | attrs: item.attrs, 17 | generics: item.generics, 18 | }), 19 | Item::Struct(item) => Ok(DeriveMessage { 20 | ident: item.ident, 21 | attrs: item.attrs, 22 | generics: item.generics, 23 | }), 24 | Item::Union(item) => Ok(DeriveMessage { 25 | ident: item.ident, 26 | attrs: item.attrs, 27 | generics: item.generics, 28 | }), 29 | item => Err(Error::new_spanned(item, "Must be enum, struct or union")), 30 | } 31 | } 32 | } 33 | 34 | pub fn derive_message(item: TokenStream) -> Result { 35 | let DeriveMessage { 36 | ident, 37 | attrs, 38 | generics, 39 | } = parse2::(item)?; 40 | let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); 41 | 42 | let msg_type = get_msg_type(&attrs)?; 43 | 44 | Ok(quote! { 45 | impl #impl_generics ::zestors::messaging::Message for #ident #ty_generics #where_clause { 46 | type Returned = <#msg_type as ::zestors::messaging::MessageDerive>::Returned; 47 | type Payload = <#msg_type as ::zestors::messaging::MessageDerive>::Payload; 48 | fn create(self) -> (Self::Payload, Self::Returned) { 49 | <#msg_type as ::zestors::messaging::MessageDerive>::create(self) 50 | } 51 | fn cancel(payload: Self::Payload, returned: Self::Returned) -> Self { 52 | <#msg_type as ::zestors::messaging::MessageDerive>::cancel(payload, returned) 53 | } 54 | } 55 | }) 56 | } 57 | 58 | pub fn get_msg_type(attrs: &Vec) -> Result { 59 | let mut msg_type = if let Some(attr) = attrs.iter().find(|attr| attr.path.is_ident("msg")) { 60 | let ty = attr.parse_args::()?; 61 | Some(quote! { #ty }) 62 | } else { 63 | None 64 | }; 65 | 66 | if let Some(attr) = attrs.iter().find(|attr| attr.path.is_ident("request")) { 67 | if msg_type.is_some() { 68 | Err(Error::new_spanned( 69 | attr, 70 | "Can't have both #[msg(..)] and #[request(..)]", 71 | ))? 72 | } 73 | let ty = attr.parse_args::()?; 74 | msg_type = Some(quote! { ::zestors::messaging::Rx<#ty> }) 75 | }; 76 | 77 | Ok(msg_type.unwrap_or(quote! { () })) 78 | } 79 | -------------------------------------------------------------------------------- /zestors/src/messaging/accepts.rs: -------------------------------------------------------------------------------- 1 | use crate::all::*; 2 | use futures::{future::BoxFuture, Future}; 3 | 4 | /// [`Accepts`] is implemented for any [`ActorType`] that accepts the [`Message`] `M`. 5 | pub trait Accepts: ActorType { 6 | type SendFut<'a>: Future>> + Send; 7 | fn try_send(channel: &Self::Channel, msg: M) -> Result>; 8 | fn force_send(channel: &Self::Channel, msg: M) -> Result>; 9 | fn send_blocking(channel: &Self::Channel, msg: M) -> Result>; 10 | fn send(channel: &Self::Channel, msg: M) -> Self::SendFut<'_>; 11 | } 12 | 13 | /// Automatically implemented methods for any type that implements [`Accepts`] 14 | pub trait AcceptsExt: Accepts { 15 | fn try_request( 16 | channel: &Self::Channel, 17 | msg: M, 18 | ) -> BoxFuture<'_, Result>> 19 | where 20 | M: Message + Send + 'static, 21 | F: Future> + Send, 22 | { 23 | Box::pin(async move { 24 | match Self::try_send(channel, msg) { 25 | Ok(rx) => match rx.await { 26 | Ok(msg) => Ok(msg), 27 | Err(e) => Err(TryRequestError::NoReply(e)), 28 | }, 29 | Err(TrySendError::Closed(msg)) => Err(TryRequestError::Closed(msg)), 30 | Err(TrySendError::Full(msg)) => Err(TryRequestError::Full(msg)), 31 | } 32 | }) 33 | } 34 | 35 | fn force_request( 36 | channel: &Self::Channel, 37 | msg: M, 38 | ) -> BoxFuture<'_, Result>> 39 | where 40 | M: Message + Send + 'static, 41 | F: Future> + Send, 42 | { 43 | Box::pin(async move { 44 | match Self::force_send(channel, msg) { 45 | Ok(rx) => match rx.await { 46 | Ok(msg) => Ok(msg), 47 | Err(e) => Err(TryRequestError::NoReply(e)), 48 | }, 49 | Err(TrySendError::Closed(msg)) => Err(TryRequestError::Closed(msg)), 50 | Err(TrySendError::Full(msg)) => Err(TryRequestError::Full(msg)), 51 | } 52 | }) 53 | } 54 | 55 | fn request( 56 | channel: &Self::Channel, 57 | msg: M, 58 | ) -> BoxFuture<'_, Result>> 59 | where 60 | M: Message + Send + 'static, 61 | F: Future> + Send, 62 | { 63 | Box::pin(async move { 64 | match Self::send(channel, msg).await { 65 | Ok(rx) => match rx.await { 66 | Ok(msg) => Ok(msg), 67 | Err(e) => Err(RequestError::NoReply(e)), 68 | }, 69 | Err(SendError(msg)) => Err(RequestError::Closed(msg)), 70 | } 71 | }) 72 | } 73 | } 74 | impl AcceptsExt for T where T: Accepts {} 75 | -------------------------------------------------------------------------------- /zestors/src/handler/state.rs: -------------------------------------------------------------------------------- 1 | use crate::all::*; 2 | use futures::Future; 3 | use std::{ 4 | marker::PhantomData, 5 | pin::Pin, 6 | task::{Context, Poll}, 7 | }; 8 | 9 | /// The handler-state generates [`HandlerItem`]s that the [`Handler`] `H` can handle. 10 | pub trait HandlerState: ActorRef + Unpin + Send { 11 | /// The [`Protocol`] of this state. 12 | type Protocol: Protocol + HandledBy; 13 | 14 | /// The [`InboxType`] of this state. 15 | type InboxType: InboxType; 16 | 17 | /// Create the state from the [`Self::InboxType`]. 18 | fn from_inbox(inbox: Self::InboxType) -> Self; 19 | 20 | /// Poll the next [`HandlerItem`]. 21 | /// 22 | /// This method is very similar to [`futures::Stream::poll_next`], with the main difference that this 23 | /// may continue being polled even if [`Event::Dead`] is returned. 24 | fn poll_next_item( 25 | self: Pin<&mut Self>, 26 | cx: &mut Context, 27 | ) -> Poll>; 28 | } 29 | 30 | /// An item returned from a [`HandlerState`]. 31 | pub enum HandlerItem { 32 | Protocol(P), 33 | Action(Action), 34 | Event(Event), 35 | HandlerResult(Result, S::Exception>), 36 | } 37 | 38 | impl HandlerItem { 39 | pub fn is_dead(&self) -> bool { 40 | if let Self::Event(Event::Dead) = self { 41 | true 42 | } else { 43 | false 44 | } 45 | } 46 | } 47 | 48 | impl HandlerItem 49 | where 50 | H: Handler, 51 | P: HandledBy, 52 | { 53 | /// Handle the item with [`Handler`] `H`. 54 | pub async fn handle_with(self, handler: &mut H, state: &mut H::State) -> HandlerResult { 55 | match self { 56 | HandlerItem::Event(event) => handler.handle_event(state, event).await, 57 | HandlerItem::Action(action) => action.handle_with(handler, state).await, 58 | HandlerItem::Protocol(protocol) => protocol.handle_with(handler, state).await, 59 | HandlerItem::HandlerResult(result) => result, 60 | } 61 | } 62 | } 63 | 64 | /// An extension to [`HandlerState`]. 65 | pub trait HandlerStateExt: HandlerState { 66 | /// Returns a future that resolves to the next [`HandlerItem`]. 67 | fn next_handler_item(&mut self) -> NextHandlerItem<'_, Self, H> 68 | where 69 | Self: Unpin, 70 | { 71 | NextHandlerItem(self, PhantomData) 72 | } 73 | } 74 | impl, H: Handler> HandlerStateExt for T {} 75 | 76 | /// A future that resolves to a [`HandlerItem`] 77 | pub struct NextHandlerItem<'a, S: HandlerState, H: Handler>(&'a mut S, PhantomData); 78 | 79 | impl<'a, S: HandlerState, H: Handler> Unpin for NextHandlerItem<'a, S, H> {} 80 | 81 | impl<'a, S: HandlerState + Unpin, H: Handler> Future for NextHandlerItem<'a, S, H> { 82 | type Output = HandlerItem; 83 | 84 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 85 | Pin::new(&mut *self.0).poll_next_item(cx) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /zestors/examples/messaging.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused)] 2 | use zestors::prelude::*; 3 | #[macro_use] 4 | extern crate zestors; 5 | 6 | // First we will define two different messages. 7 | // A simple message .. 8 | #[derive(Message, Envelope, Debug, Clone)] 9 | struct MyMessage { 10 | param1: String, 11 | param2: u32, 12 | } 13 | 14 | // .. and a request. 15 | #[derive(Message, Envelope, Debug, Clone)] 16 | #[request(u32)] 17 | struct MyRequest { 18 | param1: String, 19 | param2: u32, 20 | } 21 | 22 | // Now we are ready to define our protocol! 23 | // This protocol accepts three messages: `MyMessage`, `MyRequest` and `u32`. 24 | #[protocol] 25 | enum MyProtocol { 26 | Msg1(MyMessage), 27 | Msg2(MyRequest), 28 | U32(u32), 29 | } 30 | // That is our actor-definition done. 31 | 32 | // We can now start using it! 33 | #[tokio::main] 34 | async fn main() { 35 | // Let's spawn a basic actor that just prints any messages it receives .. 36 | let (child, address) = spawn(|mut inbox: Inbox| async move { 37 | loop { 38 | match inbox.recv().await.unwrap() { 39 | MyProtocol::Msg1(msg1) => { 40 | println!("Received: {:?}", msg1); 41 | } 42 | MyProtocol::Msg2((msg2, tx)) => { 43 | println!("Received: {:?}", msg2); 44 | tx.send(msg2.param2 + 10).unwrap(); 45 | } 46 | MyProtocol::U32(msg_u32) => { 47 | println!("Received: {:?}", msg_u32); 48 | } 49 | } 50 | } 51 | }); 52 | 53 | let my_msg = MyMessage { 54 | param1: "hi".to_string(), 55 | param2: 10, 56 | }; 57 | let my_request = MyRequest { 58 | param1: "hi".to_string(), 59 | param2: 10, 60 | }; 61 | 62 | // .. and send it some messages! 63 | address.send(10u32).await.unwrap(); 64 | address.send(my_msg.clone()).await.unwrap(); 65 | 66 | // We can also request (with boilerplate) .. 67 | let reply: Rx = address.send(my_request.clone()).await.unwrap(); 68 | assert_eq!(20, reply.await.unwrap()); 69 | 70 | // .. or do it without the boilerplate! 71 | assert_eq!(20, address.request(my_request.clone()).await.unwrap()); 72 | 73 | // It is also possible to send a message by creating an envelope and sending that .. 74 | address.envelope(my_msg.clone()).send().await.unwrap(); 75 | address 76 | .envelope(my_request.clone()) 77 | .request() 78 | .await 79 | .unwrap(); 80 | 81 | // .. or directly by using our derived Envelope trait! 82 | address 83 | .my_message("hi".to_string(), 10) 84 | .send() 85 | .await 86 | .unwrap(); 87 | address 88 | .my_request("hi".to_string(), 10) 89 | .request() 90 | .await 91 | .unwrap(); 92 | 93 | // As a final example, we can use the Accepts-bound on a function: 94 | send_using_accepts(&address, my_msg.clone()).await; 95 | } 96 | 97 | async fn send_using_accepts(address: &Address>, msg: MyMessage) { 98 | address.send(msg).await.unwrap(); 99 | } -------------------------------------------------------------------------------- /zestors/src/messaging/errors.rs: -------------------------------------------------------------------------------- 1 | use thiserror::Error; 2 | 3 | /// Error returned when trying to send a message, checking at compile-time that 4 | /// the message is actually accepted by the actor. 5 | #[derive(Debug, thiserror::Error, Clone, PartialEq, Eq, Hash)] 6 | pub enum TrySendCheckedError { 7 | Full(M), 8 | Closed(M), 9 | NotAccepted(M), 10 | } 11 | 12 | /// Error returned when sending a message, checking at compile-time that 13 | /// the message is actually accepted by the actor. 14 | #[derive(Debug, thiserror::Error, Clone, PartialEq, Eq, Hash)] 15 | pub enum SendCheckedError { 16 | Closed(M), 17 | NotAccepted(M), 18 | } 19 | 20 | /// An error returned when trying to send a message. 21 | #[derive(Debug, Clone, PartialEq, Eq, Hash, Error)] 22 | pub enum TrySendError { 23 | /// The channel has been closed, and no longer accepts new messages. 24 | #[error("Couldn't send message because Channel is closed")] 25 | Closed(M), 26 | /// The channel is full. 27 | #[error("Couldn't send message because Channel is full")] 28 | Full(M), 29 | } 30 | 31 | /// The channel has been closed, and no longer accepts new messages. 32 | #[derive(Debug, Clone, PartialEq, Eq, Hash, Error)] 33 | pub struct SendError(pub M); 34 | 35 | /// Error returned when sending a request. 36 | /// 37 | /// This error combines failures in sending and receiving. 38 | #[derive(Debug, thiserror::Error, Clone, Copy, PartialEq, Eq)] 39 | pub enum RequestError { 40 | NoReply(E), 41 | Closed(M), 42 | } 43 | 44 | /// Error returned when trying to send a request. 45 | /// 46 | /// This error combines failures in sending and receiving. 47 | #[derive(Debug, Error, Clone, Copy, PartialEq, Eq)] 48 | pub enum TryRequestError { 49 | NoReply(E), 50 | Closed(M), 51 | Full(M), 52 | } 53 | 54 | /// Error returned when receiving a message. 55 | #[derive(Debug, Clone, PartialEq, Eq, Hash, Error)] 56 | pub enum RecvError { 57 | /// Process has been halted and should now exit. 58 | #[error("Couldn't receive because the process has been halted")] 59 | Halted, 60 | /// Channel has been closed, and contains no more messages. It is impossible for new 61 | /// messages to be sent to the channel. 62 | #[error("Couldn't receive becuase the channel is closed and empty")] 63 | ClosedAndEmpty, 64 | } 65 | 66 | /// Error returned when trying to receive a message. 67 | #[derive(Debug, Clone, PartialEq, Eq, Hash, Error)] 68 | pub enum TryRecvError { 69 | /// Process has been halted and should now exit. 70 | #[error("Couldn't receive because the process has been halted")] 71 | Halted, 72 | /// The channel is empty, but is not yet closed. New messges may arrive. 73 | #[error("Couldn't receive because the channel is empty")] 74 | Empty, 75 | /// Channel has been closed, and contains no more messages. It is impossible for new 76 | /// messages to be sent to the channel. 77 | #[error("Couldn't receive becuase the channel is closed and empty")] 78 | ClosedAndEmpty, 79 | } 80 | 81 | /// This process has been halted and should now exit. 82 | #[derive(Debug, Clone, PartialEq, Eq, Hash, Error)] 83 | #[error("Process has been halted")] 84 | pub struct Halted; 85 | -------------------------------------------------------------------------------- /zestors/src/actor_type/mod.rs: -------------------------------------------------------------------------------- 1 | //! # Overview 2 | //! The [`ActorType`] of a [`Child<_, A, _>`] or [`Address`] specifies what type of actor it refers 3 | //! to. Some of the things it specifies are the messages the actor can accept and the type of [`Channel`] used. 4 | //! 5 | //! An [`ActorType`] can be specified in two different ways: 6 | //! 1) As the [`InboxType`] the actor is spawned with. Some examples of built-in inboxes are the [`Inbox`] 7 | //! and the [`Halter`]. An actor can choose what inbox it is spawned with, and new inboxes can be created in third-party 8 | //! crates. 9 | //! 2) As a [`DynActor`](struct@DynActor) where `T` is one of the [`dyn_types`], usually written with [`DynActor!`]. 10 | //! The actor is not specified by it's inbox, but instead by the messages it [`Accepts`]. 11 | //! 12 | //! # `DynActor!` macro 13 | //! Writing [`DynActor`](struct@DynActor) types can become complicated, nstead of writing and remembering these types, 14 | //! use the [`DynActor!`] macro to specify the [`ActorType`]: 15 | //! - `DynActor!()` = `DynActor` 16 | //! - `DynActor!(u32)` = `DynActor>` 17 | //! - `DynActor!(u32, u64)` = `DynActor>` 18 | //! - etc. 19 | //! 20 | //! # Transforming actor-types 21 | //! Any [`ActorRef`] that implements [`Transformable`] allows it's [`ActorType`] to be transformed into and 22 | //! from a dynamic one. This allows references to different inbox-types to be transformed into ones of the same 23 | //! actor-type. 24 | //! 25 | //! [`Transformable`] is implemented for an [`Address`] and [`Child<_, A, _>`], which allows them to be transformed 26 | //! into an `Address` and `Child<_, T, _>` as long as [`A: TransformInto`](TransformInto). 27 | //! Some examples with the default inbox-types: 28 | //! - A [`Halter`] can be transformed into a `DynActor!()`. 29 | //! - An [`Inbox

`] can be transformed into a `DynActor!(M1 .. Mn)` as long as `P` implements 30 | //! [`FromPayload`] for `M in [M1 .. Mn]`. 31 | //! - A [`DynActor!(M1 .. Mn)`](DynActor!) can be transformed into a `DynActor!(T1 .. Tm)` as long 32 | //! as [`T1 .. Tn`] ⊆ [`M1 .. Mm`]. 33 | //! 34 | //! For these examples transformation can be done with [`Transformable::transform_into`] with transformations 35 | //! checked at compile-time. Transformations can also be checked at run-time (or not at all) 36 | //! with [`Transformable::try_transform_into`] and [`Transformable::transform_unchecked_into`]. 37 | //! 38 | //! A [`DynActor`](struct@DynActor) can be downcast into the original [`InboxType`] with [`Transformable::downcast`]. 39 | //! 40 | //! All addresses that can be transformed implement [`IntoAddress`] and all children [`IntoChild`]. 41 | //! 42 | //! | __<--__ [`actor_reference`](crate::actor_reference) | [`spawning`](crate::spawning) __-->__ | 43 | //! |---|---| 44 | //! 45 | //! # Example 46 | //! ``` 47 | #![doc = include_str!("../../examples/actor_type.rs")] 48 | //! ``` 49 | 50 | #[allow(unused)] 51 | use crate::all::*; 52 | 53 | mod actor_id; 54 | mod actor_type; 55 | mod channel; 56 | mod dyn_actor; 57 | mod errors; 58 | mod halter; 59 | mod inbox; 60 | mod multi_halter; 61 | pub use { 62 | actor_id::*, actor_type::*, channel::*, dyn_actor::*, errors::*, halter::*, 63 | inbox::*, multi_halter::*, 64 | }; 65 | 66 | pub mod dyn_types; 67 | -------------------------------------------------------------------------------- /supervision/src/supervision/handler_spec.rs: -------------------------------------------------------------------------------- 1 | use crate::all::*; 2 | use async_trait::async_trait; 3 | use futures::{future::BoxFuture, FutureExt}; 4 | use std::{ 5 | marker::PhantomData, 6 | pin::Pin, 7 | task::{ready, Context, Poll}, 8 | time::Duration, 9 | }; 10 | 11 | pub struct HandlerSpec { 12 | init: I, 13 | handler: PhantomData, 14 | } 15 | 16 | impl HandlerSpec 17 | where 18 | H: HandleRestart, 19 | I: Send + 'static, 20 | { 21 | pub fn new(init: I) -> Self { 22 | Self { 23 | init, 24 | handler: PhantomData, 25 | } 26 | } 27 | } 28 | 29 | #[async_trait] 30 | impl Specification for HandlerSpec 31 | where 32 | H: HandleRestart, 33 | I: Send + 'static, 34 | { 35 | type Ref = H::Ref; 36 | type Supervisee = HandlerSupervisee; 37 | 38 | async fn start_supervised(self) -> StartResult { 39 | match H::start(self.init).await { 40 | Ok((child, reference)) => Ok(( 41 | HandlerSupervisee { 42 | child, 43 | init: PhantomData, 44 | restart_fut: None, 45 | }, 46 | reference, 47 | )), 48 | Err(e) => match H::handle_restart(RestartReason::InitError(e)).await { 49 | Ok(Some(init)) => Err(StartError::StartFailed(Self { 50 | init, 51 | handler: PhantomData, 52 | })), 53 | Ok(None) => Err(StartError::Completed), 54 | Err(e) => Err(StartError::Fatal(e)), 55 | }, 56 | } 57 | } 58 | } 59 | 60 | pub struct HandlerSupervisee { 61 | restart_fut: Option, FatalError>>>, 62 | child: Child>, 63 | init: PhantomData, 64 | } 65 | 66 | impl Unpin for HandlerSupervisee {} 67 | 68 | impl Supervisee for HandlerSupervisee 69 | where 70 | H: HandleRestart, 71 | I: Send + 'static, 72 | { 73 | type Spec = HandlerSpec; 74 | 75 | fn poll_supervise( 76 | mut self: Pin<&mut Self>, 77 | cx: &mut Context, 78 | ) -> Poll> { 79 | loop { 80 | if let Some(restart_fut) = &mut self.restart_fut { 81 | break restart_fut.poll_unpin(cx).map(|res| match res { 82 | Ok(Some(init)) => Ok(Some(HandlerSpec { 83 | init, 84 | handler: PhantomData, 85 | })), 86 | Ok(None) => Ok(None), 87 | Err(e) => Err(e), 88 | }); 89 | } else { 90 | let exit = ready!(self.child.poll_unpin(cx)); 91 | self.restart_fut = Some(H::handle_restart(RestartReason::Exit(exit))); 92 | } 93 | } 94 | } 95 | 96 | fn shutdown_time(self: Pin<&Self>) -> Duration { 97 | self.child.link().into_duration_or_default() 98 | } 99 | 100 | fn halt(self: Pin<&mut Self>) { 101 | self.child.halt() 102 | } 103 | 104 | fn abort(mut self: Pin<&mut Self>) { 105 | self.child.abort(); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /zestors/src/actor_type/multi_halter/mod.rs: -------------------------------------------------------------------------------- 1 | mod channel; 2 | pub use channel::*; 3 | 4 | use event_listener::EventListener; 5 | use futures::{ready, Future, FutureExt}; 6 | use std::{sync::Arc, task::Poll}; 7 | use crate::all::*; 8 | 9 | /// A halter can be used for processes that do not handle any messages, but that should still be 10 | /// supervisable. The halter can be awaited and resolves when the task should halt. 11 | /// 12 | /// For a halter that can only spawn a single processes, see [`Halter`]. 13 | pub struct MultiHalter { 14 | channel: Arc, 15 | halt_event_listener: Option, 16 | halted: bool, 17 | } 18 | 19 | impl MultiHalter { 20 | /// Whether this task has been halted. 21 | pub fn halted(&self) -> bool { 22 | self.halted 23 | } 24 | } 25 | 26 | impl ActorType for MultiHalter { 27 | type Channel = MultiHalterChannel; 28 | } 29 | 30 | impl ActorRef for MultiHalter { 31 | type ActorType = Self; 32 | fn channel_ref(this: &Self) -> &Arc<::Channel> { 33 | &this.channel 34 | } 35 | } 36 | 37 | impl MultiProcessInbox for MultiHalter { 38 | fn init_multi_inbox( 39 | _config: Self::Config, 40 | process_count: usize, 41 | address_count: usize, 42 | actor_id: ActorId, 43 | ) -> Arc { 44 | Arc::new(MultiHalterChannel::new(address_count, process_count, actor_id)) 45 | } 46 | 47 | fn from_channel(channel: Arc) -> Self { 48 | Self { 49 | channel, 50 | halt_event_listener: None, 51 | halted: false, 52 | } 53 | } 54 | } 55 | 56 | impl InboxType for MultiHalter { 57 | type Config = (); 58 | 59 | fn init_single_inbox( 60 | config: Self::Config, 61 | address_count: usize, 62 | actor_id: ActorId, 63 | ) -> (Arc, Self) { 64 | let channel = Self::init_multi_inbox(config, 1, address_count, actor_id); 65 | (channel.clone(), Self::from_channel(channel)) 66 | } 67 | 68 | 69 | } 70 | 71 | impl Unpin for MultiHalter {} 72 | 73 | impl Future for MultiHalter { 74 | type Output = (); 75 | 76 | fn poll( 77 | mut self: std::pin::Pin<&mut Self>, 78 | cx: &mut std::task::Context<'_>, 79 | ) -> std::task::Poll { 80 | // If we have already halted before, then return immeadeately.. 81 | if self.halted { 82 | return Poll::Ready(()); 83 | } 84 | loop { 85 | // Acquire a halt listener if not set. 86 | let listener = if let Some(listener) = &mut self.halt_event_listener { 87 | listener 88 | } else { 89 | self.halt_event_listener = Some(self.channel.get_halt_listener()); 90 | self.halt_event_listener.as_mut().unwrap() 91 | }; 92 | 93 | // If it is pending return, otherwise remove the listener. 94 | ready!(listener.poll_unpin(cx)); 95 | self.halt_event_listener = None; 96 | 97 | if self.channel.should_halt() { 98 | break Poll::Ready(()); 99 | } 100 | } 101 | } 102 | } 103 | 104 | impl Drop for MultiHalter { 105 | fn drop(&mut self) { 106 | self.channel.decrement_halter_count(1) 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /zestors/src/actor_reference/mod.rs: -------------------------------------------------------------------------------- 1 | //! # Overview 2 | //! An actor reference is anything that implements [`ActorRef`]; examples include [`Child`], 3 | //! [`Address`], [`Inbox`] and [`Halter`]. An actor-reference can be used to interact with the actor: 4 | //! You can for example send messages, close the inbox or halt it using the [`ActorRefExt`] trait. 5 | //! 6 | //! When an actor is spawned, it returns tuple of a [`Child`] and [`Address`]. The child is a unique 7 | //! reference to the actor similar to a [tokio JoinHandle](tokio::task::JoinHandle). By default, when 8 | //! the child is dropped the actor is shut down, therefore it can be used to build supervision-trees. If 9 | //! the actor is [detached](Child::detach) then the actor won't be shut-down upon dropping the child. 10 | //! The address is a cloneable reference to the actor that can be shared with other processes to allow 11 | //! them to communicate. 12 | //! 13 | //! # Monitoring 14 | //! An actor can be monitored using it's [`Child`] or [`Address`] by awaiting them. When the actor exits, 15 | //! it will notify the child and address and they return a value; a `Child` returns a 16 | //! [`Result`](ExitError), while an `Address<_>` returns `()`. When monitoring a 17 | //! [`ChildPool`], instead of returning a single `Result`, a [`Stream`](futures::Stream) 18 | //! of these values is returned. 19 | //! 20 | //! # Stopping an actor 21 | //! An actor can be stopped in three different ways: 22 | //! 23 | //! - __Halting:__ An actor can be halted using its [`Child`], [`Address`] or anything else that 24 | //! implements [`ActorRef`]. When the actor is halted it should clean up it's state and then exit gracefully. 25 | //! 26 | //! - __Aborting:__ An actor can be aborted with [`Child::abort`]. Aborting will forcefully interrupt 27 | //! the process at its first `.await` point, and does not allow it to clean up it's state before exiting. 28 | //! ([see tokio abort](tokio::task::JoinHandle::abort)) 29 | //! 30 | //! - __Shutting down:__ An actor can be shut down using its [`Child::shutdown`]. This will first attempt to 31 | //! halt the actor until a certain amout of time has passed, and if the actor has not exited by that 32 | //! point it is aborted instead. This is the advised way of shutting down an actor in most cases. 33 | //! 34 | //! # Actor state 35 | //! The state of an actor can be queried from any [`ActorRef`] with four different methods: 36 | //! - `has_exited`: Returns true if all [inboxes](`InboxType`) have been dropped. 37 | //! - `is_closed`: Returns true if the channel has been closed and does not accept new messages. 38 | //! - `is_aborted`: Returns true if the actor has been aborted. (Only available on a [`Child`]) 39 | //! - `is_finished`: Returns true if all underlying tasks have finished. (Only available on a [`Child`]) 40 | //! 41 | //! # Child or ChildPool 42 | //! When specifying a [`Child<_, _, C>`], the argument `C` is the [`ChildType`]. This specifies 43 | //! whether the child is a single [`Child`] or [`ChildPool`]. A child can be converted into a child-pool 44 | //! using [`Child::into_pool`]. 45 | //! - `Child<_, _>` = `Child<_, _, SingleProcess>` 46 | //! - `ChildPool<_, _>` = `Child<_, _, MultiProcess>` 47 | //! 48 | //! | __<--__ [`messaging`] | [`actor_type`] __-->__ | 49 | //! |---|---| 50 | //! 51 | //! # Example 52 | //! ``` 53 | #![doc = include_str!("../../examples/actor_reference.rs")] 54 | //! ``` 55 | 56 | #[allow(unused)] 57 | use crate::{all::*, *}; 58 | 59 | mod actor_ref; 60 | mod address; 61 | mod child; 62 | mod child_type; 63 | mod shutdown; 64 | pub use actor_ref::*; 65 | pub use address::*; 66 | pub use child::*; 67 | pub use child_type::*; 68 | pub use shutdown::*; 69 | -------------------------------------------------------------------------------- /zestors/examples/handler.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate zestors; 3 | use zestors::{export::async_trait, prelude::*}; 4 | 5 | // Let's start by defining a message .. 6 | #[derive(Message, Debug)] 7 | #[request(u32)] 8 | pub struct PrintString { 9 | val: String, 10 | } 11 | 12 | // .. and a protocol. 13 | #[protocol] 14 | #[derive(Debug)] 15 | pub enum MyProtocol { 16 | A(u32), 17 | B(PrintString), 18 | C(Action), 19 | } 20 | 21 | // Now we can define our handler .. 22 | #[derive(Debug)] 23 | pub struct MyHandler { 24 | handled: u32, 25 | } 26 | 27 | // .. and implement the main trait `Handler` (or use #[derive(Handler)]) 28 | #[async_trait] 29 | impl Handler for MyHandler { 30 | type State = Inbox; 31 | type Exception = eyre::Report; 32 | type Stop = (); 33 | type Exit = u32; 34 | 35 | async fn handle_exit( 36 | self, 37 | _state: &mut Self::State, 38 | reason: Result, 39 | ) -> ExitFlow { 40 | match reason { 41 | // Upon ok, we exit normally. 42 | Ok(()) => ExitFlow::Exit(self.handled), 43 | // When an error occured, we also exit but log it. 44 | Err(exception) => { 45 | println!("[ERROR] Actor exited with an exception: {exception}"); 46 | ExitFlow::Exit(self.handled) 47 | } 48 | } 49 | } 50 | 51 | async fn handle_event(&mut self, state: &mut Self::State, event: Event) -> HandlerResult { 52 | // For most events we stop our actor. 53 | // When the actor is halted we just close the inbox and continue until it is empty. 54 | match event { 55 | Event::Halted => { 56 | state.close(); 57 | Ok(Flow::Continue) 58 | } 59 | Event::ClosedAndEmpty => Ok(Flow::Stop(())), 60 | Event::Dead => Ok(Flow::Stop(())), 61 | } 62 | } 63 | } 64 | 65 | // Let's handle a u32 message .. 66 | #[async_trait] 67 | impl HandleMessage for MyHandler { 68 | async fn handle_msg( 69 | &mut self, 70 | _state: &mut Self::State, 71 | msg: u32, 72 | ) -> Result, Self::Exception> { 73 | self.handled += msg; 74 | Ok(Flow::Continue) 75 | } 76 | } 77 | 78 | // .. and our custom request. 79 | #[async_trait] 80 | impl HandleMessage for MyHandler { 81 | async fn handle_msg( 82 | &mut self, 83 | _state: &mut Self::State, 84 | (msg, tx): (PrintString, Tx), 85 | ) -> Result, Self::Exception> { 86 | println!("{}", msg.val); 87 | let _ = tx.send(self.handled); 88 | self.handled += 1; 89 | Ok(Flow::Continue) 90 | } 91 | } 92 | 93 | // That was all! 94 | #[tokio::main] 95 | async fn main() { 96 | // Let's spawn our actor: 97 | let (child, address) = MyHandler { handled: 0 }.spawn(); 98 | // We can send it a basic message: 99 | address.send(10u32).await.unwrap(); 100 | 101 | // Send it a bunch of requests that will print the message. 102 | for i in 0..10 { 103 | let response = address 104 | .request(PrintString { 105 | val: String::from("Printing a message"), 106 | }) 107 | .await 108 | .unwrap(); 109 | println!("Got response {i} = {response}"); 110 | } 111 | 112 | // Or send it a custom closure with the `action!` macro 113 | child 114 | .send(action!(|handler: &mut MyHandler, _state| async move { 115 | println!("This is now 20: `{}`", handler.handled); 116 | Ok(Flow::Continue) 117 | })) 118 | .await 119 | .unwrap(); 120 | 121 | // And finally our actor can be halted and will exit! 122 | child.halt(); 123 | assert!(matches!(child.await, Ok(20))); 124 | } 125 | -------------------------------------------------------------------------------- /supervision/src/supervision/combinators/on_start_spec.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | marker::PhantomData, 3 | pin::Pin, 4 | task::{Context, Poll}, 5 | time::Duration, 6 | }; 7 | 8 | use super::*; 9 | use async_trait::async_trait; 10 | use pin_project::pin_project; 11 | 12 | //------------------------------------------------------------------------------------------------ 13 | // Specification 14 | //------------------------------------------------------------------------------------------------ 15 | 16 | #[pin_project] 17 | pub struct OnStartSpec { 18 | inner_spec: S, 19 | on_start: Fun, 20 | phantom: PhantomData T>, 21 | } 22 | 23 | impl OnStartSpec 24 | where 25 | S: Specification, 26 | F: FnMut(S::Ref) -> T, 27 | { 28 | pub fn new(spec: S, on_start: F) -> Self { 29 | Self { 30 | inner_spec: spec, 31 | on_start, 32 | phantom: PhantomData, 33 | } 34 | } 35 | } 36 | 37 | #[async_trait] 38 | impl Specification for OnStartSpec 39 | where 40 | Sp: Specification, 41 | F: FnMut(Sp::Ref) -> T + Send + 'static, 42 | T: Send + 'static, 43 | { 44 | type Ref = T; 45 | type Supervisee = OnStartSupervisee; 46 | 47 | async fn start_supervised(mut self) -> StartResult { 48 | match self.inner_spec.start_supervised().await { 49 | Ok((supervisee, reference)) => { 50 | let reference = (self.on_start)(reference); 51 | Ok(( 52 | OnStartSupervisee { 53 | supervisee, 54 | on_start: Some(self.on_start), 55 | phantom: self.phantom, 56 | }, 57 | reference, 58 | )) 59 | } 60 | Err(StartError::StartFailed(inner_spec)) => { 61 | Err(StartError::StartFailed(OnStartSpec { 62 | inner_spec, 63 | on_start: self.on_start, 64 | phantom: self.phantom, 65 | })) 66 | } 67 | Err(StartError::Completed) => Err(StartError::Completed), 68 | Err(StartError::Fatal(e)) => { 69 | Err(StartError::Fatal(e)) 70 | } 71 | } 72 | } 73 | } 74 | 75 | //------------------------------------------------------------------------------------------------ 76 | // Supervisee 77 | //------------------------------------------------------------------------------------------------ 78 | 79 | #[pin_project] 80 | pub struct OnStartSupervisee 81 | where 82 | S: Specification, 83 | { 84 | #[pin] 85 | supervisee: S::Supervisee, 86 | on_start: Option, 87 | phantom: PhantomData T>, 88 | } 89 | 90 | impl Supervisee for OnStartSupervisee 91 | where 92 | S: Specification, 93 | F: FnMut(S::Ref) -> T + Send + 'static, 94 | T: Send + 'static, 95 | { 96 | type Spec = OnStartSpec; 97 | 98 | fn shutdown_time(self: Pin<&Self>) -> Duration { 99 | self.project_ref().supervisee.shutdown_time() 100 | } 101 | 102 | fn halt(self: Pin<&mut Self>) { 103 | self.project().supervisee.halt() 104 | } 105 | 106 | fn abort(self: Pin<&mut Self>) { 107 | self.project().supervisee.abort() 108 | } 109 | 110 | fn poll_supervise(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { 111 | let this = self.project(); 112 | this.supervisee.poll_supervise(cx).map(|res| { 113 | res.map(|spec| { 114 | spec.map(|spec| OnStartSpec { 115 | inner_spec: spec, 116 | on_start: this.on_start.take().unwrap(), 117 | phantom: *this.phantom, 118 | }) 119 | }) 120 | }) 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /zestors-codegen/src/lib.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream as TokenStream1; 2 | #[macro_use] 3 | extern crate quote; 4 | 5 | /// Derive the `Message` trait. 6 | /// 7 | /// # Usage 8 | /// ```ignore 9 | /// #[derive(Message)] 10 | /// struct MyMessage; 11 | /// 12 | /// #[derive(Message)] 13 | /// #[request(T)] 14 | /// struct MyRequest; 15 | /// 16 | /// // (Same as the one above) 17 | /// #[derive(Message)] 18 | /// #[msg(Rx)] 19 | /// struct MyRequest; 20 | /// ``` 21 | /// 22 | /// This generates the following implementation: 23 | /// ```ignore 24 | /// impl Message for MyMessage { ... } 25 | /// ``` 26 | #[proc_macro_derive(Message, attributes(msg, request))] 27 | pub fn derive_message(item: TokenStream1) -> TokenStream1 { 28 | message::derive_message(item.into()) 29 | .unwrap_or_else(|e| e.into_compile_error()) 30 | .into() 31 | } 32 | 33 | 34 | /// Modifies the enum to implement protocol: 35 | /// 36 | /// # Usage 37 | /// ```ignore 38 | /// #[protocol] 39 | /// enum MyProtocol { 40 | /// MessageOne(Msg), 41 | /// MessageTwo(Request) 42 | /// } 43 | /// ``` 44 | /// 45 | /// Creates the following enum: 46 | /// ```ignore 47 | /// enum MyProtocol { 48 | /// MessageOne(Msg), 49 | /// MessageTwo((Request, Tx)) 50 | /// } 51 | /// ``` 52 | /// 53 | /// And also generates the following implementations: 54 | /// ```ignore 55 | /// impl Protocol for MyProtocol { ... } 56 | /// impl Accept for MyProtocol { ... } 57 | /// impl Accept for MyProtocol { ... } 58 | /// impl HandledBy for MyProtocol 59 | /// where 60 | /// H: HandleMessage + HandleMessage 61 | /// { ... } 62 | /// ``` 63 | #[proc_macro_attribute] 64 | pub fn protocol(attr: TokenStream1, item: TokenStream1) -> TokenStream1 { 65 | protocol::protocol(attr.into(), item.into()) 66 | .unwrap_or_else(|e| e.into_compile_error()) 67 | .into() 68 | } 69 | 70 | /// Generates a trait that can be used to send `Envelope`s of this message. 71 | /// 72 | /// # Usage 73 | /// ```ignore 74 | /// #[derive(Message, Envelope)] 75 | /// struct MyMessage { 76 | /// arg1: u32, 77 | /// arg2: u64 78 | /// } 79 | /// 80 | /// #[derive(Message, Envelope)] 81 | /// #[envelope(CustomTraitName, custom_method_name)] 82 | /// struct MyMessage { 83 | /// arg1: u32, 84 | /// arg2: u64 85 | /// } 86 | /// ``` 87 | /// 88 | /// Which generates the a trait similar to the following: 89 | /// ```ignore 90 | /// trait MyMessageEnvelope { 91 | /// pub fn my_message(&self, arg1: u32, arg2: u64) -> Envelope<..> { 92 | /// ... 93 | /// } 94 | /// } 95 | /// 96 | /// impl MyMessageEnvelope for T 97 | /// where 98 | /// T: ActorRef, 99 | /// A: Accept 100 | /// ``` 101 | /// 102 | /// This makes it possible to call `my_message` directly on an `Address` or `Child`. 103 | #[proc_macro_derive(Envelope, attributes(envelope))] 104 | pub fn derive_envelope(item: TokenStream1) -> TokenStream1 { 105 | envelope::derive_envelope(item.into()) 106 | .unwrap_or_else(|e| e.into_compile_error()) 107 | .into() 108 | } 109 | 110 | /// Implements `HandleExit` for your `Handler`. 111 | /// 112 | /// # Usage 113 | /// ```ignore 114 | /// #[derive(HandleExit)] 115 | /// struct MyHandler { .. } 116 | /// ``` 117 | /// 118 | /// Which generates the following implementation: 119 | /// ```ignore 120 | /// impl HandleExit for MyHandler { 121 | /// type Exit = ExitReason; 122 | /// 123 | /// async fn handle_exit( 124 | /// self, 125 | /// state: &mut Self::State, 126 | /// reason: ExitReason, 127 | /// ) -> ExitFlow { 128 | /// ExitFlow::Exit(reason) 129 | /// } 130 | /// } 131 | /// ``` 132 | #[proc_macro_derive(Handler, attributes(state))] 133 | pub fn derive_handler(item: TokenStream1) -> TokenStream1 { 134 | handler::derive_handler(item.into()) 135 | .unwrap_or_else(|e| e.into_compile_error()) 136 | .into() 137 | } 138 | 139 | mod message; 140 | mod protocol; 141 | mod envelope; 142 | mod handler; 143 | -------------------------------------------------------------------------------- /zestors/src/actor_type/halter/channel.rs: -------------------------------------------------------------------------------- 1 | use crate::all::*; 2 | use event_listener::{Event, EventListener}; 3 | use std::{ 4 | any::Any, 5 | sync::{ 6 | atomic::{AtomicBool, AtomicUsize, Ordering}, 7 | Arc, 8 | }, 9 | }; 10 | 11 | /// The [`Channel`] of a [`Halter`]. 12 | #[derive(Debug)] 13 | pub struct HalterChannel { 14 | address_count: AtomicUsize, 15 | halted: AtomicBool, 16 | has_exited: AtomicBool, 17 | actor_id: ActorId, 18 | exit_event: Event, 19 | halt_event: Event, 20 | } 21 | 22 | impl HalterChannel { 23 | pub(crate) fn new(address_count: usize, actor_id: ActorId) -> Self { 24 | Self { 25 | address_count: AtomicUsize::new(address_count), 26 | halted: AtomicBool::new(false), 27 | has_exited: AtomicBool::new(false), 28 | actor_id, 29 | exit_event: Event::new(), 30 | halt_event: Event::new(), 31 | } 32 | } 33 | 34 | pub(crate) fn get_halt_listener(&self) -> EventListener { 35 | self.halt_event.listen() 36 | } 37 | 38 | pub(crate) fn halted(&self) -> bool { 39 | self.halted.load(Ordering::Acquire) 40 | } 41 | 42 | pub(crate) fn exit(&self) { 43 | self.has_exited.store(true, Ordering::Release) 44 | } 45 | } 46 | 47 | impl Channel for HalterChannel { 48 | fn close(&self) -> bool { 49 | false 50 | } 51 | 52 | fn halt_some(&self, _n: u32) { 53 | self.halt() 54 | } 55 | 56 | fn halt(&self) { 57 | self.halted.store(true, Ordering::Release); 58 | self.halt_event.notify(1) 59 | } 60 | 61 | fn process_count(&self) -> usize { 62 | if self.has_exited() { 63 | 0 64 | } else { 65 | 1 66 | } 67 | } 68 | 69 | fn msg_count(&self) -> usize { 70 | 0 71 | } 72 | 73 | fn address_count(&self) -> usize { 74 | self.address_count.load(Ordering::Acquire) 75 | } 76 | 77 | fn is_closed(&self) -> bool { 78 | true 79 | } 80 | 81 | fn has_exited(&self) -> bool { 82 | self.has_exited.load(Ordering::Acquire) 83 | } 84 | 85 | fn get_exit_listener(&self) -> EventListener { 86 | self.exit_event.listen() 87 | } 88 | 89 | fn actor_id(&self) -> ActorId { 90 | self.actor_id 91 | } 92 | 93 | fn capacity(&self) -> Capacity { 94 | Capacity::Bounded(0) 95 | } 96 | 97 | fn increment_address_count(&self) -> usize { 98 | self.address_count.fetch_add(1, Ordering::Acquire) 99 | } 100 | 101 | fn decrement_address_count(&self) -> usize { 102 | let prev_address_count = self.address_count.fetch_sub(1, Ordering::Acquire); 103 | assert!(prev_address_count >= 1); 104 | prev_address_count 105 | } 106 | 107 | fn try_increment_process_count(&self) -> Result { 108 | Err(AddProcessError::SingleProcessOnly) 109 | } 110 | 111 | fn try_send_box(&self, boxed: BoxPayload) -> Result<(), TrySendCheckedError> { 112 | Err(TrySendCheckedError::NotAccepted(boxed)) 113 | } 114 | 115 | fn force_send_box(&self, boxed: BoxPayload) -> Result<(), TrySendCheckedError> { 116 | Err(TrySendCheckedError::NotAccepted(boxed)) 117 | } 118 | 119 | fn send_box_blocking(&self, boxed: BoxPayload) -> Result<(), SendCheckedError> { 120 | Err(SendCheckedError::NotAccepted(boxed)) 121 | } 122 | 123 | fn send_box<'a>( 124 | &'a self, 125 | boxed: BoxPayload, 126 | ) -> std::pin::Pin< 127 | Box>> + Send + 'a>, 128 | > { 129 | Box::pin(async move { Err(SendCheckedError::NotAccepted(boxed)) }) 130 | } 131 | 132 | fn accepts(&self, _id: &std::any::TypeId) -> bool { 133 | false 134 | } 135 | 136 | fn into_any(self: Arc) -> Arc { 137 | self 138 | } 139 | 140 | fn into_dyn(self: Arc) -> Arc { 141 | self 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /supervision/src/supervision/combinators/ref_sender.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use async_trait::async_trait; 3 | use futures::{Future, future::BoxFuture}; 4 | use pin_project::pin_project; 5 | use std::{ 6 | pin::Pin, 7 | task::{Context, Poll}, time::Duration, 8 | }; 9 | use tokio::sync::mpsc; 10 | 11 | //------------------------------------------------------------------------------------------------ 12 | // Spec 13 | //------------------------------------------------------------------------------------------------ 14 | 15 | #[pin_project] 16 | pub struct RefSenderSpec { 17 | #[pin] 18 | spec: S, 19 | sender: Option>, 20 | } 21 | 22 | #[pin_project] 23 | pub struct RefSenderSpecFut { 24 | #[pin] 25 | fut: BoxFuture<'static, StartResult>, 26 | sender: Option>, 27 | } 28 | 29 | #[pin_project] 30 | pub struct RefSenderSupervisee { 31 | #[pin] 32 | supervisee: S::Supervisee, 33 | sender: Option>, 34 | } 35 | 36 | impl RefSenderSpec { 37 | pub fn new(spec: Sp) -> (Self, mpsc::UnboundedReceiver) { 38 | let (sender, receiver) = mpsc::unbounded_channel(); 39 | (Self::new_with_channel(spec, sender), receiver) 40 | } 41 | 42 | pub fn new_with_channel(spec: Sp, sender: mpsc::UnboundedSender) -> Self { 43 | Self { 44 | spec, 45 | sender: Some(sender), 46 | } 47 | } 48 | } 49 | 50 | #[async_trait] 51 | impl Specification for RefSenderSpec { 52 | type Ref = (); 53 | type Supervisee = RefSenderSupervisee; 54 | 55 | async fn start_supervised(self) -> StartResult { 56 | RefSenderSpecFut { 57 | fut: self.spec.start_supervised(), 58 | sender: self.sender, 59 | }.await 60 | } 61 | 62 | } 63 | 64 | impl Future for RefSenderSpecFut { 65 | type Output = StartResult>; 66 | 67 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 68 | let proj = self.project(); 69 | proj.fut.poll(cx).map(|start| match start { 70 | Ok((supervisee, reference)) => { 71 | let sender = proj.sender.take().unwrap(); 72 | let _ = sender.send(reference); 73 | Ok(( 74 | RefSenderSupervisee { 75 | supervisee, 76 | sender: Some(sender), 77 | }, 78 | (), 79 | )) 80 | } 81 | Err(StartError::StartFailed(spec)) => Err(StartError::StartFailed(RefSenderSpec { 82 | spec, 83 | sender: Some(proj.sender.take().unwrap()), 84 | })), 85 | Err(StartError::Completed) => Err(StartError::Completed), 86 | Err(StartError::Fatal(e)) => Err(StartError::Fatal(e)), 87 | }) 88 | } 89 | } 90 | 91 | //------------------------------------------------------------------------------------------------ 92 | // Supervisee 93 | //------------------------------------------------------------------------------------------------ 94 | 95 | impl Supervisee for RefSenderSupervisee { 96 | type Spec = RefSenderSpec; 97 | 98 | fn shutdown_time(self: Pin<&Self>) -> Duration { 99 | self.project_ref().supervisee.shutdown_time() 100 | } 101 | 102 | fn halt(self: Pin<&mut Self>) { 103 | self.project().supervisee.halt() 104 | } 105 | 106 | fn abort(self: Pin<&mut Self>) { 107 | self.project().supervisee.abort() 108 | } 109 | 110 | fn poll_supervise(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { 111 | let proj = self.project(); 112 | proj.supervisee.poll_supervise(cx).map(|res| { 113 | res.map(|spec| { 114 | spec.map(|spec| RefSenderSpec { 115 | spec, 116 | sender: proj.sender.take(), 117 | }) 118 | }) 119 | }) 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /supervision/src/supervision2/box_supervisee.rs: -------------------------------------------------------------------------------- 1 | #[allow(unused)] 2 | use crate::all::*; 3 | use async_trait::async_trait; 4 | use futures::future::BoxFuture; 5 | use std::{ 6 | pin::Pin, 7 | task::{Context, Poll}, 8 | time::Duration, 9 | }; 10 | 11 | pub struct BoxSupervisee(Box>); 12 | 13 | impl BoxSupervisee { 14 | pub fn new>(supervisee: S) -> Self { 15 | Self(Box::new(supervisee)) 16 | } 17 | } 18 | 19 | impl Supervisee for BoxSupervisee { 20 | type Ref = Ref; 21 | type Running = RunningBoxSupervisee; 22 | type Future = BoxFuture<'static, Result<(Self::Running, Self::Ref), Self>>; 23 | 24 | fn start_supervised(self) -> Self::Future { 25 | self.0.start_supervised() 26 | } 27 | } 28 | 29 | impl Unpin for BoxSupervisee {} 30 | 31 | pub struct RunningBoxSupervisee(Pin>>); 32 | 33 | impl RunningBoxSupervisee { 34 | pub fn new(supervisee: S) -> Self 35 | where 36 | S::Supervisee: Supervisee, 37 | { 38 | Self(Box::pin(supervisee)) 39 | } 40 | } 41 | 42 | impl RunningSupervisee for RunningBoxSupervisee { 43 | type Supervisee = BoxSupervisee; 44 | 45 | fn poll_supervise( 46 | mut self: Pin<&mut Self>, 47 | cx: &mut Context, 48 | ) -> Poll> { 49 | self.0.as_mut().poll_supervise(cx) 50 | } 51 | 52 | fn shutdown_time(self: Pin<&Self>) -> Duration { 53 | self.0.as_ref().shutdown_time() 54 | } 55 | 56 | fn halt(mut self: Pin<&mut Self>) { 57 | self.0.as_mut().halt() 58 | } 59 | 60 | fn abort(mut self: Pin<&mut Self>) { 61 | self.0.as_mut().abort() 62 | } 63 | } 64 | 65 | impl Unpin for RunningBoxSupervisee {} 66 | 67 | //------------------------------------------------------------------------------------------------ 68 | // Dynamic traits 69 | //------------------------------------------------------------------------------------------------ 70 | 71 | #[async_trait] 72 | trait DynSupervisee: Send + 'static { 73 | type Ref: Send + 'static; 74 | 75 | async fn start_supervised( 76 | self: Box, 77 | ) -> Result<(RunningBoxSupervisee, Self::Ref), BoxSupervisee>; 78 | } 79 | 80 | #[async_trait] 81 | impl DynSupervisee for T 82 | where 83 | T: Supervisee, 84 | { 85 | type Ref = Ref; 86 | 87 | async fn start_supervised( 88 | self: Box, 89 | ) -> Result<(RunningBoxSupervisee, Ref), BoxSupervisee> { 90 | match Supervisee::start_supervised(*self).await { 91 | Ok((running, reference)) => Ok((RunningBoxSupervisee::new(running), reference)), 92 | Err(this) => Err(BoxSupervisee::new(this)), 93 | } 94 | } 95 | } 96 | 97 | #[async_trait] 98 | pub trait DynRunningSupervisee: Send { 99 | type Ref: Send + 'static; 100 | 101 | fn poll_supervise( 102 | self: Pin<&mut Self>, 103 | cx: &mut Context, 104 | ) -> Poll>>; 105 | fn shutdown_time(self: Pin<&Self>) -> Duration; 106 | fn halt(self: Pin<&mut Self>); 107 | fn abort(self: Pin<&mut Self>); 108 | } 109 | 110 | impl DynRunningSupervisee for T 111 | where 112 | T: RunningSupervisee, 113 | { 114 | type Ref = ::Ref; 115 | 116 | fn poll_supervise( 117 | self: Pin<&mut Self>, 118 | cx: &mut Context, 119 | ) -> Poll>> { 120 | RunningSupervisee::poll_supervise(self, cx).map(|res| match res { 121 | Some(supervisee) => Some(BoxSupervisee::new(supervisee)), 122 | None => None, 123 | }) 124 | } 125 | 126 | fn shutdown_time(self: Pin<&Self>) -> Duration { 127 | RunningSupervisee::shutdown_time(self) 128 | } 129 | 130 | fn halt(self: Pin<&mut Self>) { 131 | RunningSupervisee::halt(self) 132 | } 133 | 134 | fn abort(self: Pin<&mut Self>) { 135 | RunningSupervisee::abort(self) 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /zestors/src/spawning/functions.rs: -------------------------------------------------------------------------------- 1 | use crate::all::*; 2 | use futures::Future; 3 | 4 | /// Same as [`spawn_with`] but with a default [`Link`] and [`InboxType::Config`]. 5 | /// 6 | /// # Usage 7 | /// ``` 8 | /// # tokio_test::block_on(main()); 9 | /// use zestors::prelude::*; 10 | /// 11 | /// # async fn main() { 12 | /// let (child, address) = spawn(|inbox: Inbox<()>| async move { 13 | /// todo!() 14 | /// }); 15 | /// # } 16 | /// ``` 17 | pub fn spawn(function: Fun) -> (Child, Address) 18 | where 19 | Fun: FnOnce(I) -> Fut + Send + 'static, 20 | Fut: Future + Send, 21 | I: InboxType, 22 | I::Config: Default, 23 | E: Send + 'static, 24 | { 25 | spawn_with(Default::default(), Default::default(), function) 26 | } 27 | 28 | /// Spawn an actor with the given function using the [`Link`] and [`InboxType::Config`]. 29 | /// 30 | /// # Usage 31 | /// ``` 32 | /// # tokio_test::block_on(main()); 33 | /// use zestors::prelude::*; 34 | /// 35 | /// # async fn main() { 36 | /// let (child, address) = spawn_with( 37 | /// Link::default(), 38 | /// Capacity::default(), 39 | /// |inbox: Inbox<()>| async move { 40 | /// todo!() 41 | /// } 42 | /// ); 43 | /// # } 44 | /// ``` 45 | pub fn spawn_with( 46 | link: Link, 47 | config: I::Config, 48 | function: Fun, 49 | ) -> (Child, Address) 50 | where 51 | Fun: FnOnce(I) -> Fut + Send + 'static, 52 | Fut: Future + Send, 53 | I: InboxType, 54 | E: Send + 'static, 55 | { 56 | let (channel, inbox) = I::init_single_inbox(config, 1, ActorId::generate()); 57 | // let inbox = I::from_channel(channel.clone()); 58 | let handle = tokio::task::spawn(async move { function(inbox).await }); 59 | ( 60 | Child::new(channel.clone(), handle, link), 61 | Address::from_channel(channel), 62 | ) 63 | } 64 | 65 | /// Spawn an actor consisting of multiple processes with the given function using a default 66 | /// [`Link`] and [`InboxType::Config`]. 67 | /// 68 | /// # Usage 69 | /// ``` 70 | /// # tokio_test::block_on(main()); 71 | /// use zestors::prelude::*; 72 | /// 73 | /// # async fn main() { 74 | /// let (child, address) = spawn_many(0..5, |i: u32, inbox: Inbox<()>| async move { 75 | /// todo!() 76 | /// }); 77 | /// # } 78 | /// ``` 79 | pub fn spawn_many( 80 | iter: impl ExactSizeIterator, 81 | function: Fun, 82 | ) -> (ChildPool, Address) 83 | where 84 | Fun: FnOnce(Itm, I) -> Fut + Clone + Send + 'static, 85 | Fut: Future + Send, 86 | I: MultiProcessInbox, 87 | I::Config: Default, 88 | E: Send + 'static, 89 | Itm: Send + 'static, 90 | { 91 | spawn_many_with(Default::default(), Default::default(), iter, function) 92 | } 93 | 94 | /// Spawn an actor consisting of multiple processes with the given function using a custom 95 | /// [`Link`] and [`InboxType::Config`]. 96 | /// 97 | /// # Usage 98 | /// ``` 99 | /// # tokio_test::block_on(main()); 100 | /// use zestors::prelude::*; 101 | /// 102 | /// # async fn main() { 103 | /// let (child, address) = spawn_many_with( 104 | /// Link::default(), 105 | /// Capacity::default(), 106 | /// 0..5, 107 | /// |i: u32, inbox: Inbox<()>| async move { 108 | /// todo!() 109 | /// } 110 | /// ); 111 | /// # } 112 | /// ``` 113 | pub fn spawn_many_with( 114 | link: Link, 115 | config: I::Config, 116 | iter: impl ExactSizeIterator, 117 | function: Fun, 118 | ) -> (ChildPool, Address) 119 | where 120 | Fun: FnOnce(Itm, I) -> Fut + Clone + Send + 'static, 121 | Fut: Future + Send, 122 | I: MultiProcessInbox, 123 | E: Send + 'static, 124 | Itm: Send + 'static, 125 | { 126 | let channel = I::init_multi_inbox(config, iter.len(), 1, ActorId::generate()); 127 | let handles = iter 128 | .map(|i| { 129 | let fun = function.clone(); 130 | let inbox = I::from_channel(channel.clone()); 131 | tokio::task::spawn(async move { fun(i, inbox).await }) 132 | }) 133 | .collect::>(); 134 | ( 135 | Child::new(channel.clone(), handles, link), 136 | Address::from_channel(channel), 137 | ) 138 | } 139 | -------------------------------------------------------------------------------- /zestors-codegen/src/envelope.rs: -------------------------------------------------------------------------------- 1 | use heck::ToSnakeCase; 2 | use proc_macro2::{Ident, Span, TokenStream}; 3 | use quote::{quote, ToTokens}; 4 | use syn::{ 5 | parse2, parse_quote, punctuated::Punctuated, spanned::Spanned, token::Colon, Error, Fields, 6 | ItemStruct, Meta, Pat, PatType, Token, 7 | }; 8 | 9 | pub fn derive_envelope(item: TokenStream) -> Result { 10 | let item = parse2::(item)?; 11 | 12 | if !item.generics.params.is_empty() { 13 | Err(Error::new_spanned( 14 | item.generics.params, 15 | "Generics currently not supported with Envelope", 16 | ))? 17 | } 18 | 19 | let (trait_name, method_name) = match item 20 | .attrs 21 | .iter() 22 | .find(|attr| attr.path.is_ident("envelope")) 23 | { 24 | Some(attr) => { 25 | let Meta::List(meta_list) = attr.parse_meta()? else { panic!() }; 26 | let mut iter = meta_list.nested.into_iter(); 27 | let Some(trait_name) = iter.next() else { 28 | Err(Error::new_spanned(&meta_list.path, "Expected trait name"))? 29 | }; 30 | let Some(fn_name) = iter.next() else { 31 | Err(Error::new_spanned(&meta_list.path, "Expected method name"))? 32 | }; 33 | ( 34 | parse2(trait_name.to_token_stream())?, 35 | parse2(fn_name.to_token_stream())?, 36 | ) 37 | } 38 | None => { 39 | let trait_ident = Ident::new( 40 | &format!("{}Envelope", item.ident.to_string()), 41 | item.ident.span(), 42 | ); 43 | let method_ident = 44 | Ident::new(&item.ident.to_string().to_snake_case(), item.ident.span()); 45 | (trait_ident, method_ident) 46 | } 47 | }; 48 | let trait_doc = format!( 49 | " 50 | Automatically generated trait for creating envelopes of the 51 | message[`{}`]. 52 | ", 53 | item.ident 54 | ); 55 | let method_doc = format!( 56 | " 57 | Creates an [`Envelope`](::zestors::messaging::Envelope) for the 58 | message[`{}`]. 59 | ", 60 | item.ident 61 | ); 62 | let ident = item.ident; 63 | let vis = item.vis; 64 | let (field_params, field_idents) = parse_fields(item.fields); 65 | 66 | Ok(quote! { 67 | #[doc = #trait_doc] 68 | #vis trait #trait_name: ::zestors::actor_reference::ActorRef 69 | where 70 | Self::ActorType: ::zestors::messaging::Accepts<#ident> 71 | { 72 | #[doc = #method_doc] 73 | fn #method_name( 74 | &self, 75 | #field_params 76 | ) -> ::zestors::messaging::Envelope<'_, Self::ActorType, #ident> { 77 | ::envelope(self, #ident { 78 | #field_idents 79 | }) 80 | } 81 | } 82 | 83 | impl #trait_name for T 84 | where 85 | T: ::zestors::actor_reference::ActorRef, 86 | T::ActorType: ::zestors::messaging::Accepts<#ident> 87 | { } 88 | }) 89 | } 90 | 91 | pub fn parse_fields( 92 | fields: Fields, 93 | ) -> ( 94 | Punctuated, 95 | Punctuated, Token![,]>, 96 | ) { 97 | let params = match fields { 98 | Fields::Named(named_fields) => named_fields 99 | .named 100 | .into_iter() 101 | .enumerate() 102 | .map(|(i, field)| { 103 | let span = field.span(); 104 | let ident = field.ident.unwrap_or(Ident::new(&format!("arg{i}"), span)); 105 | PatType { 106 | attrs: field.attrs, 107 | pat: parse_quote!(#ident), 108 | colon_token: Colon(Span::call_site()), 109 | ty: field.ty.into(), 110 | } 111 | }) 112 | .collect::>(), 113 | Fields::Unnamed(_) => todo!(), 114 | Fields::Unit => todo!(), 115 | }; 116 | let fields = params 117 | .iter() 118 | .map(|field| field.pat.clone()) 119 | .collect::>(); 120 | (params, fields) 121 | } 122 | -------------------------------------------------------------------------------- /zestors/src/actor_reference/address.rs: -------------------------------------------------------------------------------- 1 | use crate::{all::*, DynActor}; 2 | use event_listener::EventListener; 3 | use futures::{Future, FutureExt}; 4 | use std::{ 5 | fmt::Debug, 6 | mem::ManuallyDrop, 7 | pin::Pin, 8 | sync::Arc, 9 | task::{Context, Poll}, 10 | }; 11 | 12 | /// An address is a cloneable [reference](ActorRef) to an actor. Most methods are located in the traits 13 | /// [`ActorRefExt`] and [`Transformable`]. 14 | /// 15 | /// ## ActorType 16 | /// The generic parameter `A` in `Address` defines the [`ActorType`]. 17 | /// 18 | /// An `Address` = `Address`. (An address that doesn't accept any messages). 19 | /// 20 | /// ## Awaiting 21 | /// An address can be awaited which resolves to `()` when the actor exits. 22 | #[derive(Debug)] 23 | pub struct Address { 24 | channel: Arc, 25 | exit_listener: Option, 26 | } 27 | 28 | impl Address { 29 | pub(crate) fn from_channel(channel: Arc) -> Self { 30 | Self { 31 | channel, 32 | exit_listener: None, 33 | } 34 | } 35 | 36 | fn into_parts(self) -> (Arc, Option) { 37 | let manually_drop = ManuallyDrop::new(self); 38 | unsafe { 39 | let channel = std::ptr::read(&manually_drop.channel); 40 | let exit_listener = std::ptr::read(&manually_drop.exit_listener); 41 | (channel, exit_listener) 42 | } 43 | } 44 | } 45 | 46 | impl Transformable for Address { 47 | type IntoRef = Address where T: ActorType; 48 | 49 | fn transform_unchecked_into(self) -> Self::IntoRef 50 | where 51 | T: DynActorType, 52 | { 53 | let (channel, exit_listener) = self.into_parts(); 54 | Address { 55 | channel: ::into_dyn(channel), 56 | exit_listener, 57 | } 58 | } 59 | 60 | fn transform_into(self) -> Self::IntoRef 61 | where 62 | Self::ActorType: TransformInto, 63 | T: ActorType, 64 | { 65 | let (channel, exit_listener) = self.into_parts(); 66 | Address { 67 | channel: A::transform_into(channel), 68 | exit_listener, 69 | } 70 | } 71 | 72 | fn downcast(self) -> Result, Self> 73 | where 74 | Self::ActorType: DynActorType, 75 | T: ActorType, 76 | T::Channel: Sized + 'static, 77 | { 78 | let (channel, exit_listener) = self.into_parts(); 79 | match channel.clone().into_any().downcast() { 80 | Ok(channel) => Ok(Address { 81 | channel, 82 | exit_listener, 83 | }), 84 | Err(_) => Err(Self { 85 | channel, 86 | exit_listener, 87 | }), 88 | } 89 | } 90 | } 91 | 92 | impl ActorRef for Address { 93 | type ActorType = A; 94 | fn channel_ref(this: &Self) -> &Arc<::Channel> { 95 | &this.channel 96 | } 97 | } 98 | 99 | impl Future for Address { 100 | type Output = (); 101 | 102 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 103 | let this = &mut *self; 104 | 105 | let exit_listener = this 106 | .exit_listener 107 | .get_or_insert(this.channel.get_exit_listener()); 108 | 109 | if this.channel.has_exited() { 110 | Poll::Ready(()) 111 | } else { 112 | exit_listener.poll_unpin(cx) 113 | } 114 | } 115 | } 116 | 117 | impl Unpin for Address {} 118 | 119 | impl Clone for Address { 120 | fn clone(&self) -> Self { 121 | self.clone_address() 122 | } 123 | } 124 | 125 | impl Drop for Address { 126 | fn drop(&mut self) { 127 | self.channel.decrement_address_count(); 128 | } 129 | } 130 | 131 | //------------------------------------------------------------------------------------------------ 132 | // IntoAddress 133 | //------------------------------------------------------------------------------------------------ 134 | 135 | /// Implemented for any type that can be transformed into an [`Address`]. 136 | pub trait IntoAddress { 137 | fn into_address(self) -> Address; 138 | } 139 | 140 | impl IntoAddress for Address 141 | where 142 | A: TransformInto, 143 | { 144 | fn into_address(self) -> Address { 145 | self.transform_into() 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

8 | 9 | 10 | # Zestors 11 | A fast and flexible actor-framework for building fault-tolerant Rust applications, inspired by Erlang/OTP. 12 | 13 | ## Getting started 14 | Zestors is thoroughly documented at [docs.rs](https://docs.rs/zestors) with guides covering most aspects of the system and a quick-start to get up and running. This is the recommended place to get started. 15 | 16 | ## Design choices 17 | ### The Message and Protocol traits 18 | Central to the design of zestors is the definition of messages and protocols. While at first this seems like a of bloat, it was a deliberate choice: 19 | - The definition of messages makes sure that all actors handle the same message in the same way. 20 | This allows you to write code that is generic over the messages an actor accepts. 21 | - The definition of a protocol allows you to write a custom, arbitrarily-complex event-loop for your actor. Without this option, writing more complex actors becomes impossible. 22 | - It is possible to write abstractions (i.e. `Handler`) that reduce bloat on top, while this is impossible the other way around. 23 | 24 | ### Static and dynamic typing 25 | Most actor-systems in rust take the approach of `Actix`, which defines an address by the handler. You have to know who is receiving a message in order to be able to send it (though there are some [ugly workarounds](https://docs.rs/actix/0.13.0/actix/struct.Recipient.html)). Zestors allows you to send messages in this way, but also allows you to type an address based on the messages it receives, i.e. `Address`. 26 | 27 | Sending messages with a statically-defined actor reference is very similar in speed to spawning a tokio-task with an mpsc-channel and sending messages as enums. You only pay for dynamically-defined addresses when you actually use them. 28 | 29 | ## Note 30 | Zestors is still early in development, and while the core parts of zestors have been stable for quite some time, from the `handler` module and beyond big changes are expected. Instead of perfecting everything privately, I would rather get it out there to see what people think. Certain parts of the system have been tested extensively but there are (very likely) bugs. 31 | 32 | If you have any feedback whether it is a bug, anything unclear or inspiration/ideas then I would appreciate to hear this! 33 | 34 | ## Future work 35 | 1. Finalize design for the `handler` module and continue work on the (to be released) `supervision` crate. 36 | 2. Continue with unit- and integration-tests and build out a few bigger examples. 37 | 3. Start work and design for a distributed environment. Everything has been designed from the ground up with distribution in mind, but details have not been worked out. 38 | 39 | 40 | ## Minimal example 41 | ```rust 42 | #[macro_use] 43 | extern crate zestors; 44 | use zestors::{messaging::RecvError, prelude::*}; 45 | 46 | // Let's define a single request .. 47 | #[derive(Message, Envelope, Debug)] 48 | #[request(u32)] 49 | struct MyRequest { 50 | param: String, 51 | } 52 | 53 | // .. and create a protocol that accepts this request. 54 | #[protocol] 55 | enum MyProtocol { 56 | MyRequest(MyRequest), 57 | String(String), 58 | } 59 | 60 | #[tokio::main] 61 | async fn main() { 62 | // Now we can spawn a simple actor .. 63 | let (child, address) = spawn(|mut inbox: Inbox| async move { 64 | loop { 65 | match inbox.recv().await { 66 | Ok(msg) => match msg { 67 | MyProtocol::MyRequest((request, tx)) => { 68 | println!("Received request: {:?}", request.param); 69 | tx.send(100).unwrap(); 70 | } 71 | MyProtocol::String(string) => { 72 | println!("Received message: {:?}", string); 73 | } 74 | }, 75 | Err(e) => match e { 76 | RecvError::Halted => break "Halted", 77 | RecvError::ClosedAndEmpty => break "Closed", 78 | }, 79 | } 80 | } 81 | }); 82 | 83 | // .. and send it some messages! 84 | address.send("Hi".to_string()).await.unwrap(); 85 | 86 | let response = address 87 | .request(MyRequest { 88 | param: "Hi".to_string(), 89 | }) 90 | .await 91 | .unwrap(); 92 | assert_eq!(response, 100); 93 | 94 | let response = address 95 | .my_request("Hi".to_string()) 96 | .request() 97 | .await 98 | .unwrap(); 99 | assert_eq!(response, 100); 100 | 101 | child.halt(); 102 | assert_eq!(child.await.unwrap(), "Halted"); 103 | } 104 | ``` 105 | -------------------------------------------------------------------------------- /zestors/src/messaging/mod.rs: -------------------------------------------------------------------------------- 1 | //! # Overview 2 | //! In order to receive messages an actor must define a [`Protocol`] that specifies which messages it 3 | //! [`Accepts`]. All messages must in turn define how they are handled by implementing [`Message`]. 4 | //! 5 | //! The [`Message`] trait specifies how a message should be handled by the actor. Since it is unique per 6 | //! message, every actor is guaranteed to respond in the same way. Normally [`Message`] is derived with the 7 | //! [`macro@Message`] macro, and most standard rust types like `u32`, `String` and `Vec`, have a 8 | //! message-implementation. 9 | //! 10 | //! The [`Protocol`] specifies exactly which messages an actor accepts. Normally the [`Protocol`] trait 11 | //! can be automatically generated with the [`macro@protocol`] macro. 12 | //! 13 | //! # The `Message` macro 14 | //! When using the derive [`macro@Message`] macro it is possible to set a `#[msg(T)]` or `#[request(T)]` 15 | //! attribute which specifies how the actor should handle the message. There are three types for which this 16 | //! is implemented automatically: 17 | //! 18 | //! | Attribute | Result | 19 | //! |---|---| 20 | //! | `none` / `#[msg(())]` | A simple message that does not receive a reply, the [`Message::Payload`] is `M` and [`Message::Returned`] is `()`. | 21 | //! | `#[request(T)]` / `#[msg(Rx)]` | A request of `T` where the [`Message::Payload`] is [`(M, Tx)`](Tx) and the [`Message::Returned`] is [`Rx`]. | 22 | //! | `#[msg(Tx)]` | Same as `Rx` but swapped. | 23 | //! 24 | //! It is possible to create custom types usable in the `#[msg(..)]` attribute by implementing [`MessageDerive`] 25 | //! for this type. 26 | //! 27 | //! # Sending 28 | //! The following send-methods can be used on an [`Address`], [`Child`], [`InboxType`] or any other type that 29 | //! implements [`ActorRef`] / [`ActorRefExt`]. 30 | //! 31 | //! __Default send methods__: The default methods for sending a message. 32 | //! - [`try_send`](ActorRefExt::try_send): Attempts to send a message to the actor. If the inbox is closed/full 33 | //! or if a timeout is returned from the backpressure-mechanic, this method fails. 34 | //! - [`force_send`](ActorRefExt::force_send): Same as `try_send` but ignores any backpressure-mechanic. 35 | //! - [`send`](ActorRefExt::send): Attempts to send a message to the actor. If the inbox is full or if a timeout 36 | //! is returned from the backpressure-mechanic, this method will wait until there is space or until the timeout has 37 | //! expired. If the inbox is closed this method fails. 38 | //! - [`send_blocking`](ActorRefExt::send_blocking): Same as `send` but blocks the thread for non-async 39 | //! execution environments. 40 | //! 41 | //! __Checked-send methods__: Same as the regular send methods, but instead of checking at 42 | //! compile-time whether the messages are accepted by the actor, these check it at runtime. These methods are 43 | //! only valid for [`DynActor`](struct@DynActor) types. 44 | //! - [`try_send_checked`](ActorRefExt::try_send_checked) 45 | //! - [`force_send_checked`](ActorRefExt::force_send_checked) 46 | //! - [`send_checked`](ActorRefExt::send_checked) 47 | //! - [`send_blocking_checked`](ActorRefExt::send_blocking_checked) 48 | //! 49 | //! # Requesting 50 | //! A request is a [`Message`] which expects a reply to be sent from the [`Tx`] to the [`Rx`]. A request can 51 | //! be manually created with [`new_request`], but is usually automatically created when sending a message. 52 | //! Requests can be sent in the standard way -- by first sending the request and then waiting for the reply -- but 53 | //! this can also be done simpler with the following methods: 54 | //! - [`try_request`](ActorRefExt::try_request) 55 | //! - [`force_request`](ActorRefExt::force_request) 56 | //! - [`request`](ActorRefExt::request) 57 | //! 58 | //! These methods will send the request and subsequently await a response from the actor with a single method 59 | //! and `.await` point. 60 | //! 61 | //! # Envelope 62 | //! An [`Envelope`](struct@Envelope) is a [`Message`] containing an [`Address`] of where it should be sent. An envelope 63 | //! can be created with the [`ActorRefExt::envelope`] function. 64 | //! 65 | //! The [`macro@Envelope`] macro generates a custom trait that allows a user to directly call `.my_message(..)` on an 66 | //! actor-reference. This custom method constructs an [`Envelope`](struct@Envelope) from the [`Message`] parameters which 67 | //! can subsequently be sent. This macro is entirely optional and just exists for ergonomics. 68 | //! 69 | //! | [`actor_reference`](crate::actor_reference) __-->__ | 70 | //! |---| 71 | //! 72 | //! # Example 73 | //! ``` 74 | #![doc = include_str!("../../examples/messaging.rs")] 75 | //! ``` 76 | 77 | #[allow(unused)] 78 | use crate::all::*; 79 | 80 | pub use zestors_codegen::{protocol, Envelope, Message}; 81 | mod accepts; 82 | mod box_payload; 83 | mod envelope; 84 | mod errors; 85 | mod message; 86 | mod protocol; 87 | mod request; 88 | pub use accepts::*; 89 | pub use box_payload::*; 90 | pub use envelope::*; 91 | pub use errors::*; 92 | pub use message::*; 93 | pub use protocol::*; 94 | pub use request::*; 95 | -------------------------------------------------------------------------------- /zestors/src/messaging/request.rs: -------------------------------------------------------------------------------- 1 | use futures::{Future, FutureExt}; 2 | use std::{ 3 | pin::Pin, 4 | task::{Context, Poll}, 5 | }; 6 | use tokio::sync::oneshot; 7 | use crate::all::*; 8 | 9 | /// Create a new request, consisting of a [`Tx`] and an [`Rx`]. 10 | /// The `Tx` (_transmitter_) can be used to send a single message `T` to the `Rx` (_receiver_). 11 | /// 12 | /// This is just a wrapper around a [`tokio::sync::oneshot`] channel. 13 | pub fn new_request() -> (Tx, Rx) { 14 | let (tx, rx) = oneshot::channel(); 15 | (Tx(tx), Rx(rx)) 16 | } 17 | 18 | //------------------------------------------------------------------------------------------------ 19 | // Tx 20 | //------------------------------------------------------------------------------------------------ 21 | 22 | /// The transmitter part of a request, created with [`new_request`]. 23 | /// 24 | /// This implements [`MessageDerive`] to be used with the [`derive@Message`] derive macro. 25 | #[derive(Debug)] 26 | pub struct Tx(pub(super) oneshot::Sender); 27 | 28 | impl Tx { 29 | /// Send a message. 30 | pub fn send(self, msg: M) -> Result<(), TxError> { 31 | self.0.send(msg).map_err(|msg| TxError(msg)) 32 | } 33 | 34 | /// Whether the [`Rx`] has closed/dropped the oneshot-channel. 35 | pub fn is_closed(&self) -> bool { 36 | self.0.is_closed() 37 | } 38 | 39 | /// Wait for the [`Rx`] to close/drop the oneshot-channel. 40 | pub async fn closed(&mut self) { 41 | self.0.closed().await 42 | } 43 | } 44 | 45 | impl MessageDerive for Tx { 46 | type Payload = (M, Rx); 47 | type Returned = Tx; 48 | 49 | fn create(msg: M) -> ((M, Rx), Tx) { 50 | let (tx, rx) = new_request(); 51 | ((msg, rx), tx) 52 | } 53 | 54 | fn cancel(sent: (M, Rx), _returned: Tx) -> M { 55 | sent.0 56 | } 57 | } 58 | 59 | //------------------------------------------------------------------------------------------------ 60 | // Rx 61 | //------------------------------------------------------------------------------------------------ 62 | 63 | /// The receiver part of a request, created with [`new_request`]. 64 | /// 65 | /// This implements [`MessageDerive`] to be used with the [`derive@Message`] derive macro. 66 | #[derive(Debug)] 67 | pub struct Rx(pub(super) oneshot::Receiver); 68 | 69 | impl Rx { 70 | /// Attempt to take the message out, if it exists. 71 | pub fn try_recv(&mut self) -> Result { 72 | self.0.try_recv().map_err(|e| e.into()) 73 | } 74 | 75 | /// Block the thread while waiting for the message. 76 | pub fn recv_blocking(self) -> Result { 77 | self.0.blocking_recv().map_err(|e| e.into()) 78 | } 79 | 80 | /// Close the oneshot-channel, preventing the [`Tx`] from sending a message. 81 | pub fn close(&mut self) { 82 | self.0.close() 83 | } 84 | } 85 | 86 | impl MessageDerive for Rx { 87 | type Payload = (M, Tx); 88 | type Returned = Rx; 89 | 90 | fn create(msg: M) -> ((M, Tx), Rx) { 91 | let (tx, rx) = new_request(); 92 | ((msg, tx), rx) 93 | } 94 | 95 | fn cancel(sent: (M, Tx), _returned: Rx) -> M { 96 | sent.0 97 | } 98 | } 99 | 100 | impl Unpin for Rx {} 101 | 102 | impl Future for Rx { 103 | type Output = Result; 104 | 105 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 106 | self.0.poll_unpin(cx).map_err(|e| e.into()) 107 | } 108 | } 109 | 110 | //------------------------------------------------------------------------------------------------ 111 | // Errors 112 | //------------------------------------------------------------------------------------------------ 113 | 114 | /// Error returned when receiving a message using an [`Rx`]. 115 | #[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash, thiserror::Error)] 116 | #[error("Failed to receive from Rx because it is closed.")] 117 | pub struct RxError; 118 | 119 | impl From for RxError { 120 | fn from(_: oneshot::error::RecvError) -> Self { 121 | Self 122 | } 123 | } 124 | 125 | /// Error returned when trying to receive a message using an [`Rx`]. 126 | #[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash, thiserror::Error)] 127 | pub enum TryRxError { 128 | #[error("Closed")] 129 | Closed, 130 | #[error("Empty")] 131 | Empty, 132 | } 133 | 134 | impl From for TryRxError { 135 | fn from(e: oneshot::error::TryRecvError) -> Self { 136 | match e { 137 | oneshot::error::TryRecvError::Empty => Self::Empty, 138 | oneshot::error::TryRecvError::Closed => Self::Closed, 139 | } 140 | } 141 | } 142 | 143 | /// Error returned when sending a message using a [`Tx`]. 144 | #[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash, thiserror::Error)] 145 | #[error("Failed to send to Tx because it is closed.")] 146 | pub struct TxError(pub M); 147 | -------------------------------------------------------------------------------- /zestors/src/spawning/capacity.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | 3 | /// The configuration for spawning an inbox. This decides whether the inbox is bounded or unbounded. 4 | /// If it is unbounded then a [BackPressure] must be specified. 5 | #[derive(Debug, Clone, PartialEq)] 6 | pub enum Capacity { 7 | Bounded(usize), 8 | BackPressure(BackPressure), 9 | Unbounded 10 | } 11 | 12 | impl Capacity { 13 | /// Whether the capacity is bounded. 14 | pub fn is_bounded(&self) -> bool { 15 | matches!(self, Self::Bounded(_)) 16 | } 17 | } 18 | 19 | impl Default for Capacity { 20 | fn default() -> Self { 21 | Capacity::BackPressure(BackPressure::default()) 22 | } 23 | } 24 | 25 | /// The backpressure mechanism for unbounded inboxes. 26 | /// 27 | /// # Default 28 | /// - `starts_at: 5` - The backpressure mechanism should start if the inbox contains 5 or more 29 | /// messages. 30 | /// - `timeout: 25ns` - The timeout at which the backpressure mechanism starts is 25ns. 31 | /// - `factor: 1.3` - For every message in the inbox, the timeout is multiplied by 1.3. 32 | #[derive(Debug, Clone, PartialEq)] 33 | pub struct BackPressure { 34 | starts_at: usize, 35 | base_ns: u64, 36 | exp_growth: Option, 37 | } 38 | 39 | impl BackPressure { 40 | /// Creates a new linear backpressure. 41 | /// 42 | /// The timeout is calculated as follows: 43 | /// `timeout = timeout * (msg_count - start_at)` 44 | /// 45 | /// # Panics 46 | /// Panics if the `timeout` is bigger than `213_503 days`. 47 | pub fn linear(starts_at: usize, timeout: Duration) -> Self { 48 | let base_ns = timeout 49 | .as_nanos() 50 | .try_into() 51 | .expect("Base duration > 213_503 days"); 52 | 53 | Self { 54 | starts_at, 55 | base_ns, 56 | exp_growth: None, 57 | } 58 | } 59 | 60 | /// Creates a new linear backpressure. 61 | /// 62 | /// The timeout is calculated as follows: 63 | /// `timeout = timeout * (factor ^ (msg_count - start_at))` 64 | /// 65 | /// # Panics 66 | /// Panics if the `factor` is negative, or if the `timeout` is bigger than `213_503 days.` 67 | pub fn exponential(starts_at: usize, timeout: Duration, factor: f32) -> Self { 68 | if factor < 0.0 { 69 | panic!("Negative factors not allowed!") 70 | } 71 | 72 | let base_ns = timeout 73 | .as_nanos() 74 | .try_into() 75 | .expect("Base duration > 213_503 days"); 76 | 77 | Self { 78 | starts_at, 79 | base_ns, 80 | exp_growth: Some(factor), 81 | } 82 | } 83 | 84 | pub fn get_timeout(&self, msg_count: usize) -> Option { 85 | if msg_count < self.starts_at { 86 | return None; 87 | } 88 | 89 | match self.exp_growth { 90 | Some(factor) => { 91 | let diff = (msg_count - self.starts_at).try_into().unwrap_or(i32::MAX); 92 | let mult = (factor as f64).powi(diff); 93 | let ns = self.base_ns as f64 * mult; 94 | Some(Duration::from_nanos(ns as u64)) 95 | } 96 | None => { 97 | let diff = (msg_count - self.starts_at + 1) as u64; 98 | let ns = self.base_ns.saturating_mul(diff); 99 | Some(Duration::from_nanos(ns)) 100 | } 101 | } 102 | } 103 | } 104 | 105 | impl Default for BackPressure { 106 | fn default() -> Self { 107 | Self::exponential(5, Duration::from_nanos(25), 1.3) 108 | } 109 | } 110 | 111 | #[cfg(test)] 112 | mod test { 113 | use super::*; 114 | use std::time::Duration; 115 | 116 | #[test] 117 | fn backpressure_linear_start_at() { 118 | let bp = BackPressure::linear(10, Duration::from_secs(1)); 119 | 120 | assert_eq!(bp.get_timeout(9), None); 121 | assert_eq!(bp.get_timeout(10), Some(Duration::from_secs(1))); 122 | } 123 | 124 | #[test] 125 | fn backpressure_exponential_start_at() { 126 | let bp = BackPressure::exponential(10, Duration::from_secs(1), 1.1); 127 | 128 | assert_eq!(bp.get_timeout(9), None); 129 | assert_eq!( 130 | bp.get_timeout(10), 131 | Some(Duration::from_nanos(1_000_000_000)) 132 | ); 133 | } 134 | 135 | #[test] 136 | fn backpressure_linear() { 137 | let bp = BackPressure::linear(0, Duration::from_secs(1)); 138 | 139 | assert_eq!(bp.get_timeout(0), Some(Duration::from_secs(1))); 140 | assert_eq!(bp.get_timeout(1), Some(Duration::from_secs(2))); 141 | assert_eq!(bp.get_timeout(10), Some(Duration::from_secs(11))); 142 | } 143 | 144 | #[test] 145 | fn backpressure_exponential() { 146 | let bp = BackPressure::exponential(0, Duration::from_secs(1), 1.1); 147 | 148 | assert_eq!(bp.get_timeout(0), Some(Duration::from_nanos(1_000_000_000))); 149 | assert_eq!(bp.get_timeout(1), Some(Duration::from_nanos(1_100_000_023))); 150 | assert_eq!(bp.get_timeout(2), Some(Duration::from_nanos(1_210_000_052))); 151 | assert_eq!(bp.get_timeout(3), Some(Duration::from_nanos(1_331_000_086))); 152 | } 153 | 154 | } -------------------------------------------------------------------------------- /zestors/src/handler/scheduler.rs: -------------------------------------------------------------------------------- 1 | use crate::all::*; 2 | use futures::{stream::FuturesUnordered, Future, Stream, StreamExt}; 3 | use std::{ 4 | pin::Pin, 5 | sync::Arc, 6 | task::{Context, Poll}, 7 | }; 8 | 9 | /// A [`Scheduler`] is a [`HandlerState`] that allows you to schedule multiple futures and a 10 | /// single stream to be run concurrently. The parameter `T` can be any other handler-state such 11 | /// as an [`Inbox

`], and the parameter `H` is the [`Handler`] that uses the state. 12 | /// 13 | /// The order in which items are processed is: 14 | /// 1 From the state `T`. 15 | /// 2. From any scheduled futures. 16 | /// 3. From the scheduled stream. 17 | pub struct Scheduler { 18 | inner_state: Inbox

, 19 | futures: FuturesUnordered> + Send>>>, 20 | stream: Option> + Send>>>, 21 | } 22 | 23 | impl Scheduler { 24 | /// Schedule a future that resolves to an [`Action`]. 25 | pub fn schedule_future( 26 | &mut self, 27 | fut: impl Future> + Send + 'static, 28 | ) { 29 | self.futures.push(Box::pin( 30 | async move { HandlerItem::HandlerResult(fut.await) }, 31 | )) 32 | } 33 | 34 | /// Schedule a future that resolves to an [`Action`]. 35 | pub fn schedule_action(&mut self, fut: impl Future> + Send + 'static) { 36 | self.futures 37 | .push(Box::pin(async move { HandlerItem::Action(fut.await) })) 38 | } 39 | 40 | /// Schedule an [`Action`] to be run. 41 | pub fn schedule_action_now(&mut self, action: Action) { 42 | self.schedule_action(async move { action }) 43 | } 44 | 45 | /// Schedule a future that resolves to a [`Protocol`]. 46 | pub fn schedule_protocol(&mut self, fut: impl Future + Send + 'static) { 47 | self.futures 48 | .push(Box::pin(async move { HandlerItem::Protocol(fut.await) })) 49 | } 50 | 51 | /// Schedule a [`Protocol`] to be handled. 52 | pub fn schedule_protocol_now(&mut self, protocol: P) { 53 | self.futures 54 | .push(Box::pin(async move { HandlerItem::Protocol(protocol) })) 55 | } 56 | 57 | /// Schedule a [`Message`] to be handled. 58 | pub fn schedule_msg_now(&mut self, msg: M) -> M::Returned 59 | where 60 | P: FromPayload, 61 | { 62 | let (payload, returned) = M::create(msg); 63 | self.schedule_protocol_now(FromPayload::::from_payload(payload)); 64 | returned 65 | } 66 | 67 | /// Schedule a [`Stream`] to be run. 68 | /// 69 | /// Fails if another stream was already scheduled. 70 | pub fn schedule_stream(&mut self, stream: S) -> Result<(), S> 71 | where 72 | S: Stream> + Send + 'static, 73 | { 74 | if self.stream.is_some() { 75 | Err(stream) 76 | } else { 77 | self.stream = Some(Box::pin(stream)); 78 | Ok(()) 79 | } 80 | } 81 | 82 | /// Remove the [`Stream`] if it was scheduled. 83 | pub fn remove_stream( 84 | &mut self, 85 | ) -> Option> + Send>>> { 86 | self.stream.take() 87 | } 88 | } 89 | 90 | impl HandlerState for Scheduler 91 | where 92 | H: Handler, 93 | P: Protocol + HandledBy, 94 | { 95 | type Protocol = P; 96 | type InboxType = Inbox

; 97 | 98 | fn from_inbox(inbox: Self::InboxType) -> Self { 99 | Self { 100 | inner_state: inbox, 101 | futures: FuturesUnordered::new(), 102 | stream: None, 103 | } 104 | } 105 | 106 | fn poll_next_item(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { 107 | let this = &mut *self; 108 | #[allow(unused)] 109 | let mut dead = true; 110 | 111 | if let Poll::Ready(Some(item)) = Pin::new(&mut this.inner_state).poll_next_unpin(cx) { 112 | match item { 113 | Ok(protocol) => return Poll::Ready(HandlerItem::Protocol(protocol)), 114 | Err(_) => return Poll::Ready(HandlerItem::Event(Event::Halted)), 115 | } 116 | } else { 117 | dead = false 118 | } 119 | 120 | if let Poll::Ready(Some(item)) = this.futures.poll_next_unpin(cx) { 121 | if !item.is_dead() { 122 | return Poll::Ready(item); 123 | } 124 | } else { 125 | dead = false 126 | } 127 | 128 | if let Some(stream) = &mut this.stream { 129 | if let Poll::Ready(Some(item)) = stream.poll_next_unpin(cx) { 130 | if !item.is_dead() { 131 | return Poll::Ready(item); 132 | } 133 | } else { 134 | dead = false 135 | } 136 | } 137 | 138 | if dead { 139 | Poll::Ready(HandlerItem::Event(Event::Dead)) 140 | } else { 141 | Poll::Pending 142 | } 143 | } 144 | } 145 | 146 | impl ActorRef for Scheduler { 147 | type ActorType = Inbox

; 148 | 149 | fn channel_ref(actor_ref: &Self) -> &Arc<::Channel> { 150 | Inbox::

::channel_ref(&actor_ref.inner_state) 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /zestors/src/actor_type/multi_halter/channel.rs: -------------------------------------------------------------------------------- 1 | use crate::all::*; 2 | use event_listener::{Event, EventListener}; 3 | use std::{ 4 | any::Any, 5 | sync::{ 6 | atomic::{AtomicI32, AtomicUsize, Ordering}, 7 | Arc, 8 | }, 9 | }; 10 | 11 | /// The [Channel] of a [`MultiHalter`]. 12 | #[derive(Debug)] 13 | pub struct MultiHalterChannel { 14 | address_count: AtomicUsize, 15 | halter_count: AtomicUsize, 16 | to_halt: AtomicI32, 17 | actor_id: ActorId, 18 | exit_event: Event, 19 | halt_event: Event, 20 | } 21 | 22 | impl MultiHalterChannel { 23 | pub(crate) fn new(address_count: usize, halter_count: usize, actor_id: ActorId) -> Self { 24 | Self { 25 | address_count: AtomicUsize::new(address_count), 26 | halter_count: AtomicUsize::new(halter_count), 27 | to_halt: AtomicI32::new(0), 28 | actor_id, 29 | exit_event: Event::new(), 30 | halt_event: Event::new(), 31 | } 32 | } 33 | 34 | pub(crate) fn get_halt_listener(&self) -> EventListener { 35 | self.halt_event.listen() 36 | } 37 | 38 | pub(crate) fn should_halt(&self) -> bool { 39 | let halt_count = self.to_halt.fetch_sub(1, Ordering::AcqRel); 40 | if halt_count > 0 { 41 | true 42 | } else { 43 | if halt_count < -1000 { 44 | // If it was smaller than -1000, reset the halt-count to 0 to prevent overflow. 45 | self.to_halt 46 | .fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| { 47 | // Check here again, since it might have been updated in the mean time. 48 | if x < -1_000 { 49 | Some(0) 50 | } else { 51 | Some(x) 52 | } 53 | }) 54 | .unwrap(); 55 | }; 56 | false 57 | } 58 | } 59 | 60 | pub(crate) fn decrement_halter_count(&self, n: usize) { 61 | self.halter_count.fetch_sub(n, Ordering::AcqRel); 62 | } 63 | } 64 | 65 | impl Channel for MultiHalterChannel { 66 | fn close(&self) -> bool { 67 | false 68 | } 69 | fn halt_some(&self, n: u32) { 70 | let n = n.try_into().unwrap_or(i32::MAX); 71 | self.to_halt 72 | .fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| { 73 | Some(x.saturating_add(n)) 74 | }) 75 | .unwrap(); 76 | self.halt_event.notify(n as usize) 77 | } 78 | fn halt(&self) { 79 | self.to_halt.store(i32::MAX, Ordering::Release); 80 | self.halt_event.notify(usize::MAX) 81 | } 82 | fn process_count(&self) -> usize { 83 | self.halter_count.load(Ordering::Acquire) 84 | } 85 | fn msg_count(&self) -> usize { 86 | 0 87 | } 88 | fn address_count(&self) -> usize { 89 | self.address_count.load(Ordering::Acquire) 90 | } 91 | fn is_closed(&self) -> bool { 92 | true 93 | } 94 | fn has_exited(&self) -> bool { 95 | self.halter_count.load(Ordering::Acquire) == 0 96 | } 97 | fn get_exit_listener(&self) -> EventListener { 98 | self.exit_event.listen() 99 | } 100 | fn actor_id(&self) -> ActorId { 101 | self.actor_id 102 | } 103 | fn capacity(&self) -> Capacity { 104 | Capacity::Bounded(0) 105 | } 106 | fn increment_address_count(&self) -> usize { 107 | self.address_count.fetch_add(1, Ordering::Acquire) 108 | } 109 | fn decrement_address_count(&self) -> usize { 110 | let prev_address_count = self.address_count.fetch_sub(1, Ordering::Acquire); 111 | assert!(prev_address_count >= 1); 112 | prev_address_count 113 | } 114 | fn try_increment_process_count(&self) -> Result { 115 | let result = self 116 | .halter_count 117 | .fetch_update(Ordering::AcqRel, Ordering::Acquire, |val| { 118 | if val < 1 { 119 | None 120 | } else { 121 | Some(val + 1) 122 | } 123 | }); 124 | 125 | match result { 126 | Ok(prev) => Ok(prev), 127 | Err(_) => Err(AddProcessError::ActorHasExited), 128 | } 129 | } 130 | 131 | fn try_send_box(&self, boxed: BoxPayload) -> Result<(), TrySendCheckedError> { 132 | Err(TrySendCheckedError::NotAccepted(boxed)) 133 | } 134 | 135 | fn force_send_box(&self, boxed: BoxPayload) -> Result<(), TrySendCheckedError> { 136 | Err(TrySendCheckedError::NotAccepted(boxed)) 137 | } 138 | 139 | fn send_box_blocking(&self, boxed: BoxPayload) -> Result<(), SendCheckedError> { 140 | Err(SendCheckedError::NotAccepted(boxed)) 141 | } 142 | 143 | fn send_box<'a>( 144 | &'a self, 145 | boxed: BoxPayload, 146 | ) -> std::pin::Pin< 147 | Box>> + Send + 'a>, 148 | > { 149 | Box::pin(async move { Err(SendCheckedError::NotAccepted(boxed)) }) 150 | } 151 | 152 | fn accepts(&self, _id: &std::any::TypeId) -> bool { 153 | false 154 | } 155 | 156 | fn into_any(self: Arc) -> Arc { 157 | self 158 | } 159 | 160 | fn into_dyn(self: Arc) -> Arc { 161 | self 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /supervision/src/supervision/child_spec/child_start_spec.rs: -------------------------------------------------------------------------------- 1 | use super::{get_default_shutdown_time, FatalError}; 2 | use crate::all::*; 3 | use async_trait::async_trait; 4 | use futures::{future::BoxFuture, Future, FutureExt}; 5 | use pin_project::pin_project; 6 | use std::{ 7 | marker::PhantomData, 8 | pin::Pin, 9 | task::{ready, Context, Poll}, 10 | time::Duration, 11 | }; 12 | 13 | pub struct BoxStartChildSpec { 14 | start_fn: Box< 15 | dyn FnOnce( 16 | Option>, 17 | ) -> BoxFuture<'static, Result<(Child, Ref), FatalError>>, 18 | >, 19 | exit_fn: Box< 20 | dyn FnOnce( 21 | Option>, 22 | ) -> BoxFuture<'static, Result<(Child, Ref), FatalError>>, 23 | >, 24 | } 25 | 26 | #[pin_project] 27 | pub struct StartChildSpec { 28 | start_fn: SFun, 29 | exit_fn: EFun, 30 | with: T, 31 | phantom: PhantomData<(SFut, EFut, E, A, Ref)>, 32 | } 33 | 34 | impl StartChildSpec { 35 | pub fn new(start_fn: SFun, exit_fn: EFun, with: T) -> Self { 36 | Self { 37 | start_fn, 38 | exit_fn, 39 | with, 40 | phantom: PhantomData, 41 | } 42 | } 43 | } 44 | 45 | #[async_trait] 46 | impl Specification 47 | for StartChildSpec 48 | where 49 | SFun: FnOnce(T) -> SFut + Send + Clone + 'static, 50 | SFut: Future< 51 | Output = Result< 52 | (Child, Ref), 53 | StartError>, 54 | >, 55 | > + Send 56 | + 'static, 57 | EFun: (FnOnce(Result) -> EFut) + Send + Clone + 'static, 58 | EFut: Future> + Send + 'static, 59 | E: Send + 'static, 60 | T: Send + 'static, 61 | A: ActorType + Send + 'static, 62 | Ref: Send + 'static, 63 | { 64 | type Ref = Ref; 65 | type Supervisee = StartChildSupervisee; 66 | 67 | async fn start_supervised(self) -> StartResult { 68 | let start_fut = (self.start_fn.clone())(self.with); 69 | match start_fut.await { 70 | Ok((child, reference)) => { 71 | let supervisee = StartChildSupervisee { 72 | child, 73 | start_fn: Some(self.start_fn), 74 | exit_fn: Some(self.exit_fn), 75 | phantom: PhantomData, 76 | exit_fut: None, 77 | }; 78 | Ok((supervisee, reference)) 79 | } 80 | Err(e) => Err(e), 81 | } 82 | } 83 | } 84 | 85 | #[pin_project] 86 | pub struct StartChildSupervisee 87 | where 88 | E: Send + 'static, 89 | A: ActorType, 90 | { 91 | child: Child, 92 | start_fn: Option, 93 | exit_fn: Option, 94 | #[pin] 95 | exit_fut: Option, 96 | phantom: PhantomData<(SFut, EFut, D, Ref)>, 97 | } 98 | 99 | impl Supervisee 100 | for StartChildSupervisee 101 | where 102 | SFun: FnOnce(T) -> SFut + Send + Clone + 'static, 103 | SFut: Future< 104 | Output = Result< 105 | (Child, Ref), 106 | StartError>, 107 | >, 108 | > + Send 109 | + 'static, 110 | EFun: (FnOnce(Result) -> EFut) + Send + Clone + 'static, 111 | EFut: Future> + Send + 'static, 112 | E: Send + 'static, 113 | T: Send + 'static, 114 | A: ActorType + Send + 'static, 115 | Ref: Send + 'static, 116 | { 117 | type Spec = StartChildSpec; 118 | 119 | fn poll_supervise( 120 | self: Pin<&mut Self>, 121 | cx: &mut Context, 122 | ) -> Poll> { 123 | let mut this = self.project(); 124 | loop { 125 | if let Some(exit_fut) = this.exit_fut.as_mut().as_pin_mut() { 126 | break exit_fut.poll(cx).map_ok(|res| match res { 127 | Some(with) => Some(StartChildSpec { 128 | start_fn: this.start_fn.take().unwrap(), 129 | exit_fn: this.exit_fn.take().unwrap(), 130 | with, 131 | phantom: PhantomData, 132 | }), 133 | None => None, 134 | }); 135 | } else { 136 | let exit = ready!(this.child.poll_unpin(cx)); 137 | let exit_fut = (this.exit_fn.as_ref().unwrap().clone())(exit); 138 | unsafe { 139 | *this.exit_fut.as_mut().get_unchecked_mut() = Some(exit_fut); 140 | }; 141 | } 142 | } 143 | } 144 | 145 | fn shutdown_time(self: Pin<&Self>) -> Duration { 146 | match self.child.link() { 147 | Link::Detached => get_default_shutdown_time(), 148 | Link::Attached(duration) => duration.to_owned(), 149 | } 150 | } 151 | 152 | fn halt(self: Pin<&mut Self>) { 153 | self.child.halt() 154 | } 155 | 156 | fn abort(self: Pin<&mut Self>) { 157 | self.project().child.abort(); 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /zestors/tests/lib.rs: -------------------------------------------------------------------------------- 1 | use futures::{future::pending, StreamExt}; 2 | use std::{collections::HashSet, time::Duration}; 3 | use zestors::{ 4 | actor_reference::{ActorRefExt, Transformable}, 5 | actor_type::Inbox, 6 | messaging::RecvError, 7 | protocol, 8 | spawning::{spawn, spawn_many, spawn_many_with, spawn_with, BackPressure, Capacity, Link}, 9 | }; 10 | 11 | #[tokio::test] 12 | async fn spawn_and_abort() { 13 | let (mut child, _address) = spawn(|_: Inbox<()>| async move { 14 | let () = pending().await; 15 | }); 16 | child.abort(); 17 | assert!(child.await.unwrap_err().is_abort()); 18 | } 19 | 20 | #[tokio::test] 21 | async fn spawn_await_address() { 22 | let (mut child, address) = spawn(|_: Inbox<()>| async move { 23 | let () = pending().await; 24 | }); 25 | child.abort(); 26 | address.await; 27 | } 28 | 29 | #[tokio::test] 30 | async fn spawn_and_panic() { 31 | let (child, _address) = spawn(|_: Inbox<()>| async move { panic!() }); 32 | assert!(child.await.unwrap_err().is_panic()); 33 | } 34 | 35 | #[tokio::test] 36 | async fn spawn_and_normal_exit() { 37 | let (child, _address) = spawn(|_: Inbox<()>| async move {}); 38 | assert!(child.await.is_ok()); 39 | } 40 | 41 | #[tokio::test] 42 | async fn spawn_and_halt() { 43 | let (child, _address) = spawn(|mut inbox: Inbox<()>| async move { 44 | assert_eq!(inbox.recv().await.unwrap_err(), RecvError::Halted); 45 | }); 46 | child.halt(); 47 | assert!(child.await.is_ok()); 48 | } 49 | 50 | #[tokio::test] 51 | async fn spawn_and_drop() { 52 | let (child, address) = spawn_with( 53 | Link::Attached(Duration::from_millis(10).into()), 54 | Capacity::Bounded(10), 55 | |mut inbox: Inbox<()>| async move { 56 | assert_eq!(inbox.recv().await.unwrap_err(), RecvError::Halted); 57 | let () = pending().await; 58 | }, 59 | ); 60 | drop(child); 61 | address.await; 62 | } 63 | 64 | #[tokio::test] 65 | async fn spawn_and_drop_detached() { 66 | let (child, address) = spawn_with( 67 | Link::Detached, 68 | Capacity::BackPressure(BackPressure::default()), 69 | |mut inbox: Inbox<()>| async move { 70 | assert_eq!(inbox.recv().await.unwrap(), ()); 71 | }, 72 | ); 73 | drop(child); 74 | tokio::time::sleep(Duration::from_millis(10)).await; 75 | address.send(()).await.unwrap(); 76 | address.await; 77 | } 78 | 79 | #[tokio::test] 80 | async fn base_counts() { 81 | let (mut child, address) = spawn(|inbox: Inbox<()>| async move { 82 | pending::<()>().await; 83 | drop(inbox); 84 | }); 85 | assert_eq!(child.address_count(), 1); 86 | assert_eq!(child.process_count(), 1); 87 | assert_eq!(address.msg_count(), 0); 88 | child.abort(); 89 | } 90 | 91 | #[tokio::test] 92 | async fn address_counts() { 93 | let (mut child, address) = spawn(|inbox: Inbox<()>| async move { 94 | pending::<()>().await; 95 | drop(inbox); 96 | }); 97 | assert_eq!(child.address_count(), 1); 98 | let address2 = address.clone(); 99 | assert_eq!(child.address_count(), 2); 100 | drop(address2); 101 | assert_eq!(child.address_count(), 1); 102 | child.abort(); 103 | } 104 | 105 | #[tokio::test] 106 | async fn inbox_counts() { 107 | let (pool, _address) = spawn_many(0..4, |_, mut inbox: Inbox<()>| async move { 108 | inbox.recv().await.unwrap_err(); 109 | }); 110 | let mut pool = pool.into_dyn(); 111 | assert_eq!(pool.process_count(), 4); 112 | 113 | pool.halt_some(1); 114 | tokio::time::sleep(Duration::from_millis(10)).await; 115 | assert_eq!(pool.process_count(), 3); 116 | 117 | pool.try_spawn_onto(|mut inbox: Inbox<()>| async move { 118 | inbox.recv().await.unwrap_err(); 119 | }) 120 | .unwrap(); 121 | assert_eq!(pool.process_count(), 4); 122 | 123 | pool.halt_some(2); 124 | tokio::time::sleep(Duration::from_millis(10)).await; 125 | assert_eq!(pool.process_count(), 2); 126 | 127 | pool.halt(); 128 | tokio::time::sleep(Duration::from_millis(10)).await; 129 | assert_eq!(pool.process_count(), 0); 130 | } 131 | 132 | #[protocol] 133 | enum U32Protocol { 134 | U32(u32), 135 | } 136 | 137 | #[tokio::test] 138 | async fn pooled_messaging_split() { 139 | let (pool, address) = spawn_many_with( 140 | Link::default(), 141 | Capacity::Bounded(5), 142 | 0..3, 143 | |_, mut inbox: Inbox| async move { 144 | let mut numbers = Vec::new(); 145 | loop { 146 | match inbox.recv().await { 147 | Ok(U32Protocol::U32(msg)) => { 148 | tokio::time::sleep(Duration::from_millis(1)).await; 149 | numbers.push(msg); 150 | } 151 | Err(signal) => match signal { 152 | RecvError::Halted => break Ok(numbers), 153 | RecvError::ClosedAndEmpty => break Err(()), 154 | }, 155 | } 156 | } 157 | }, 158 | ); 159 | 160 | for i in 0..30 { 161 | address.send(i).await.unwrap(); 162 | } 163 | 164 | tokio::time::sleep(Duration::from_millis(20)).await; 165 | address.halt(); 166 | 167 | let res = pool 168 | .map(|e| e.unwrap().unwrap()) 169 | .fold(HashSet::new(), |mut acc, vals| async move { 170 | assert!(vals.len() >= 9); 171 | assert!(vals.len() <= 11); 172 | for val in vals { 173 | assert!(acc.insert(val)); 174 | } 175 | acc 176 | }) 177 | .await; 178 | assert_eq!(res.len(), 30) 179 | } 180 | -------------------------------------------------------------------------------- /zestors/src/actor_type/actor_type.rs: -------------------------------------------------------------------------------- 1 | use crate::all::*; 2 | use futures::future::BoxFuture; 3 | use std::{any::TypeId, marker::PhantomData, sync::Arc}; 4 | 5 | /// The [`ActorType`] defines what kind of inbox the actor uses. An actor-type can 6 | /// either be statically or dynamically typed: 7 | /// - __Static__: A static actor-type is defined as the [`InboxType`]. 8 | /// - __Dynamic__: A dynamic actor-type is defined as a [`struct@DynActor`], usually written 9 | /// as [`DynActor!(Msg1, Msg2, ..]`)(DynActor!). 10 | /// 11 | /// The actor-type is used as the generic parameter `A` in for example a [`Child<_, A, _>`] and 12 | /// an [`Address`]. These can then be `transformed` into and between dynamic actor-types. 13 | pub trait ActorType { 14 | /// The unerlying [`Channel`] that this actor uses. 15 | type Channel: Channel + ?Sized; 16 | } 17 | 18 | /// A specialization of [`ActorType`] for dynamic actor-types. 19 | pub trait DynActorType: ActorType { 20 | /// Get all [`Message`] type-ids that this actor accepts. 21 | fn msg_ids() -> Box<[TypeId]>; 22 | } 23 | 24 | /// All actors are spawned with an [`InboxType`] which defines the [`ActorType`] of that actor. 25 | pub trait InboxType: ActorType + ActorRef + Send + 'static { 26 | /// The inbox's configuration. 27 | type Config: Default + Send; 28 | 29 | /// Sets up the channel with the given address-count and actor-id. 30 | fn init_single_inbox( 31 | config: Self::Config, 32 | address_count: usize, 33 | actor_id: ActorId, 34 | ) -> (Arc, Self); 35 | } 36 | 37 | /// An [`InboxType`] that allows for spawning multiple processes onto an actor. 38 | pub trait MultiProcessInbox: InboxType { 39 | /// Sets up the channel, preparing for x processes to be spawned. 40 | fn init_multi_inbox( 41 | config: Self::Config, 42 | process_count: usize, 43 | address_count: usize, 44 | actor_id: ActorId, 45 | ) -> Arc; 46 | 47 | /// Create an inbox from the channel. 48 | /// 49 | /// The inbox-count should be incremented outside of this method. 50 | fn from_channel(channel: Arc) -> Self; 51 | } 52 | 53 | /// A dynamic [`ActorType`] that accepts specific messages: 54 | /// - `DynActor` -> Accepts no messages. 55 | /// - `DynActor` -> Accepts two messages: `u32` and `u64`. 56 | /// 57 | /// See [`DynActor!`] for writing this types in a simpler way. 58 | #[derive(Debug)] 59 | pub struct DynActor(PhantomData T>); 60 | 61 | 62 | impl ActorType for DynActor { 63 | type Channel = dyn Channel; 64 | } 65 | 66 | impl Accepts for DynActor 67 | where 68 | Self: DynActorType + TransformInto, 69 | M: Message + Send + 'static, 70 | M::Returned: Send, 71 | { 72 | fn try_send(channel: &Self::Channel, msg: M) -> Result> { 73 | channel.try_send_checked(msg).map_err(|e| match e { 74 | TrySendCheckedError::Full(msg) => TrySendError::Full(msg), 75 | TrySendCheckedError::Closed(msg) => TrySendError::Closed(msg), 76 | TrySendCheckedError::NotAccepted(_) => { 77 | panic!("Sent message which was not accepted by actor") 78 | } 79 | }) 80 | } 81 | 82 | fn force_send(channel: &Self::Channel, msg: M) -> Result> { 83 | channel.force_send_checked(msg).map_err(|e| match e { 84 | TrySendCheckedError::Full(msg) => TrySendError::Full(msg), 85 | TrySendCheckedError::Closed(msg) => TrySendError::Closed(msg), 86 | TrySendCheckedError::NotAccepted(_) => { 87 | panic!("Sent message which was not accepted by actor") 88 | } 89 | }) 90 | } 91 | 92 | fn send_blocking(channel: &Self::Channel, msg: M) -> Result> { 93 | channel.send_blocking_checked(msg).map_err(|e| match e { 94 | SendCheckedError::Closed(msg) => SendError(msg), 95 | SendCheckedError::NotAccepted(_) => { 96 | panic!("Sent message which was not accepted by actor") 97 | } 98 | }) 99 | } 100 | 101 | type SendFut<'a> = BoxFuture<'a, Result>>; 102 | 103 | fn send(channel: &Self::Channel, msg: M) -> Self::SendFut<'_> { 104 | Box::pin(async move { 105 | channel.send_checked(msg).await.map_err(|e| match e { 106 | SendCheckedError::Closed(msg) => SendError(msg), 107 | SendCheckedError::NotAccepted(_) => { 108 | panic!("Sent message which was not accepted by actor") 109 | } 110 | }) 111 | }) 112 | } 113 | } 114 | 115 | /// Allows for the transformation of one [`ActorType`]'s [`Channel`] into another one's. 116 | pub trait TransformInto: ActorType { 117 | /// Transform the channel into another one. 118 | fn transform_into(channel: Arc) -> Arc; 119 | } 120 | 121 | #[cfg(test)] 122 | mod test { 123 | use crate::DynActor; 124 | 125 | #[test] 126 | fn dynamic_definitions_compile() { 127 | type _1 = DynActor!(()); 128 | type _2 = DynActor!((), ()); 129 | type _3 = DynActor!((), (), ()); 130 | type _4 = DynActor!((), (), (), ()); 131 | type _5 = DynActor!((), (), (), (), ()); 132 | type _6 = DynActor!((), (), (), (), (), ()); 133 | type _7 = DynActor!((), (), (), (), (), (), ()); 134 | type _8 = DynActor!((), (), (), (), (), (), (), ()); 135 | type _9 = DynActor!((), (), (), (), (), (), (), (), ()); 136 | type _10 = DynActor!((), (), (), (), (), (), (), (), (), ()); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /zestors/src/handler/mod.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | # Overview 3 | 4 | A big advantage of using an actor-system is the ability to write actors in a declarative manner. 5 | This module implements a way to write actors of this kind with the [`Handler`] trait. 6 | 7 | # Handler 8 | The [`Handler`] trait specifies properties of the actor like it's [`InboxType`] and [`Handler::Exit`]. 9 | If a handler wishes to handle message `M` it must define this in it's [`Protocol`] and implement 10 | [`HandleMessage`]. The [`Handler`]-trait can be derived with the [`Handler`](derive@crate::Handler)-macro. 11 | 12 | # HandlerState 13 | Every handler has to specify a [`HandlerState`]. This state can generate [`HandlerItem`]s that the 14 | actor subsequently handles. Examples of a handler-state are the [`Inbox`] and [`Scheduler`]. The 15 | inbox is the most basic version, which just receives messages and forwards them to the handler. The 16 | scheduler also allows for scheduling arbitrary futures and a stream on the actor which can run 17 | asynchronously; the scheduler automatically takes care of selecting messages from the inbox and 18 | futures scheduled. 19 | 20 | # Action 21 | An [`Action`] is a closure that an actor can execute with it's [`Handler::State`]. Actions can 22 | be created with the [`action!`] macro, see it's documentation for exact usage. When an actor 23 | accepts the message [`Action`], then actions (closures) can be sent and handled 24 | automatically on the actor. An upside of this is that it is now possible to send messages with 25 | generics, but a downside is that the actor's protocol is now tightly coupled to the state. 26 | 27 | | __<--__ [`spawning`](crate::spawning) | [`runtime`](crate::runtime) __-->__ | 28 | |---|---| 29 | 30 | # Example 31 | ``` 32 | #[macro_use] 33 | extern crate zestors; 34 | use zestors::{export::async_trait, prelude::*}; 35 | 36 | // Let's start by defining a message .. 37 | #[derive(Message, Debug)] 38 | #[request(u32)] 39 | pub struct PrintString { 40 | val: String, 41 | } 42 | 43 | // .. and a protocol. 44 | #[protocol] 45 | #[derive(Debug)] 46 | pub enum MyProtocol { 47 | A(u32), 48 | B(PrintString), 49 | C(Action), 50 | } 51 | 52 | // Now we can define our handler .. 53 | #[derive(Debug)] 54 | pub struct MyHandler { 55 | handled: u32, 56 | } 57 | 58 | // .. and implement the main trait `Handler` (or use #[derive(Handler)]) 59 | #[async_trait] 60 | impl Handler for MyHandler { 61 | type State = Inbox; 62 | type Exception = eyre::Report; 63 | type Stop = (); 64 | type Exit = u32; 65 | 66 | async fn handle_exit( 67 | self, 68 | _state: &mut Self::State, 69 | reason: Result, 70 | ) -> ExitFlow { 71 | match reason { 72 | // Upon ok, we exit normally. 73 | Ok(()) => ExitFlow::Exit(self.handled), 74 | // When an error occured, we also exit but log it. 75 | Err(exception) => { 76 | println!("[ERROR] Actor exited with an exception: {exception}"); 77 | ExitFlow::Exit(self.handled) 78 | } 79 | } 80 | } 81 | 82 | async fn handle_event(&mut self, state: &mut Self::State, event: Event) -> HandlerResult { 83 | // For most events we stop our actor. 84 | // When the actor is halted we just close the inbox and continue until it is empty. 85 | match event { 86 | Event::Halted => { 87 | state.close(); 88 | Ok(Flow::Continue) 89 | } 90 | Event::ClosedAndEmpty => Ok(Flow::Stop(())), 91 | Event::Dead => Ok(Flow::Stop(())), 92 | } 93 | } 94 | } 95 | 96 | // Let's handle a u32 message .. 97 | #[async_trait] 98 | impl HandleMessage for MyHandler { 99 | async fn handle_msg( 100 | &mut self, 101 | _state: &mut Self::State, 102 | msg: u32, 103 | ) -> Result, Self::Exception> { 104 | self.handled += msg; 105 | Ok(Flow::Continue) 106 | } 107 | } 108 | 109 | // .. and our custom request. 110 | #[async_trait] 111 | impl HandleMessage for MyHandler { 112 | async fn handle_msg( 113 | &mut self, 114 | _state: &mut Self::State, 115 | (msg, tx): (PrintString, Tx), 116 | ) -> Result, Self::Exception> { 117 | println!("{}", msg.val); 118 | let _ = tx.send(self.handled); 119 | self.handled += 1; 120 | Ok(Flow::Continue) 121 | } 122 | } 123 | 124 | // That was all! 125 | #[tokio::main] 126 | async fn main() { 127 | // Let's spawn our actor: 128 | let (child, address) = MyHandler { handled: 0 }.spawn(); 129 | // We can send it a basic message: 130 | address.send(10u32).await.unwrap(); 131 | 132 | // Send it a bunch of requests that will print the message. 133 | for i in 0..10 { 134 | let response = address 135 | .request(PrintString { 136 | val: String::from("Printing a message"), 137 | }) 138 | .await 139 | .unwrap(); 140 | println!("Got response {i} = {response}"); 141 | } 142 | 143 | // Or send it a custom closure with the `action!` macro 144 | child 145 | .send(action!(|handler: &mut MyHandler, _state| async move { 146 | println!("This is now 20: `{}`", handler.handled); 147 | Ok(Flow::Continue) 148 | })) 149 | .await 150 | .unwrap(); 151 | 152 | // And finally our actor can be halted and will exit! 153 | child.halt(); 154 | assert!(matches!(child.await, Ok(20))); 155 | } 156 | ``` 157 | */ 158 | 159 | #[allow(unused)] 160 | use crate::all::*; 161 | 162 | mod action; 163 | mod handler_ext; 164 | mod state; 165 | mod handler; 166 | mod event_loop; 167 | mod scheduler; 168 | pub use {action::*, handler_ext::*, state::*, handler::*, scheduler::*}; 169 | -------------------------------------------------------------------------------- /zestors/src/messaging/message.rs: -------------------------------------------------------------------------------- 1 | #[allow(unused)] 2 | use crate::all::*; 3 | use std::sync::Arc; 4 | 5 | /// Any message should implement [`Message`], and has two associated types: 6 | /// - [`Message::Payload`] -> The payload of the message that is sent to the actor. 7 | /// - [`Message::Returned`] -> The value that is returned when the message is sent. 8 | /// 9 | /// A simple message has the following associated types: 10 | /// - `Payload = Self` 11 | /// - `Returned = ()` 12 | /// 13 | /// A request of type `R` has the following associated types: 14 | /// - `Payload = (Self, Tx)` 15 | /// - `Returned = Rx` 16 | /// 17 | /// # Derive 18 | /// [`Message`] can be derived with the [`derive@Message`] macro. 19 | pub trait Message: Sized { 20 | /// The payload of the message that is sent to the actor. 21 | type Payload: Send + 'static; 22 | 23 | /// The value that is returned when the message is sent. 24 | type Returned; 25 | 26 | /// This is called before the message is sent. 27 | fn create(self) -> (Self::Payload, Self::Returned); 28 | 29 | /// This is called if the message cannot be sent. 30 | fn cancel(sent: Self::Payload, returned: Self::Returned) -> Self; 31 | } 32 | 33 | /// A version of the [`Message`] trait generic over `M`, used as the `[msg(..)]` attribute for the [`derive@Message`] derive 34 | /// macro. 35 | /// 36 | /// This is implemented for `()`, [`Rx<_>`] and [`Tx<_>`] 37 | pub trait MessageDerive { 38 | /// See [`Message::Payload`]. 39 | type Payload; 40 | 41 | /// See [`Message::Returned`]. 42 | type Returned; 43 | 44 | /// See [`Message::create`]. 45 | fn create(msg: M) -> (Self::Payload, Self::Returned); 46 | 47 | /// See [`Message::cancel`]. 48 | fn cancel(sent: Self::Payload, returned: Self::Returned) -> M; 49 | } 50 | 51 | impl MessageDerive for () { 52 | type Payload = M; 53 | type Returned = (); 54 | fn create(msg: M) -> (M, ()) { 55 | (msg, ()) 56 | } 57 | fn cancel(payload: M, _returns: ()) -> M { 58 | payload 59 | } 60 | } 61 | 62 | //------------------------------------------------------------------------------------------------ 63 | // Message: Default implementations 64 | //------------------------------------------------------------------------------------------------ 65 | 66 | macro_rules! implement_message_for_base_types { 67 | ($( 68 | $ty:ty 69 | ),*) => { 70 | $( 71 | impl Message for $ty { 72 | type Payload = Self; 73 | type Returned = (); 74 | fn create(self) -> (Self, ()) { 75 | (self, ()) 76 | } 77 | fn cancel(payload: Self, _returns: ()) -> Self { 78 | payload 79 | } 80 | } 81 | )* 82 | }; 83 | } 84 | implement_message_for_base_types! { 85 | u8, u16, u32, u64, u128, 86 | i8, i16, i32, i64, i128, 87 | (), 88 | String, &'static str 89 | } 90 | 91 | macro_rules! implement_message_for_wrappers { 92 | ($( 93 | $wrapper:ty 94 | $(where $_:ty: $where:ident)* 95 | ,)*) => { 96 | $( 97 | impl Message for $wrapper 98 | where M: SimpleMessage + Send + 'static + $($where +)* 99 | { 100 | type Payload = Self; 101 | type Returned = (); 102 | fn create(self) -> (Self, ()) { 103 | (self, ()) 104 | } 105 | fn cancel(payload: Self, _returns: ()) -> Self { 106 | payload 107 | } 108 | } 109 | )* 110 | }; 111 | } 112 | implement_message_for_wrappers!( 113 | Box, 114 | Arc where M: Sync, 115 | Vec, 116 | Box<[M]>, 117 | ); 118 | 119 | macro_rules! implement_message_kind_and_message_for_tuples { 120 | ($( 121 | ($($id:ident: $na:ident + $na2:ident),*), 122 | )*) => { 123 | $( 124 | impl<$($id),*> Message for ($($id,)*) 125 | where 126 | $($id: SimpleMessage + Send + 'static,)* 127 | { 128 | type Payload = Self; 129 | type Returned = (); 130 | fn create(self) -> (Self, ()) { 131 | (self, ()) 132 | } 133 | fn cancel(payload: Self, _returns: ()) -> Self { 134 | payload 135 | } 136 | } 137 | )* 138 | }; 139 | } 140 | implement_message_kind_and_message_for_tuples!( 141 | (M1: m1 + m_1), 142 | (M1: m1 + m_1, M2: m2 + m_2), 143 | (M1: m1 + m_1, M2: m2 + m_2, M3: m3 + m_3), 144 | (M1: m1 + m_1, M2: m2 + m_2, M3: m3 + m_3, M4: m4 + m_4), 145 | ( 146 | M1: m1 + m_1, 147 | M2: m2 + m_2, 148 | M3: m3 + m_3, 149 | M4: m4 + m_4, 150 | M5: m5 + m_5 151 | ), 152 | ( 153 | M1: m1 + m_1, 154 | M2: m2 + m_2, 155 | M3: m3 + m_3, 156 | M4: m4 + m_4, 157 | M5: m5 + m_5, 158 | M6: m6 + m_6 159 | ), 160 | ( 161 | M1: m1 + m_1, 162 | M2: m2 + m_2, 163 | M3: m3 + m_3, 164 | M4: m4 + m_4, 165 | M5: m5 + m_5, 166 | M6: m6 + m_6, 167 | M7: m7 + m_7 168 | ), 169 | ( 170 | M1: m1 + m_1, 171 | M2: m2 + m_2, 172 | M3: m3 + m_3, 173 | M4: m4 + m_4, 174 | M5: m5 + m_5, 175 | M6: m6 + m_6, 176 | M7: m7 + m_7, 177 | M8: m8 + m_8 178 | ), 179 | ( 180 | M1: m1 + m_1, 181 | M2: m2 + m_2, 182 | M3: m3 + m_3, 183 | M4: m4 + m_4, 184 | M5: m5 + m_5, 185 | M6: m6 + m_6, 186 | M7: m7 + m_7, 187 | M8: m8 + m_8, 188 | M9: m9 + m_9 189 | ), 190 | ( 191 | M1: m1 + m_1, 192 | M2: m2 + m_2, 193 | M3: m3 + m_3, 194 | M4: m4 + m_4, 195 | M5: m5 + m_5, 196 | M6: m6 + m_6, 197 | M7: m7 + m_7, 198 | M8: m8 + m_8, 199 | M9: m9 + m_9, 200 | M10: m10 + m_10 201 | ), 202 | ); 203 | 204 | trait SimpleMessage: Message {} 205 | impl SimpleMessage for T where T: Message {} 206 | -------------------------------------------------------------------------------- /supervision/src/supervision/mod.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | # Supervisee lifecycle 3 | 4 | #### __`(1)` Specification__: 5 | Every supervisee starts out as a [`Specification`]. The supervisee is not running, and 6 | can be started using [`Specification::start`]. This returns the [`Specification::StartFut`] `(2)`. 7 | 8 | In this state the supervisee can also be queried for it's [`Specification::start_timeout`]. 9 | 10 | #### __`(2)` Starting__: 11 | The supervisee is currently starting as a [`Specification::StartFut`]. If the future completes 12 | successfully then we get a [`Supervisee`] `(3)` that can be supervised. If however the supervisee 13 | fails to start, it returns one of three errors: 14 | - [`StartError::Unrecoverable(Box)`] : An unrecoverable error occured during starting. 15 | This should backpropagate to supervisors until it can be handled or until the application 16 | as a whole shuts down `(5)`. 17 | - [`StartError::Failed(Specification)`] : Starting has failed, and the original specification 18 | is returned `(1)`. This specification may be used to restart the supervisee. 19 | - [`StartError::Completed`] : Starting has failed because the supervisee does not have 20 | any work left to be done and is therefore completed `(4)`. 21 | 22 | If starting the supervisee takes longer than it's `start_timeout`, then the future may be 23 | dropped `(5)`. 24 | 25 | #### __`(3)` Supervisee__: 26 | The supervisee is running and can be supervised by awaiting the [`Supervisee`]. 27 | The supervisee can exit successfully in two ways: 28 | - `Some(Specification)` : The supervisee has exited, but is not yet completed. It would like to be 29 | restarted with it's [`Specification`] `(5)`. 30 | - `None` : The supervisee has exited and has completed all it's task `(4)`. 31 | 32 | The supervisee can also exit with a `Err(Box)`. This indicates an unrecoverable 33 | error `(5)`. 34 | 35 | The supervisee can be halted and aborted using [`Supervisee::halt`] and [`Supervisee::abort`]. The 36 | supervisee also defines a [`Supervisee::abort_timeout`] which gives an upper limit how long the 37 | supervisee needs to shut down after being halted; if the supervisee exceeds this limit, it may 38 | be aborted or dropped `(5)`. 39 | 40 | #### __`(4)` Completed__: 41 | The supervisee has completed it's task successfully and can not be restarted. 42 | 43 | #### __`(5)` Unrecoverable__: 44 | The supervisee is not completed but is not able to restart either. 45 | 46 | | __<--__ [`handler`](crate::handler) | [`distribution`](crate::distribution) __-->__ | 47 | |---|---| 48 | */ 49 | 50 | mod child_spec; 51 | mod combinators; 52 | mod restart_limiter; 53 | mod supervisor; 54 | mod supervisor2; 55 | mod traits; 56 | mod traits_ext; 57 | mod handler_spec; 58 | pub use child_spec::*; 59 | use futures::Future; 60 | pub(super) use restart_limiter::*; 61 | pub use {combinators::*, traits_ext::*, handler_spec::*}; 62 | pub use {traits::*}; // pub use supervisor::*; 63 | 64 | #[allow(unused)] 65 | use crate::all::*; 66 | use std::{convert::Infallible, error::Error}; 67 | 68 | //------------------------------------------------------------------------------------------------ 69 | // Private types 70 | //------------------------------------------------------------------------------------------------ 71 | 72 | 73 | pub fn spec_from_start_function( 74 | function: impl FnOnce() -> Fut + Clone + Send, 75 | ) -> impl Specification 76 | where 77 | Fut: Future, Ref), Err>> + Send, 78 | E: Send + 'static, 79 | A: ActorType, 80 | Ref: Send + 'static, 81 | { 82 | TodoSpec(todo!()) 83 | } 84 | 85 | // pub fn spec_from_startable( 86 | // f: impl FnOnce() -> Fut + Clone + Send, 87 | // link: Link, 88 | // cfg: ::Config, 89 | // ) -> impl Specification 90 | // where 91 | // Fut: Future + Send, 92 | // S: Startable, 93 | // ::Config: Clone, 94 | // I: Send, 95 | // { 96 | // spec_from_start_function(move || async move { 97 | // let init = f().await; 98 | // S::start_with(init, link, cfg).await 99 | // }) 100 | // } 101 | 102 | // pub fn spec_from_spawn_function( 103 | // function: impl FnOnce() -> (Child, Address) + Clone + Send, 104 | // ) -> impl Specification> 105 | // where 106 | // E: Send + 'static, 107 | // A: ActorType + 'static, 108 | // { 109 | // spec_from_start_function(|| async move { Ok::<_, Infallible>(function()) }) 110 | // } 111 | 112 | // pub fn spec_from_spawnable( 113 | // init: S, 114 | // link: Link, 115 | // cfg: ::Config, 116 | // ) -> impl Specification> 117 | // where 118 | // S: Spawnable + Clone + Send, 119 | // ::Config: Clone, 120 | // { 121 | // spec_from_spawn_function(move || init.spawn_with(link, cfg)) 122 | // } 123 | 124 | //------------------------------------------------------------------------------------------------ 125 | // Todo 126 | //------------------------------------------------------------------------------------------------ 127 | 128 | struct TodoSpec(T); 129 | 130 | impl Specification for TodoSpec { 131 | type Ref = T; 132 | 133 | type Supervisee = TodoSpec; 134 | 135 | fn start_supervised<'async_trait>( 136 | self, 137 | ) -> core::pin::Pin< 138 | Box< 139 | dyn core::future::Future> 140 | + core::marker::Send 141 | + 'async_trait, 142 | >, 143 | > 144 | where 145 | Self: 'async_trait, 146 | { 147 | todo!() 148 | } 149 | } 150 | 151 | impl Supervisee for TodoSpec { 152 | type Spec = TodoSpec; 153 | 154 | fn poll_supervise( 155 | self: std::pin::Pin<&mut Self>, 156 | cx: &mut std::task::Context, 157 | ) -> std::task::Poll> { 158 | todo!() 159 | } 160 | 161 | fn shutdown_time(self: std::pin::Pin<&Self>) -> std::time::Duration { 162 | todo!() 163 | } 164 | 165 | fn halt(self: std::pin::Pin<&mut Self>) { 166 | todo!() 167 | } 168 | 169 | fn abort(self: std::pin::Pin<&mut Self>) { 170 | todo!() 171 | } 172 | } 173 | 174 | -------------------------------------------------------------------------------- /supervision/src/supervision2/mod.rs: -------------------------------------------------------------------------------- 1 | #[allow(unused)] 2 | use crate::all::*; 3 | use async_trait::async_trait; 4 | use futures::{future::BoxFuture, Future, FutureExt}; 5 | use pin_project::pin_project; 6 | use std::{ 7 | mem::swap, 8 | pin::Pin, 9 | task::{Context, Poll}, 10 | time::Duration, sync::Mutex, 11 | }; 12 | 13 | struct MyProcessId; 14 | 15 | 16 | trait ActorIdentifier { 17 | fn to_id(&self) -> u64; 18 | } 19 | 20 | struct BoxSpec; 21 | 22 | struct OneForOneSpec { 23 | specs: Mutex> 24 | } 25 | 26 | #[async_trait] 27 | pub trait Specification: Clone + Sized { 28 | type Supervisee; 29 | type Ref; 30 | type Error; 31 | 32 | async fn start_supervised(&self) -> Result<(Self::Supervisee, Self::Ref), Self::Error>; 33 | fn start_duration(&self) -> Duration { 34 | Duration::from_secs(1) 35 | } 36 | } 37 | 38 | pub trait Supervisee: Send + Sized { 39 | type Supervisee: Specification; 40 | type Exit; 41 | 42 | fn poll_supervise(self: Pin<&mut Self>, cx: &mut Context) -> Poll<(Self::Exit, bool)>; 43 | 44 | fn shutdown_time(self: Pin<&Self>) -> Duration; 45 | 46 | fn halt(self: Pin<&mut Self>); 47 | 48 | fn abort(self: Pin<&mut Self>); 49 | } 50 | 51 | //------------------------------------------------------------------------------------------------ 52 | // OLD STUFF 53 | //------------------------------------------------------------------------------------------------ 54 | 55 | // mod box_supervisee; 56 | 57 | // #[allow(unused)] 58 | // use crate::all::*; 59 | // use async_trait::async_trait; 60 | // use futures::{future::BoxFuture, Future, FutureExt}; 61 | // use pin_project::pin_project; 62 | // use std::{ 63 | // mem::swap, 64 | // pin::Pin, 65 | // task::{Context, Poll}, 66 | // time::Duration, 67 | // }; 68 | 69 | // use self::box_supervisee::BoxSupervisee; 70 | 71 | // pub trait Supervisee: Send + Sized + 'static { 72 | // type Ref: Send + 'static; 73 | 74 | // type Running: RunningSupervisee; 75 | 76 | // type Future: Future> + Send + 'static; 77 | 78 | // fn start_supervised(self) -> Self::Future; 79 | // } 80 | 81 | // pub trait RunningSupervisee: Send + Sized { 82 | // type Supervisee: Supervisee; 83 | 84 | // fn poll_supervise(self: Pin<&mut Self>, cx: &mut Context) -> Poll>; 85 | 86 | // fn shutdown_time(self: Pin<&Self>) -> Duration; 87 | 88 | // fn halt(self: Pin<&mut Self>); 89 | 90 | // fn abort(self: Pin<&mut Self>); 91 | // } 92 | 93 | // #[pin_project] 94 | // pub enum SuperviseeState { 95 | // Startable(S), 96 | // Starting(#[pin] BoxFuture<'static, Result<(S::Running, S::Ref), S>>), 97 | // Running(#[pin] S::Running, bool), 98 | // Complete, 99 | // } 100 | 101 | // pub struct OneForOneSupervisee { 102 | // supervisees: Vec, 103 | // } 104 | 105 | // impl Supervisee for OneForOneSupervisee { 106 | // type Ref = (); 107 | // type Running = RunningOneForOneSupervisee; 108 | // type Future = OneForOneSuperviseeFuture; 109 | 110 | // fn start_supervised(self) -> Self::Future { 111 | // OneForOneSuperviseeFuture { 112 | // inner: Some(self), 113 | // canceling: false, 114 | // } 115 | // } 116 | // } 117 | 118 | // pub struct OneForOneSuperviseeFuture { 119 | // inner: Option, 120 | // canceling: bool, 121 | // } 122 | 123 | // impl Future for OneForOneSuperviseeFuture { 124 | // type Output = Result<(RunningOneForOneSupervisee, ()), OneForOneSupervisee>; 125 | 126 | // fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 127 | // let this = &mut *self; 128 | // for supervisee in &mut this.inner.as_mut().unwrap().supervisees { 129 | // 'this_supervisee: loop { 130 | // match supervisee { 131 | // SuperviseeState::Startable(_) => { 132 | // if this.canceling { 133 | // break 'this_supervisee; 134 | // } 135 | // let SuperviseeState::Startable(s) = std::mem::replace(supervisee, SuperviseeState::Complete) else { 136 | // unreachable!() 137 | // }; 138 | // *supervisee = SuperviseeState::Starting(s.start_supervised()); 139 | // } 140 | // SuperviseeState::Starting(s) => { 141 | // if let Poll::Ready(start_res) = s.poll_unpin(cx) { 142 | // match start_res { 143 | // Ok((running, ())) => { 144 | // *supervisee = SuperviseeState::Running(running, false); 145 | // break 'this_supervisee; 146 | // } 147 | // Err(s) => { 148 | // *supervisee = SuperviseeState::Startable(s); 149 | // this.canceling = true; 150 | // break 'this_supervisee; 151 | // } 152 | // } 153 | // } 154 | // } 155 | // SuperviseeState::Running(s, halted) => { 156 | // if !this.canceling { 157 | // break; 158 | // } 159 | // } 160 | // SuperviseeState::Complete => break, 161 | // } 162 | // } 163 | // } 164 | 165 | // todo!() 166 | // } 167 | // } 168 | 169 | // pub struct RunningOneForOneSupervisee { 170 | // inner: Option, 171 | // } 172 | 173 | // impl RunningSupervisee for RunningOneForOneSupervisee { 174 | // type Supervisee = OneForOneSupervisee; 175 | 176 | // fn poll_supervise(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { 177 | // todo!() 178 | // } 179 | 180 | // fn shutdown_time(self: Pin<&Self>) -> Duration { 181 | // todo!() 182 | // } 183 | 184 | // fn halt(self: Pin<&mut Self>) { 185 | // todo!() 186 | // } 187 | 188 | // fn abort(self: Pin<&mut Self>) { 189 | // todo!() 190 | // } 191 | // } 192 | -------------------------------------------------------------------------------- /zestors/src/handler/handler.rs: -------------------------------------------------------------------------------- 1 | use crate::all::*; 2 | use async_trait::async_trait; 3 | 4 | /// The [`Handler`] trait makes it easy to write actors in a declarative manner. See [`HandlerExt`] for 5 | /// methods to call on a handler. 6 | /// 7 | /// (See [`handler`](crate::handler) for an overview). 8 | #[async_trait] 9 | pub trait Handler: Sized + Send + 'static { 10 | /// The state is passed to every handler function and always contains the inbox. 11 | type State: HandlerState; 12 | 13 | /// The `Err(T)` value that can be returned from any handler function. 14 | type Exception: Send; 15 | 16 | /// The [`Flow::Stop(T)`] value that can be returned from any handler function. 17 | type Stop: Send; 18 | 19 | /// The value that the process exits with. 20 | /// 21 | /// The [`Child`] returned when spawning a handler is a `Child`. 22 | type Exit: Send + 'static; 23 | 24 | /// Specifies how the [`Handler`] handles it's exit. The handler can either 25 | /// [`ExitFlow::Continue`] or [`ExitFlow::Exit`]. 26 | async fn handle_exit( 27 | self, 28 | state: &mut Self::State, 29 | reason: Result, 30 | ) -> ExitFlow; 31 | 32 | /// Specifies how the [`Handler`] handles an [`Event`] generated by it's [`HandlerState`]. 33 | async fn handle_event(&mut self, state: &mut Self::State, event: Event) -> HandlerResult; 34 | 35 | /// The default [`Link`] used when this process is spawned or started. 36 | fn default_link() -> Link { 37 | Default::default() 38 | } 39 | 40 | /// The default [`InboxType::Config`] used when this process is spawned or started. 41 | fn default_config() -> HandlerConfig { 42 | Default::default() 43 | } 44 | } 45 | 46 | /// Specifies how the [`Handler`] handles the [`Message`] `M`. 47 | #[async_trait] 48 | pub trait HandleMessage: Handler { 49 | async fn handle_msg(&mut self, state: &mut Self::State, msg: M::Payload) 50 | -> HandlerResult; 51 | } 52 | 53 | pub enum RestartReason { 54 | InitError(Err), 55 | Exit(Result), 56 | } 57 | 58 | /// Specifies that a [`Protocol`] can be handled by the [`Handler`] `H`. 59 | /// 60 | /// This is automatically implemented with the [`protocol`] macro. 61 | #[async_trait] 62 | pub trait HandledBy { 63 | async fn handle_with(self, handler: &mut H, state: &mut H::State) -> HandlerResult; 64 | } 65 | 66 | /// The return-value of the [`Handler::handle_exit`] function. 67 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 68 | pub enum ExitFlow { 69 | /// Exit with [`Handler::Exit`]. 70 | Exit(H::Exit), 71 | /// Continue execution with `H`. 72 | Continue(H), 73 | } 74 | 75 | /// The `Ok(_)` value of a [`Handler`]-function. 76 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 77 | pub enum Flow { 78 | /// Continue regular execution. 79 | Continue, 80 | /// Calls [`Handler::handle_exit`] with `Ok(H::Stop)`. 81 | Stop(H::Stop), 82 | /// Immeadeately exits with [`Handler::Exit`] without calling [`Handler::handle_exit`]. 83 | ExitDirectly(H::Exit), 84 | } 85 | 86 | /// An event generated by the [`HandlerState`] that the [`Handler`] should handle. 87 | #[derive(Debug)] 88 | pub enum Event { 89 | /// This process has been halted and should exit. 90 | Halted, 91 | /// This process's inbox has been closed and is also empty. 92 | ClosedAndEmpty, 93 | /// Same as `ClosedAndEmpty` but with the extra guarantee that no [`Action`]s have 94 | /// been scheduled either. 95 | /// 96 | /// This means that the actor won't do anything until a new action is scheduled. If the process 97 | /// neither exits nor schedules a new future, it will panic! 98 | Dead, 99 | } 100 | 101 | impl Event { 102 | /// Handle the [`Event`] with [`Handler`] 103 | pub async fn handle_with( 104 | self, 105 | handler: &mut H, 106 | state: &mut H::State, 107 | ) -> HandlerResult { 108 | handler.handle_event(state, self).await 109 | } 110 | } 111 | 112 | /// Type-alias for the [`InboxType`] of the [`Handler`] `H`. 113 | pub type HandlerInbox = <::State as HandlerState>::InboxType; 114 | 115 | /// Type-alias for the [`Protocol`] of the [`Handler`] `H`. 116 | pub type HandlerProtocol = <::State as HandlerState>::Protocol; 117 | 118 | /// Type-alias for the [`InboxType::Config`] of the [`Handler`] `H`. 119 | pub type HandlerConfig = as InboxType>::Config; 120 | 121 | /// Type-alias for the return-value of a [`Handler`]-function. 122 | pub type HandlerResult = Result, ::Exception>; 123 | 124 | // pub struct SpawnConfig { 125 | // link: Link, 126 | // cfg: HandlerConfig, 127 | // } 128 | 129 | // impl SpawnConfig { 130 | // pub(crate) fn new(link: Link, cfg: HandlerConfig) -> Self { 131 | // Self { link, cfg } 132 | // } 133 | 134 | // pub(crate) fn default() -> Self { 135 | // Self { 136 | // link: H::default_link(), 137 | // cfg: H::default_config(), 138 | // } 139 | // } 140 | 141 | // pub fn spawn(self, handler: H) -> (Child>, Address>) { 142 | // spawn_with(self.link, self.cfg, |inbox| async move { 143 | // handler 144 | // .run(>::from_inbox(inbox)) 145 | // .await 146 | // }) 147 | // } 148 | 149 | // pub fn spawn_initialize( 150 | // self, 151 | // init_fn: F, 152 | // ) -> (Child>, Address>) 153 | // where 154 | // F: for<'a> FnOnce(&'a mut H::State) -> BoxFuture<'a, Result> + Send + 'static, 155 | // { 156 | // spawn_with(self.link, self.cfg, |inbox| async move { 157 | // let mut state = >::from_inbox(inbox); 158 | // match init_fn(&mut state).await { 159 | // Ok(handler) => handler.run(state).await, 160 | // Err(exit) => exit, 161 | // } 162 | // }) 163 | // } 164 | // } 165 | 166 | // #[async_trait] 167 | // pub trait HandleInit: Handler { 168 | // type Ref: Send + 'static; 169 | // type InitError: Error + Send; 170 | 171 | // /// Asynchronously spawn and initialize the process with `I`. The [`SpawnConfig`] 172 | // /// can be used either with [`SpawnConfig::spawn`] or [`SpawnConfig::spawn_initialize`]. 173 | // /// 174 | // async fn initialize( 175 | // setup: SpawnConfig, 176 | // init: I, 177 | // ) -> Result<(Child>, Self::Ref), Self::InitError>; 178 | // } 179 | -------------------------------------------------------------------------------- /supervision/src/supervision/supervisor.rs: -------------------------------------------------------------------------------- 1 | use crate as zestors; 2 | use crate::all::*; 3 | use futures::{Future, FutureExt, StreamExt, future::BoxFuture}; 4 | use pin_project::pin_project; 5 | use std::{ 6 | mem::swap, 7 | pin::Pin, 8 | task::{Context, Poll}, 9 | }; 10 | use tokio::time::{sleep, Sleep}; 11 | 12 | 13 | 14 | //------------------------------------------------------------------------------------------------ 15 | // SupervisorFut 16 | //------------------------------------------------------------------------------------------------ 17 | 18 | #[pin_project] 19 | pub struct SupervisorFut { 20 | to_shutdown: bool, 21 | #[pin] 22 | state: SupervisorFutState, 23 | } 24 | 25 | #[pin_project] 26 | enum SupervisorFutState { 27 | NotStarted(S), 28 | Starting(BoxFuture<'static, StartResult>), 29 | Supervising(Pin>, Option<(Pin>, bool)>), 30 | Exited, 31 | } 32 | 33 | impl SupervisorFut { 34 | pub fn new(spec: S) -> Self { 35 | Self { 36 | to_shutdown: false, 37 | state: SupervisorFutState::NotStarted(spec), 38 | } 39 | } 40 | 41 | pub fn shutdown(&mut self) { 42 | self.to_shutdown = true; 43 | } 44 | 45 | pub fn to_shutdown(&self) -> bool { 46 | self.to_shutdown 47 | } 48 | 49 | pub fn spawn_supervisor(self) -> (Child>, SupervisorRef) 50 | where 51 | S: Sized + Send + 'static, 52 | S::Supervisee: Send, 53 | { 54 | SupervisorProcess::spawn_supervisor(self) 55 | } 56 | } 57 | 58 | impl Future for SupervisorFut { 59 | type Output = SupervisionResult; 60 | 61 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 62 | let this = &mut *self; 63 | 64 | loop { 65 | match &mut this.state { 66 | SupervisorFutState::NotStarted(_spec) => { 67 | let spec = { 68 | let mut state = SupervisorFutState::Exited; 69 | swap(&mut this.state, &mut state); 70 | let SupervisorFutState::NotStarted (spec) = state else { 71 | panic!() 72 | }; 73 | spec 74 | }; 75 | 76 | this.state = SupervisorFutState::Starting(Box::pin(spec.start_supervised())) 77 | } 78 | SupervisorFutState::Starting(fut) => { 79 | if let Poll::Ready(res) = fut.as_mut().poll(cx) { 80 | match res { 81 | Err(StartError::Completed) => { 82 | this.state = SupervisorFutState::Exited; 83 | break Poll::Ready(Ok(None)); 84 | } 85 | Ok((supervisee, _reference)) => { 86 | this.state = 87 | SupervisorFutState::Supervising(Box::pin(supervisee), None); 88 | } 89 | Err(StartError::StartFailed(child_spec)) => { 90 | this.state = SupervisorFutState::Exited; 91 | break Poll::Ready(Ok(Some(child_spec))); 92 | } 93 | Err(StartError::Fatal(e)) => { 94 | this.state = SupervisorFutState::Exited; 95 | break Poll::Ready(Err(e)); 96 | } 97 | }; 98 | }; 99 | } 100 | 101 | SupervisorFutState::Supervising(supervisee, aborting) => match aborting { 102 | Some((sleep, aborted)) => { 103 | if let Poll::Ready(exit) = supervisee.as_mut().poll_supervise(cx) { 104 | return Poll::Ready(exit); 105 | } 106 | if let Poll::Ready(()) = sleep.poll_unpin(cx) { 107 | supervisee.as_mut().abort(); 108 | *aborted = true; 109 | } 110 | return Poll::Pending; 111 | } 112 | None => { 113 | if this.to_shutdown { 114 | supervisee.as_mut().halt(); 115 | *aborting = Some(( 116 | Box::pin(sleep(supervisee.as_ref().shutdown_time())), 117 | false, 118 | )); 119 | } else { 120 | return supervisee.as_mut().poll_supervise(cx); 121 | } 122 | } 123 | }, 124 | 125 | SupervisorFutState::Exited => panic!("Already exited."), 126 | } 127 | } 128 | } 129 | } 130 | 131 | //------------------------------------------------------------------------------------------------ 132 | // SupervisorActor 133 | //------------------------------------------------------------------------------------------------ 134 | 135 | #[pin_project] 136 | pub(super) struct SupervisorProcess { 137 | #[pin] 138 | inbox: Inbox, 139 | #[pin] 140 | supervision_fut: SupervisorFut, 141 | } 142 | 143 | pub struct SupervisorRef { 144 | address: Address>, 145 | } 146 | 147 | #[protocol] 148 | enum SupervisorProtocol {} 149 | 150 | impl SupervisorProcess 151 | where 152 | S: Specification + Send + 'static, 153 | S::Supervisee: Send, 154 | { 155 | pub fn spawn_supervisor( 156 | supervision_fut: SupervisorFut, 157 | ) -> (Child>, SupervisorRef) { 158 | let (child, address) = spawn(|inbox: Inbox| SupervisorProcess { 159 | inbox, 160 | supervision_fut, 161 | }); 162 | (child.into_dyn(), SupervisorRef { address }) 163 | } 164 | } 165 | 166 | impl Future for SupervisorProcess { 167 | type Output = SupervisionResult; 168 | 169 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 170 | let mut proj = self.as_mut().project(); 171 | 172 | if !proj.supervision_fut.to_shutdown() { 173 | if proj.inbox.halted() { 174 | proj.supervision_fut.shutdown(); 175 | } else { 176 | if let Poll::Ready(res) = proj.inbox.next().poll_unpin(cx) { 177 | match res { 178 | Some(Ok(_msg)) => { 179 | unreachable!("No messages handled yet"); 180 | } 181 | Some(Err(Halted)) => proj.supervision_fut.shutdown(), 182 | None => { 183 | println!("WARN: Inbox of supervisor has been closed!"); 184 | proj.supervision_fut.shutdown(); 185 | } 186 | } 187 | }; 188 | } 189 | } 190 | 191 | proj.supervision_fut.poll(cx) 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /zestors/src/actor_type/channel.rs: -------------------------------------------------------------------------------- 1 | use crate::all::*; 2 | use event_listener::EventListener; 3 | use futures::future::BoxFuture; 4 | use std::{ 5 | any::{Any, TypeId}, 6 | fmt::Debug, 7 | sync::Arc, 8 | }; 9 | 10 | /// This trait must be implemented for a channel. 11 | pub trait Channel: Send + Sync + Debug { 12 | /// Convert the channel into a `dyn Channel`. 13 | fn into_dyn(self: Arc) -> Arc; 14 | /// Convert the channel into a `dyn Any + Send + Sync`. 15 | fn into_any(self: Arc) -> Arc; 16 | 17 | /// The id of the actor. 18 | fn actor_id(&self) -> ActorId; 19 | 20 | /// Halts all processes of the actor. 21 | fn halt(&self); 22 | /// Halt `n` processes of the actor. 23 | /// 24 | /// For channels that do not allow multiple processes, this can defer to `self.halt()`. 25 | fn halt_some(&self, n: u32); 26 | 27 | /// The amount of addresses of the actor. 28 | fn address_count(&self) -> usize; 29 | /// Removes an address from the actor. Returns the old address-count. 30 | fn decrement_address_count(&self) -> usize; 31 | /// Adds a new address to the actor. Returns the old address-count. 32 | fn increment_address_count(&self) -> usize; 33 | 34 | /// The amount of messages in the channel. 35 | /// 36 | /// For channels that do not accept messages, this can return `0`. 37 | fn msg_count(&self) -> usize; 38 | /// The capacity of this channel. 39 | /// 40 | /// For channels that do not accept messages, this can return a `Capacity::Bounded(0)` 41 | fn capacity(&self) -> Capacity; 42 | /// Closes the channel and returns `true` if the channel was not closed before. 43 | /// 44 | /// For channels that do not accept messages, this can return `false`. 45 | fn close(&self) -> bool; 46 | /// Whether the channel is closed. 47 | /// 48 | /// For channels that do not accept messages, this can return `true`. 49 | fn is_closed(&self) -> bool; 50 | 51 | /// Whether all processes of the actor have exited. 52 | fn has_exited(&self) -> bool; 53 | /// Get an event-listener for when the actor exits. 54 | fn get_exit_listener(&self) -> EventListener; 55 | 56 | /// The amount of processes of the actor. 57 | /// 58 | /// For channels that do not allow multiple processes, this can return `1` or `0`. 59 | fn process_count(&self) -> usize; 60 | /// Attempt to add another process to the channel. If successful, this returns the previous 61 | /// process-count. 62 | fn try_increment_process_count(&self) -> Result; 63 | 64 | /// Whether the channel accepts the type-id of a given message. 65 | /// 66 | /// For channels that do not accept messages, this can return `false`. 67 | fn accepts(&self, id: &TypeId) -> bool; 68 | /// Try to send a payload to the actor. 69 | /// 70 | /// For channels that do not accept messages, this can fail with `NotAccepted`. 71 | fn try_send_box(&self, msg: BoxPayload) -> Result<(), TrySendCheckedError>; 72 | /// Try to force-send a message to the actor. 73 | /// 74 | /// For channels that do not accept messages, this can fail with `NotAccepted`. 75 | fn force_send_box(&self, msg: BoxPayload) -> Result<(), TrySendCheckedError>; 76 | /// Send a payload to the actor while blocking the thread when waiting for space. 77 | /// 78 | /// For channels that do not accept messages, this can fail with `NotAccepted`. 79 | fn send_box_blocking(&self, msg: BoxPayload) -> Result<(), SendCheckedError>; 80 | /// Send a payload to the actor waiting for space. 81 | /// 82 | /// For channels that do not accept messages, this can fail with `NotAccepted`. 83 | fn send_box(&self, msg: BoxPayload) -> BoxFuture<'_, Result<(), SendCheckedError>>; 84 | } 85 | 86 | impl dyn Channel { 87 | pub fn try_send_checked( 88 | &self, 89 | msg: M, 90 | ) -> Result> { 91 | let (sends, returns) = M::create(msg); 92 | match self.try_send_box(BoxPayload::new::(sends)) { 93 | Ok(()) => Ok(returns), 94 | Err(e) => match e { 95 | TrySendCheckedError::Full(boxed) => Err(TrySendCheckedError::Full( 96 | boxed.downcast_and_cancel(returns).unwrap(), 97 | )), 98 | TrySendCheckedError::Closed(boxed) => Err(TrySendCheckedError::Closed( 99 | boxed.downcast_and_cancel(returns).unwrap(), 100 | )), 101 | TrySendCheckedError::NotAccepted(boxed) => Err(TrySendCheckedError::NotAccepted( 102 | boxed.downcast_and_cancel(returns).unwrap(), 103 | )), 104 | }, 105 | } 106 | } 107 | 108 | pub fn force_send_checked( 109 | &self, 110 | msg: M, 111 | ) -> Result> { 112 | let (sends, returns) = M::create(msg); 113 | let res = self.force_send_box(BoxPayload::new::(sends)); 114 | 115 | match res { 116 | Ok(()) => Ok(returns), 117 | Err(e) => match e { 118 | TrySendCheckedError::Full(boxed) => Err(TrySendCheckedError::Full( 119 | boxed.downcast_and_cancel(returns).unwrap(), 120 | )), 121 | TrySendCheckedError::Closed(boxed) => Err(TrySendCheckedError::Closed( 122 | boxed.downcast_and_cancel(returns).unwrap(), 123 | )), 124 | TrySendCheckedError::NotAccepted(boxed) => Err(TrySendCheckedError::NotAccepted( 125 | boxed.downcast_and_cancel(returns).unwrap(), 126 | )), 127 | }, 128 | } 129 | } 130 | 131 | pub fn send_blocking_checked( 132 | &self, 133 | msg: M, 134 | ) -> Result> { 135 | let (sends, returns) = M::create(msg); 136 | let res = self.send_box_blocking(BoxPayload::new::(sends)); 137 | 138 | match res { 139 | Ok(()) => Ok(returns), 140 | Err(e) => match e { 141 | SendCheckedError::Closed(boxed) => Err(SendCheckedError::Closed( 142 | boxed.downcast_and_cancel(returns).unwrap(), 143 | )), 144 | SendCheckedError::NotAccepted(boxed) => Err(SendCheckedError::NotAccepted( 145 | boxed.downcast_and_cancel(returns).unwrap(), 146 | )), 147 | }, 148 | } 149 | } 150 | 151 | pub fn send_checked(&self, msg: M) -> BoxFuture<'_, Result>> 152 | where 153 | M::Returned: Send, 154 | M: Message + Send + 'static, 155 | { 156 | Box::pin(async move { 157 | let (sends, returns) = M::create(msg); 158 | let res = self.send_box(BoxPayload::new::(sends)).await; 159 | 160 | match res { 161 | Ok(()) => Ok(returns), 162 | Err(e) => match e { 163 | SendCheckedError::Closed(boxed) => Err(SendCheckedError::Closed( 164 | boxed.downcast_and_cancel(returns).unwrap(), 165 | )), 166 | SendCheckedError::NotAccepted(boxed) => Err(SendCheckedError::NotAccepted( 167 | boxed.downcast_and_cancel(returns).unwrap(), 168 | )), 169 | }, 170 | } 171 | }) 172 | } 173 | } 174 | --------------------------------------------------------------------------------