From ceae9fbd52c925e3b666f252707325b478ffbcf6 Mon Sep 17 00:00:00 2001 From: Bobonium Date: Sat, 1 Apr 2023 17:18:09 +0200 Subject: [PATCH 01/44] upgrade dependencies --- Cargo.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 91a169e..42a94bb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,11 +18,11 @@ name = "tyra" path = "src/lib.rs" [dependencies] -config = "0.13.2" +config = "0.13.3" hostname = "0.3.1" -num_cpus = "1.13.1" +num_cpus = "1.15.0" threadpool = "1.8.1" -crossbeam-channel = "0.5.6" +crossbeam-channel = "0.5.7" flume = "0.10.14" dashmap = "5.4.0" serde = { version = "1.0", features = ["derive"] } @@ -31,4 +31,4 @@ log = "0.4" [dev-dependencies] bincode = "1.3.3" -ntest = "0.8.1" \ No newline at end of file +ntest = "0.9.0" \ No newline at end of file From cb58f8caa7680d35b4e6b55662178e0812b61f2e Mon Sep 17 00:00:00 2001 From: Bobonium Date: Sat, 1 Apr 2023 17:21:45 +0200 Subject: [PATCH 02/44] update changelog --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c5d0070..e9ee98a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# 1.1.0 + + - upgrade dependencies + - + # 1.0.0 - added `LeastMessageRouter` From 20b9380a629e45a9160cb649cfd7f9e75b37efab Mon Sep 17 00:00:00 2001 From: Bobonium Date: Sun, 9 Apr 2023 17:14:36 +0200 Subject: [PATCH 03/44] wip: introduce networking - add pre_stop function to actors, that is executed before mailbox is closed to enable a clean shutdown for network connections (i.e. drop new connections but finish open communication with existing connections for http requests) - add optional signal handling to start a clean shutdown for SIGINT --- Cargo.toml | 3 + src/actor/actor.rs | 52 ++++++++++- src/actor/executor.rs | 1 + src/actor/handler.rs | 2 +- src/config/default.toml | 2 + src/config/global_config.rs | 1 + src/lib.rs | 2 + src/network/mod.rs | 6 ++ src/network/tcp_remote_actor.rs | 158 ++++++++++++++++++++++++++++++++ src/system/actor_system.rs | 33 +++++++ src/system/system_state.rs | 7 +- 11 files changed, 264 insertions(+), 3 deletions(-) create mode 100644 src/network/mod.rs create mode 100644 src/network/tcp_remote_actor.rs diff --git a/Cargo.toml b/Cargo.toml index 42a94bb..292addb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,8 +26,11 @@ crossbeam-channel = "0.5.7" flume = "0.10.14" dashmap = "5.4.0" serde = { version = "1.0", features = ["derive"] } +serde-encrypt = "0.7.0" +kcp = "0.5.1" thiserror = "1.0" log = "0.4" +ctrlc = "3.2.5" [dev-dependencies] bincode = "1.3.3" diff --git a/src/actor/actor.rs b/src/actor/actor.rs index e857d0f..d6c64c9 100644 --- a/src/actor/actor.rs +++ b/src/actor/actor.rs @@ -335,6 +335,56 @@ pub trait Actor: Send + Sync + UnwindSafe + Sized { /// ``` fn pre_restart(&mut self, _context: &ActorContext) {} + /// executed before mailbox will be disabled + /// + /// # Examples + /// + /// ```rust + /// use tyra::prelude::*; + /// use std::error::Error; + /// use std::time::Duration; + /// + /// struct TestActor {} + /// impl TestActor { + /// pub fn new() -> Self { + /// Self {} + /// } + /// } + /// impl Actor for TestActor { + /// fn pre_stop(&mut self, context: &ActorContext) { + /// context.system.stop(Duration::from_millis(5000)); + /// } + /// } + /// + /// struct TestActorFactory {} + /// impl TestActorFactory { + /// pub fn new() -> Self { + /// Self {} + /// } + /// } + /// impl ActorFactory for TestActorFactory { + /// fn new_actor(&mut self, _context: ActorContext) -> Result> { + /// Ok(TestActor::new()) + /// } + /// } + /// + /// impl Handler for TestActor { + /// fn handle(&mut self, _msg: ActorInitMessage, context: &ActorContext) -> Result> { + /// return Ok(ActorResult::Stop); + /// } + /// } + /// + /// #[ntest::timeout(10000)] + /// fn main() { + /// let actor_config = TyraConfig::new().unwrap(); + /// let actor_system = ActorSystem::new(actor_config); + /// let actor = actor_system.builder().spawn("test", TestActorFactory::new()).unwrap(); + /// actor.send(ActorInitMessage::new()).unwrap(); + /// std::process::exit(actor_system.await_shutdown()); + /// } + /// ``` + fn pre_stop(&mut self, _context: &ActorContext) {} + /// executed after the last message is handled /// /// # Examples @@ -443,7 +493,7 @@ pub trait Actor: Send + Sync + UnwindSafe + Sized { &mut self, context: &ActorContext, ) -> Result> { - let result = context.actor_ref.send(ActorStopMessage::new()); + let result = context.actor_ref.stop(); if result.is_err() { error!( "Could not forward message ActorStopMessage to target {}", diff --git a/src/actor/executor.rs b/src/actor/executor.rs index c785dcd..5d440c9 100644 --- a/src/actor/executor.rs +++ b/src/actor/executor.rs @@ -113,6 +113,7 @@ where } fn stop_actor(&mut self, immediately: bool) -> ActorState { + let _ = catch_unwind(AssertUnwindSafe(|| self.actor.pre_stop(&self.context))); self.mailbox.is_stopped.store(true, Ordering::Relaxed); if immediately { let _ = catch_unwind(AssertUnwindSafe(|| self.actor.post_stop(&self.context))); diff --git a/src/actor/handler.rs b/src/actor/handler.rs index b0b32df..2f7272f 100644 --- a/src/actor/handler.rs +++ b/src/actor/handler.rs @@ -48,7 +48,7 @@ where fn handle( &mut self, _msg: ActorStopMessage, - _context: &ActorContext, + context: &ActorContext, ) -> Result> { return Ok(ActorResult::Stop); } diff --git a/src/config/default.toml b/src/config/default.toml index c04de50..ca3f81b 100644 --- a/src/config/default.toml +++ b/src/config/default.toml @@ -11,6 +11,8 @@ default_mailbox_size = 0 default_message_throughput = 15 # defines if the rust panic hook should be overwritten by the actor system on startup override_panic_hook = true +# will start a graceful shutdown on first SIGINT, or force a shutdown if multiple SIGINT are received. Can be disabled by setting to 0 +sigint_graceful_timeout_in_seconds = 300 # default pool settings [thread_pool.config.default] diff --git a/src/config/global_config.rs b/src/config/global_config.rs index 403ab0d..cc70374 100644 --- a/src/config/global_config.rs +++ b/src/config/global_config.rs @@ -6,4 +6,5 @@ pub struct GeneralConfig { pub default_mailbox_size: usize, pub default_message_throughput: usize, pub override_panic_hook: bool, + pub sigint_graceful_timeout_in_seconds: usize, } diff --git a/src/lib.rs b/src/lib.rs index face0c6..c6e9776 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -109,6 +109,7 @@ mod config; mod message; mod routers; mod system; +mod network; /// core components pub mod prelude { @@ -116,6 +117,7 @@ pub mod prelude { pub use crate::config::prelude::*; pub use crate::message::prelude::*; pub use crate::system::prelude::*; + pub use crate::network::prelude::*; } /// collection of different router implementations diff --git a/src/network/mod.rs b/src/network/mod.rs new file mode 100644 index 0000000..096eb44 --- /dev/null +++ b/src/network/mod.rs @@ -0,0 +1,6 @@ +pub mod tcp_remote_actor; + +pub mod prelude { + pub use crate::network::tcp_remote_actor::TcpRemoteActorFactory; + pub use crate::network::tcp_remote_actor::TcpRemoteActor; +} diff --git a/src/network/tcp_remote_actor.rs b/src/network/tcp_remote_actor.rs new file mode 100644 index 0000000..806e849 --- /dev/null +++ b/src/network/tcp_remote_actor.rs @@ -0,0 +1,158 @@ +use std::error::Error; +use std::io::{BufRead, BufReader, Read, Write}; +use std::net::{TcpListener, TcpStream}; +use std::process::exit; +use std::sync::Arc; +use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; +use std::thread; +use std::thread::{sleep, Thread}; +use std::time::Duration; +use threadpool::ThreadPool; +use crate::actor::actor_address::ActorAddress; +use crate::prelude::{Actor, ActorContext, ActorFactory, ActorInitMessage, ActorMessage, ActorResult, Handler, SerializedMessage}; + +struct NetworkMessage { + address: ActorAddress, + content: SerializedMessage, + id: u32, + stream: TcpStream +} + +impl ActorMessage for NetworkMessage {} + +pub struct TcpRemoteActor { + listener: Arc, + is_stopped: Arc, + is_stopping: Arc, +} +impl TcpRemoteActor { + pub fn new() -> Self { + println!("SERS!"); + let listener = Arc::new(TcpListener::bind("127.0.0.1:2022").unwrap()); + println!("SERS!"); + + + Self { + listener, + is_stopped: Arc::new(AtomicBool::new(false)), + is_stopping: Arc::new(AtomicBool::new(false)), + } + } +} +impl Actor for TcpRemoteActor { + fn pre_stop(&mut self, _context: &ActorContext) { + println!("PRE-STOP!"); + sleep(Duration::from_secs(3)); + self.is_stopping.store(true, Ordering::Relaxed); + } + fn post_stop(&mut self, _context: &ActorContext) { + println!("POST-STOP!"); + self.is_stopped.store(true, Ordering::Relaxed); + } + fn on_system_stop(&mut self, context: &ActorContext) -> Result> { + println!("SYSTEM-STOP"); + Ok(ActorResult::Stop) + } +} + +// define a factory that creates the `Actor` for us +pub struct TcpRemoteActorFactory {} +impl TcpRemoteActorFactory { + pub fn new() -> Self { + Self {} + } +} +impl ActorFactory for TcpRemoteActorFactory { + fn new_actor( + &mut self, + context: ActorContext, + ) -> Result> { + context.actor_ref.send(ActorInitMessage::new()).unwrap(); + Ok(TcpRemoteActor::new()) + } +} + +// implement our message for the `Actor` +impl Handler for TcpRemoteActor { + fn handle( + &mut self, + _msg: ActorInitMessage, + _context: &ActorContext, + ) -> Result> { + println!("AAAAA"); + + let listener = self.listener.clone(); + let is_stopped = self.is_stopped.clone(); + let is_stopping = self.is_stopping.clone(); + let context = _context.clone(); + thread::spawn(move || { + + let mut count = AtomicUsize::new(1); + + + let pool = ThreadPool::new(100); + for stream in listener.incoming() { + //println!("SERS!"); + if is_stopped.load(Ordering::Relaxed) { + println!("EXIT"); + return; + } + if is_stopping.load(Ordering::Relaxed) { + println!("NO MORE NEW CONNECTIONS"); + continue; + } + let counter = count.fetch_add(1, Ordering::Relaxed); + println!("NEW CONNECTION!: {:?}", counter); + + let context = context.clone(); + let mut stream = stream.unwrap(); + pool.execute(move || { + //let context = &context; + + let mut buf_reader = BufReader::new(&mut stream); + let mut http_req = String::new(); + //let http_request = buf_reader.read_line(&mut http_req); + let http_request: Vec<_> = buf_reader + .lines() + .map(|result| result.unwrap()) + .take_while(|line| !line.is_empty()) + .collect(); + + //println!("Request: {:#?}", http_request); + context.actor_ref.send(NetworkMessage{ + id: 1, + stream: stream, + address: context.actor_ref.get_address().clone(), + content: SerializedMessage::new(Vec::new()), + }).unwrap(); + }) + } + }); + + + Ok(ActorResult::Ok) + } +} + +impl Handler for TcpRemoteActor { + fn handle(&mut self, mut msg: NetworkMessage, context: &ActorContext) -> Result> { + //println!("HELLO WORLD: {:?}", msg.address.actor); + //println!("ASDF: {:?}", msg.content); + let context = context.clone(); + thread::spawn(move || { + + sleep(Duration::from_secs(3)); + + let response = "HTTP/1.1 200 OK\r\n\r\n

SERS

"; + + msg.stream.write_all(response.as_bytes()).unwrap(); + + context.system.stop(Duration::from_secs(30)); + + + //context.actor_ref.stop().unwrap(); + }); + Ok(ActorResult::Ok) + + } +} \ No newline at end of file diff --git a/src/system/actor_system.rs b/src/system/actor_system.rs index 08ba361..c16a80a 100644 --- a/src/system/actor_system.rs +++ b/src/system/actor_system.rs @@ -76,11 +76,40 @@ impl ActorSystem { internal_actor_manager: InternalActorManager::new(), }; + if config.general.sigint_graceful_timeout_in_seconds > 0 { + let sys = system.clone(); + ctrlc::set_handler(move || { + sys.sigint_handler(Duration::from_secs(300)); + }).unwrap(); + } + system.internal_actor_manager.init(system.clone()); system } + /// Adds a new named pool using the [default pool configuration](https://github.com/sers-dev/tyra/blob/master/src/config/default.toml) + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rust + /// use std::time::Duration; + /// use tyra::prelude::{TyraConfig, ActorSystem}; + /// + /// let mut actor_config = TyraConfig::new().unwrap(); + /// //disable automatic setup of sigint handling, so that we can set it manually + /// actor_config.general.sigint_graceful_timeout_in_seconds = 0; + /// let actor_system = ActorSystem::new(actor_config); + /// ctrlc::set_handler(move || {actor_system.sigint_handler(Duration::from_secs(60));}).unwrap(); + /// ``` + pub fn sigint_handler(&self, graceful_termination_timeout: Duration) { + if self.state.is_stopping() { + self.force_stop(); + } + self.stop(graceful_termination_timeout); + } /// Adds a new named pool using the [default pool configuration](https://github.com/sers-dev/tyra/blob/master/src/config/default.toml) /// /// # Examples @@ -247,6 +276,10 @@ impl ActorSystem { self.state.stop(graceful_termination_timeout); } + pub fn force_stop(&self) { + self.state.force_stop(); + } + /// Same as stop, but with fixed user defined exit code /// /// # Examples diff --git a/src/system/system_state.rs b/src/system/system_state.rs index e40fc53..9c6aa1d 100644 --- a/src/system/system_state.rs +++ b/src/system/system_state.rs @@ -44,6 +44,11 @@ impl SystemState { } } + pub fn force_stop(&self) { + self.is_force_stopped.store(true, Ordering::Relaxed); + self.stop(Duration::from_secs(1)); + } + pub fn stop(&self, graceful_termination_timeout: Duration) { if self.is_stopping() { return; @@ -56,7 +61,7 @@ impl SystemState { fn shutdown(&self, timeout: Duration) { let now = Instant::now(); while self.get_actor_count() != 0 { - if now.elapsed() >= timeout { + if now.elapsed() >= timeout || self.is_force_stopped.load(Ordering::Relaxed) { self.is_force_stopped.store(true, Ordering::Relaxed); self.mailboxes.clear(); break; From e7f06cf070cb64623f0b173efc00cfcdac220ead Mon Sep 17 00:00:00 2001 From: Bobonium Date: Sat, 15 Apr 2023 11:33:07 +0200 Subject: [PATCH 04/44] improve routers and signal handling --- CHANGELOG.md | 8 ++- Cargo.toml | 2 +- examples/benchmark_bulk_router.rs | 2 +- examples/benchmark_router_round_robin.rs | 2 +- src/actor/actor.rs | 2 +- src/actor/actor_builder.rs | 57 +++++++-------- src/actor/actor_wrapper.rs | 20 ++++++ src/actor/handler.rs | 2 +- src/config/default.toml | 50 ++++++++++++- src/config/global_config.rs | 2 +- src/routers/least_message_router.rs | 66 ++++++++++++++--- src/routers/round_robin_router.rs | 90 +++++++++++++++++++++--- src/routers/sharded_router.rs | 73 ++++++++++++++++--- src/system/actor_system.rs | 10 ++- src/system/internal_actor_manager.rs | 2 +- src/system/system_state.rs | 62 ++++++++++------ 16 files changed, 357 insertions(+), 93 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e9ee98a..41dfb02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,13 @@ # 1.1.0 - upgrade dependencies - - + - `LeastMessageRouter.min_mailbox_size` is now working correctly + - Routers now come with 2 new configurations to configure behavior + - `stop_on_system_stop` => if false, the user needs to manually stop the router for a clean and quick shutdown of the system + - `stop_on_empty_targets` => automatically stops the router if there are no more targets to receive a message to be routed. This does not apply to manually removed targets + - Routers now automatically remove stopped actors from their target pool if they have been stopped + - Added `.is_mailbox_stopped()`, `is_stopped()` and `wait_for_stop()` to `ActorWrapper
` + - Added `general.signal_graceful_timeout_in_seconds` to config # 1.0.0 diff --git a/Cargo.toml b/Cargo.toml index 292addb..a921ff3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,7 @@ serde-encrypt = "0.7.0" kcp = "0.5.1" thiserror = "1.0" log = "0.4" -ctrlc = "3.2.5" +ctrlc = { version = "3.2.5", features = ["termination"] } [dev-dependencies] bincode = "1.3.3" diff --git a/examples/benchmark_bulk_router.rs b/examples/benchmark_bulk_router.rs index dec535e..61f0631 100644 --- a/examples/benchmark_bulk_router.rs +++ b/examples/benchmark_bulk_router.rs @@ -165,7 +165,7 @@ fn main() { // ideal number is "amount of threads - 3" let actor_count = 7; - let router_factory = RoundRobinRouterFactory::new(); + let router_factory = RoundRobinRouterFactory::new(true, true); let router = actor_system .builder() .spawn("benchmark-router", router_factory) diff --git a/examples/benchmark_router_round_robin.rs b/examples/benchmark_router_round_robin.rs index 933b086..bc00b0e 100644 --- a/examples/benchmark_router_round_robin.rs +++ b/examples/benchmark_router_round_robin.rs @@ -158,7 +158,7 @@ fn main() { let message_count = 10000000; let actor_count = 10; - let router_factory = RoundRobinRouterFactory::new(); + let router_factory = RoundRobinRouterFactory::new(true, true); let router = actor_system .builder() .spawn("benchmark-router", router_factory) diff --git a/src/actor/actor.rs b/src/actor/actor.rs index d6c64c9..c363eba 100644 --- a/src/actor/actor.rs +++ b/src/actor/actor.rs @@ -1,4 +1,3 @@ -use crate::message::actor_stop_message::ActorStopMessage; use crate::prelude::{ActorContext, ActorPanicSource, ActorResult, SerializedMessage}; use log::error; use std::error::Error; @@ -499,6 +498,7 @@ pub trait Actor: Send + Sync + UnwindSafe + Sized { "Could not forward message ActorStopMessage to target {}", context.actor_ref.get_address().actor ); + return Ok(ActorResult::Stop); } return Ok(ActorResult::Ok); } diff --git a/src/actor/actor_builder.rs b/src/actor/actor_builder.rs index 1a4987f..e1120c9 100644 --- a/src/actor/actor_builder.rs +++ b/src/actor/actor_builder.rs @@ -118,34 +118,40 @@ where /// } /// } /// - /// struct SecondActor {} - /// impl SecondActor { + /// struct BrokenActor {} + /// impl BrokenActor { /// pub fn new() -> Self { /// Self {} /// } /// } - /// impl Actor for SecondActor {} + /// impl Actor for BrokenActor {} /// - /// struct SecondActorFactory {} - /// impl SecondActorFactory { + /// struct BrokenActorFactory {} + /// impl BrokenActorFactory { /// pub fn new() -> Self { /// Self {} /// } /// } - /// impl ActorFactory for SecondActorFactory { - /// fn new_actor(&mut self, _context: ActorContext) -> Result> { + /// impl ActorFactory for BrokenActorFactory { + /// fn new_actor(&mut self, _context: ActorContext) -> Result> { /// let error = std::io::Error::from_raw_os_error(1337); /// return Err(Box::new(error)); /// } /// } /// - /// #[ntest::timeout(10000)] + /// #[ntest::timeout(100000)] /// fn main() { /// let mut actor_config = TyraConfig::new().unwrap(); /// actor_config.thread_pool.config.insert(String::from("default"), ThreadPoolConfig::new(1, 1, 1, 1.0)); /// let actor_system = ActorSystem::new(actor_config); - /// let actor_name = "test"; /// + /// //this does not work, because although there's not yet an actor called `broken` on the pool the `new_actor` method returns an error + /// let this_is_not_working = actor_system.builder().spawn("broken", BrokenActorFactory::new()); + /// assert!(this_is_not_working.is_err(), "The BrokenActor was spawned"); + /// let err = this_is_not_working.err().unwrap(); + /// assert_eq!(err, ActorError::InitError, "Error is not correct"); + /// + /// let actor_name = "test"; /// //this works, because there's no actor called `test` yet on the pool /// let this_works = actor_system.builder().spawn(actor_name, TestActorFactory::new()); /// assert!(this_works.is_ok(), "The actor could not be spawned"); @@ -160,25 +166,19 @@ where /// let err = pool_full.err().unwrap(); /// assert_eq!(err, ActorError::ThreadPoolHasTooManyActorsError, "Error is not correct"); /// - /// //this does not work, because the pool does not exist in the configuration + /// ////this does not work, because the pool does not exist in the configuration /// let invalid_pool = actor_system.builder().set_pool_name("invalid").spawn(actor_name, TestActorFactory::new()); /// assert!(invalid_pool.is_err(), "The Actor was spawned"); /// let err = invalid_pool.err().unwrap(); /// assert_eq!(err, ActorError::ThreadPoolDoesNotExistError, "Error is not correct"); /// - /// //this does not work, because although there's not yet an actor called `second` on the pool the `new_actor` method returns an error - /// let this_is_not_working = actor_system.builder().spawn("second", SecondActorFactory::new()); - /// assert!(this_is_not_working.is_err(), "The SecondActor was spawned"); - /// let err = this_is_not_working.err().unwrap(); - /// assert_eq!(err, ActorError::InitError, "Error is not correct"); - /// - /// //this does not work, because there's already an actor called `test` with a different type on the pool - /// let this_is_not_working_either = actor_system.builder().spawn(actor_name, SecondActorFactory::new()); + /// ////this does not work, because there's already an actor called `test` with a different type on the pool + /// let this_is_not_working_either = actor_system.builder().spawn(actor_name, BrokenActorFactory::new()); /// assert!(this_is_not_working_either.is_err(), "Illegal Actor type conversion"); /// let err = this_is_not_working_either.err().unwrap(); /// assert_eq!(err, ActorError::InvalidActorTypeError, "Error is not correct"); /// - /// actor_system.stop(Duration::from_millis(1000)); + /// actor_system.stop(Duration::from_millis(3000)); /// std::process::exit(actor_system.await_shutdown()); /// } /// ``` @@ -199,6 +199,11 @@ where .get_actor_ref(actor_address, self.internal_actor_manager.clone()); } + let result = self.system_state.increase_pool_actor_count(&actor_address); + if result.is_err() { + return Err(result.unwrap_err()); + } + let (sender, receiver) = if self.actor_config.mailbox_size == 0 { flume::unbounded() } else { @@ -230,21 +235,13 @@ where match actor_handler { Ok(a) => { - let result = self - .system_state - .add_mailbox(actor_address.clone(), mailbox); - - if result.is_err() { - return Err(result.unwrap_err()); - } - - self.wakeup_manager - .add_inactive_actor(a.get_address(), Arc::new(RwLock::new(a))); - + self.system_state.add_mailbox(actor_address.clone(), mailbox); + self.wakeup_manager.add_inactive_actor(a.get_address(), Arc::new(RwLock::new(a))); self.existing.insert(actor_address, actor_ref.clone()); return Ok(actor_ref); } Err(e) => { + self.system_state.decrease_pool_actor_count(&actor_address); return Err(e); } } diff --git a/src/actor/actor_wrapper.rs b/src/actor/actor_wrapper.rs index 4cb5b1f..6651238 100644 --- a/src/actor/actor_wrapper.rs +++ b/src/actor/actor_wrapper.rs @@ -10,6 +10,7 @@ use crate::system::internal_actor_manager::InternalActorManager; use crate::system::wakeup_manager::WakeupManager; use std::fmt::{Debug, Formatter}; use std::panic::UnwindSafe; +use std::thread::sleep; use std::time::Duration; /// Wrapper used to interact with [Actor] @@ -132,9 +133,28 @@ where &self.address } + /// Returns the current mailbox size pub fn get_mailbox_size(&self) -> usize { return self.mailbox.len(); } + + /// Returns true if an actor is no longer accepting messages + pub fn is_mailbox_stopped(&self) -> bool { + return self.mailbox.is_stopped() + } + + /// Returns true if an actor has been completely stopped after processing all messages that are still within the queue + pub fn is_stopped(&self) -> bool { + return self.get_mailbox_size() == 0 && self.mailbox.is_stopped() + } + + /// Blocks until the actor has been stopped + pub fn wait_for_stop(&self) { + let _ = self.stop(); + while !self.is_stopped() { + sleep(Duration::from_millis(25)); + } + } } impl Clone for ActorWrapper diff --git a/src/actor/handler.rs b/src/actor/handler.rs index 2f7272f..b0b32df 100644 --- a/src/actor/handler.rs +++ b/src/actor/handler.rs @@ -48,7 +48,7 @@ where fn handle( &mut self, _msg: ActorStopMessage, - context: &ActorContext, + _context: &ActorContext, ) -> Result> { return Ok(ActorResult::Stop); } diff --git a/src/config/default.toml b/src/config/default.toml index ca3f81b..4c8ad45 100644 --- a/src/config/default.toml +++ b/src/config/default.toml @@ -11,8 +11,8 @@ default_mailbox_size = 0 default_message_throughput = 15 # defines if the rust panic hook should be overwritten by the actor system on startup override_panic_hook = true -# will start a graceful shutdown on first SIGINT, or force a shutdown if multiple SIGINT are received. Can be disabled by setting to 0 -sigint_graceful_timeout_in_seconds = 300 +# will start a graceful shutdown on first SIGINT, SIGTERM or SIGHUP, or force a shutdown if multiple signals are received. Can be disabled by setting to 0 +signal_graceful_timeout_in_seconds = 300 # default pool settings [thread_pool.config.default] @@ -36,4 +36,48 @@ threads_min = 2 # maximum amount of threads to spawn for this pool threads_max = 3 # num_cpu * factor = amount of threads to spawn for this pool -threads_factor = 1 \ No newline at end of file +threads_factor = 1 + + +##WIP: How cluster config could look like +##### +# +# +## default cluster settings +#[cluster] +## addresses and port used by the cluster. +## if port is omitted, the default of 2022 is used +## if port is set to 0, a port will automatically be assigned +#hosts = ["tcp://127.0.0.1:2022", "tcp://192.168.0.1:2022", "udp://0.0.0.0:2023"] +# +## cluster groups the node should be part of. Valid values are "server", "client" and "proxy" +## - server: can be elected as master node and will open specified ports in hosts +## - client: can't be elected as master node and will ignore cluster.hosts config +## - proxy: will forward traffic between two nodes that can't directly communicate with each other +#groups = ["server", "client", "proxy"] +# +## list of cluster members, entries can include IPs and DNS records, multi A records are supported as well +## if the same node is listed multiple times with different ip addresses, only the first working occurence will be used as a connection +## if port is omitted, the default of 2022 is used +#members = [ +# "tcp://jkasfhjklahsjkghjklalkj.ahjasdjkhaskhjg.asdf:1234", +# "tcp://registry.git.sers.dev", +# "tcp://git.sers.dev", +# "tcp://abc.sers.dev:1234", +#] +## list of cidrs that are allowed to connect +#trusted_cidrs = [ +# "78.47.25.243/32", +# "78.47.42.31/32", +#] +# +## name of the pre shared key that should be used to encrypt outgoing traffic +#active_psk = "default" +# +## list of encryption keys that are used in the cluster +## new connections will go through trial and error to find which key is used by which server +#[cluster.pre_shared_keys] +#default = "sers" +#additional = "hello-world" +# +# \ No newline at end of file diff --git a/src/config/global_config.rs b/src/config/global_config.rs index cc70374..195f3c6 100644 --- a/src/config/global_config.rs +++ b/src/config/global_config.rs @@ -6,5 +6,5 @@ pub struct GeneralConfig { pub default_mailbox_size: usize, pub default_message_throughput: usize, pub override_panic_hook: bool, - pub sigint_graceful_timeout_in_seconds: usize, + pub signal_graceful_timeout_in_seconds: usize, } diff --git a/src/routers/least_message_router.rs b/src/routers/least_message_router.rs index ce489b8..7ff1341 100644 --- a/src/routers/least_message_router.rs +++ b/src/routers/least_message_router.rs @@ -5,7 +5,7 @@ use crate::actor::handler::Handler; use crate::prelude::{Actor, ActorMessage, ActorResult}; use crate::routers::add_actor_message::AddActorMessage; use crate::routers::remove_actor_message::RemoveActorMessage; -use log::error; +use log::{debug, error}; use std::error::Error; pub struct LeastMessageRouter @@ -16,6 +16,8 @@ where min_mailbox_size: usize, route_to: Vec>, can_route: bool, + stop_on_system_stop: bool, + stop_on_empty_targets: bool, } /// implements [ActorFactory](../prelude/trait.ActorFactory.html) to spawn a LeastMessageRouter within an [ActorSystem](../prelude/struct.ActorSystem.html) @@ -66,7 +68,7 @@ where /// .unwrap(); /// /// // create the router, fill it, and route a message -/// let router_factory = LeastMessageRouterFactory::new(15); +/// let router_factory = LeastMessageRouterFactory::new(15, true, true); /// let router = actor_system /// .builder() /// .spawn("router-hello-world", router_factory) @@ -75,13 +77,21 @@ where /// router.send(FooBar{}).unwrap(); /// ``` pub struct LeastMessageRouterFactory { - min_mailbox_size: usize + /// minimum mailbox size, that needs to be exceeded to actually search for the router with the least messages + min_mailbox_size: usize, + /// defines if the actor should automatically be stopped when the system is stopped. If set to false it's up to the user to setup their own shutdown process if they want a quick and clean exit + stop_on_system_stop: bool, + /// defines if the actor should automatically be stopped if it receives a message after all targets have been automatically removed + /// this does not apply if the last target has been removed through a `RemoveActorMessage` + stop_on_empty_targets: bool, } impl LeastMessageRouterFactory { - pub fn new(min_mailbox_size: usize) -> Self { + pub fn new(min_mailbox_size: usize, stop_on_system_stop: bool, stop_on_empty_targets: bool,) -> Self { Self { min_mailbox_size, + stop_on_system_stop, + stop_on_empty_targets, } } } @@ -94,7 +104,7 @@ where &mut self, _context: ActorContext>, ) -> Result, Box> { - return Ok(LeastMessageRouter::new(self.min_mailbox_size)); + return Ok(LeastMessageRouter::new(self.min_mailbox_size, self.stop_on_system_stop, self.stop_on_empty_targets)); } } @@ -102,17 +112,34 @@ impl LeastMessageRouter where A: Actor, { - pub fn new(min_mailbox_size: usize) -> Self { + pub fn new(min_mailbox_size: usize, stop_on_system_stop: bool, stop_on_empty_targets: bool,) -> Self { Self { next_route_index: 0, min_mailbox_size, route_to: Vec::new(), can_route: false, + stop_on_system_stop, + stop_on_empty_targets, } } } -impl Actor for LeastMessageRouter where A: Actor {} +impl Actor for LeastMessageRouter where A: Actor { + fn on_system_stop(&mut self, context: &ActorContext) -> Result> { + if self.stop_on_system_stop { + let result = context.actor_ref.stop(); + if result.is_err() { + error!( + "Could not forward message ActorStopMessage to target {}", + context.actor_ref.get_address().actor + ); + return Ok(ActorResult::Stop); + } + } + return Ok(ActorResult::Ok); + + } +} impl Handler> for LeastMessageRouter where @@ -166,11 +193,32 @@ where return Ok(ActorResult::Ok); } - let mut target = self.route_to.get(self.next_route_index).unwrap(); + let mut target; + // skip/remove stopped actors + loop { + let route_index = self.next_route_index; + target = self.route_to.get(self.next_route_index).unwrap(); + + if target.is_stopped() { + self.next_route_index += 1; + if self.next_route_index >= (self.route_to.len() - 1) { + self.next_route_index = 0; + } + self.route_to.remove(route_index); + if self.route_to.len() == 0 && self.stop_on_empty_targets { + debug!("Stopping router, because all targets have been removed"); + return Ok(ActorResult::Stop) + } + } + else { + break; + } + } + let mut mailbox_size = target.get_mailbox_size(); let target_len = self.route_to.len(); for i in 0..target_len { - if mailbox_size >= self.min_mailbox_size { + if mailbox_size <= self.min_mailbox_size { break; } let potential_target = self.route_to.get(i).unwrap(); diff --git a/src/routers/round_robin_router.rs b/src/routers/round_robin_router.rs index 95b18c7..20a590c 100644 --- a/src/routers/round_robin_router.rs +++ b/src/routers/round_robin_router.rs @@ -7,7 +7,7 @@ use crate::prelude::{Actor, ActorResult, BulkActorMessage}; use crate::routers::add_actor_message::AddActorMessage; use crate::routers::bulk_router_message::BulkRouterMessage; use crate::routers::remove_actor_message::RemoveActorMessage; -use log::error; +use log::{debug, error}; use std::error::Error; pub struct RoundRobinRouter @@ -17,6 +17,8 @@ where route_index: usize, route_to: Vec>, can_route: bool, + stop_on_system_stop: bool, + stop_on_empty_targets: bool, } /// implements [ActorFactory](../prelude/trait.ActorFactory.html) to spawn a RoundRobinRouter within an [ActorSystem](../prelude/struct.ActorSystem.html) @@ -68,7 +70,7 @@ where /// .unwrap(); /// /// // create the router, fill it, and route a message -/// let router_factory = RoundRobinRouterFactory::new(); +/// let router_factory = RoundRobinRouterFactory::new(true, true); /// let router = actor_system /// .builder() /// .spawn("router-hello-world", router_factory) @@ -76,11 +78,20 @@ where /// router.send(AddActorMessage::new(actor.clone())).unwrap(); /// router.send(FooBar{}).unwrap(); /// ``` -pub struct RoundRobinRouterFactory {} +pub struct RoundRobinRouterFactory { + /// defines if the actor should automatically be stopped when the system is stopped. If set to false it's up to the user to setup their own shutdown process if they want a quick and clean exit + stop_on_system_stop: bool, + /// defines if the actor should automatically be stopped if it receives a message after all targets have been automatically removed + /// this does not apply if the last target has been removed through a `RemoveActorMessage + stop_on_empty_targets: bool, +} impl RoundRobinRouterFactory { - pub fn new() -> Self { - Self {} + pub fn new(stop_on_system_stop: bool, stop_on_empty_targets: bool) -> Self { + Self { + stop_on_system_stop, + stop_on_empty_targets, + } } } @@ -92,7 +103,10 @@ where &mut self, _context: ActorContext>, ) -> Result, Box> { - return Ok(RoundRobinRouter::new()); + return Ok(RoundRobinRouter::new( + self.stop_on_system_stop, + self.stop_on_empty_targets, + )); } } @@ -100,16 +114,33 @@ impl RoundRobinRouter where A: Actor, { - pub fn new() -> Self { + pub fn new(stop_on_system_stop: bool, stop_on_empty_targets: bool) -> Self { Self { route_index: 0, route_to: Vec::new(), can_route: false, + stop_on_system_stop, + stop_on_empty_targets, } } } -impl Actor for RoundRobinRouter where A: Actor {} +impl Actor for RoundRobinRouter where A: Actor { + fn on_system_stop(&mut self, context: &ActorContext) -> Result> { + if self.stop_on_system_stop { + let result = context.actor_ref.stop(); + if result.is_err() { + error!( + "Could not forward message ActorStopMessage to target {}", + context.actor_ref.get_address().actor + ); + return Ok(ActorResult::Stop); + } + } + return Ok(ActorResult::Ok); + + } +} impl Handler> for RoundRobinRouter where @@ -163,6 +194,28 @@ where return Ok(ActorResult::Ok); } + + // skip/remove stopped actors + loop { + let route_index = self.route_index; + let target = self.route_to.get(self.route_index).unwrap(); + + if target.is_stopped() { + self.route_index += 1; + if self.route_index >= (self.route_to.len() - 1) { + self.route_index = 0; + } + self.route_to.remove(route_index); + if self.route_to.len() == 0 && self.stop_on_empty_targets { + debug!("Stopping router, because all targets have been removed"); + return Ok(ActorResult::Stop) + } + } + else { + break; + } + } + self.route_index += 1; if self.route_index >= self.route_to.len() { self.route_index = 0; @@ -194,6 +247,27 @@ where return Ok(ActorResult::Ok); } + // skip/remove stopped actors + loop { + let route_index = self.route_index; + let target = self.route_to.get(self.route_index).unwrap(); + + if target.is_stopped() { + self.route_index += 1; + if self.route_index >= (self.route_to.len() - 1) { + self.route_index = 0; + } + self.route_to.remove(route_index); + if self.route_to.len() == 0 && self.stop_on_empty_targets { + debug!("Stopping router, because all targets have been removed"); + return Ok(ActorResult::Stop) + } + } + else { + break; + } + } + let total_messages = msg.data.len(); let total_routees = self.route_to.len(); let messages_per_routee = total_messages / total_routees; diff --git a/src/routers/sharded_router.rs b/src/routers/sharded_router.rs index 9238b12..36c1645 100644 --- a/src/routers/sharded_router.rs +++ b/src/routers/sharded_router.rs @@ -7,7 +7,7 @@ use crate::prelude::{Actor, ActorMessage, ActorResult, BulkActorMessage}; use crate::routers::add_actor_message::AddActorMessage; use crate::routers::bulk_router_message::BulkRouterMessage; use crate::routers::remove_actor_message::RemoveActorMessage; -use log::error; +use log::{debug, error}; use std::collections::HashMap; use std::error::Error; @@ -19,6 +19,8 @@ where route_to: Vec>, sharding: HashMap>, can_route: bool, + stop_on_system_stop: bool, + stop_on_empty_targets: bool, } /// implements [ActorFactory](../prelude/trait.ActorFactory.html) to spawn a ShardedRouter within an [ActorSystem](../prelude/struct.ActorSystem.html) @@ -70,7 +72,7 @@ where /// .unwrap(); /// /// // create the router, fill it, and route a message -/// let router_factory = ShardedRouterFactory::new(); +/// let router_factory = ShardedRouterFactory::new(true, true); /// let router = actor_system /// .builder() /// .spawn("router-hello-world", router_factory) @@ -78,11 +80,20 @@ where /// router.send(AddActorMessage::new(actor.clone())).unwrap(); /// router.send(FooBar{}).unwrap(); /// ``` -pub struct ShardedRouterFactory {} +pub struct ShardedRouterFactory { + /// defines if the actor should automatically be stopped when the system is stopped. If set to false it's up to the user to setup their own shutdown process if they want a quick and clean exit + stop_on_system_stop: bool, + /// defines if the actor should automatically be stopped if it receives a message after all targets have been automatically removed + /// this does not apply if the last target has been removed through a `RemoveActorMessage + stop_on_empty_targets: bool, +} impl ShardedRouterFactory { - pub fn new() -> Self { - Self {} + pub fn new(stop_on_system_stop: bool, stop_on_empty_targets: bool) -> Self { + Self { + stop_on_system_stop, + stop_on_empty_targets, + } } } @@ -94,7 +105,10 @@ where &mut self, _context: ActorContext>, ) -> Result, Box> { - return Ok(ShardedRouter::new()); + return Ok(ShardedRouter::new( + self.stop_on_system_stop, + self.stop_on_empty_targets, + )); } } @@ -102,12 +116,14 @@ impl ShardedRouter where A: Actor, { - pub fn new() -> Self { + pub fn new(stop_on_system_stop: bool, stop_on_empty_targets: bool) -> Self { Self { num_shards: 0, route_to: Vec::new(), sharding: HashMap::new(), can_route: false, + stop_on_system_stop, + stop_on_empty_targets, } } @@ -122,7 +138,22 @@ where } } -impl Actor for ShardedRouter where A: Actor {} +impl Actor for ShardedRouter where A: Actor { + fn on_system_stop(&mut self, context: &ActorContext) -> Result> { + if self.stop_on_system_stop { + let result = context.actor_ref.stop(); + if result.is_err() { + error!( + "Could not forward message ActorStopMessage to target {}", + context.actor_ref.get_address().actor + ); + return Ok(ActorResult::Stop); + } + } + return Ok(ActorResult::Ok); + + } +} impl Handler> for ShardedRouter where @@ -178,8 +209,22 @@ where return Ok(ActorResult::Ok); } - let shard_id = msg.get_id() % self.num_shards; - let forward_to = self.sharding.get(&shard_id).unwrap(); + let mut shard_id = msg.get_id() % self.num_shards; + let mut forward_to = self.sharding.get(&shard_id).unwrap(); + loop { + if !forward_to.is_stopped() { + break; + } + self.route_to.remove(shard_id); + if self.route_to.len() == 0 && self.stop_on_empty_targets { + debug!("Stopping router, because all targets have been removed"); + return Ok(ActorResult::Stop) + } + self.recalculate_shards(); + shard_id = msg.get_id() % self.num_shards; + forward_to = self.sharding.get(&shard_id).unwrap(); + } + let result = forward_to.send(msg); if result.is_err() { error!( @@ -205,6 +250,14 @@ where return Ok(ActorResult::Ok); } + for i in 0..self.route_to.len() { + let target = self.route_to.get(i).unwrap(); + if target.is_stopped() { + self.route_to.remove(i); + self.recalculate_shards(); + } + } + let total_messages = msg.data.len(); let messages_per_routee = total_messages / self.num_shards; diff --git a/src/system/actor_system.rs b/src/system/actor_system.rs index c16a80a..6324215 100644 --- a/src/system/actor_system.rs +++ b/src/system/actor_system.rs @@ -10,6 +10,7 @@ use crate::system::thread_pool_manager::ThreadPoolManager; use crate::system::wakeup_manager::WakeupManager; use dashmap::DashMap; use std::sync::Arc; +use std::sync::atomic::{AtomicBool, Ordering}; use std::thread::sleep; use std::time::Duration; @@ -22,6 +23,7 @@ pub struct ActorSystem { name: String, config: Arc, internal_actor_manager: InternalActorManager, + sigint_received: Arc, } impl ActorSystem { @@ -74,9 +76,10 @@ impl ActorSystem { name: config.general.name.clone(), config: Arc::new(config.clone()), internal_actor_manager: InternalActorManager::new(), + sigint_received: Arc::new(AtomicBool::new(false)), }; - if config.general.sigint_graceful_timeout_in_seconds > 0 { + if config.general.signal_graceful_timeout_in_seconds > 0 { let sys = system.clone(); ctrlc::set_handler(move || { sys.sigint_handler(Duration::from_secs(300)); @@ -100,14 +103,15 @@ impl ActorSystem { /// /// let mut actor_config = TyraConfig::new().unwrap(); /// //disable automatic setup of sigint handling, so that we can set it manually - /// actor_config.general.sigint_graceful_timeout_in_seconds = 0; + /// actor_config.general.signal_graceful_timeout_in_seconds = 0; /// let actor_system = ActorSystem::new(actor_config); /// ctrlc::set_handler(move || {actor_system.sigint_handler(Duration::from_secs(60));}).unwrap(); /// ``` pub fn sigint_handler(&self, graceful_termination_timeout: Duration) { - if self.state.is_stopping() { + if self.sigint_received.load(Ordering::Relaxed) { self.force_stop(); } + self.sigint_received.store(true, Ordering::Relaxed); self.stop(graceful_termination_timeout); } /// Adds a new named pool using the [default pool configuration](https://github.com/sers-dev/tyra/blob/master/src/config/default.toml) diff --git a/src/system/internal_actor_manager.rs b/src/system/internal_actor_manager.rs index 38a8a9c..23aa460 100644 --- a/src/system/internal_actor_manager.rs +++ b/src/system/internal_actor_manager.rs @@ -24,7 +24,7 @@ impl InternalActorManager { .builder() .set_pool_name("tyra") .set_mailbox_unbounded() - .spawn("delay-router", RoundRobinRouterFactory::new()) + .spawn("delay-router", RoundRobinRouterFactory::new(true, true)) .unwrap(); let remaining_actors = system.get_available_actor_count_for_pool("tyra").unwrap(); for i in 0..remaining_actors { diff --git a/src/system/system_state.rs b/src/system/system_state.rs index 9c6aa1d..309f1b6 100644 --- a/src/system/system_state.rs +++ b/src/system/system_state.rs @@ -60,13 +60,15 @@ impl SystemState { fn shutdown(&self, timeout: Duration) { let now = Instant::now(); + while self.get_actor_count() != 0 { + if now.elapsed() >= timeout || self.is_force_stopped.load(Ordering::Relaxed) { self.is_force_stopped.store(true, Ordering::Relaxed); self.mailboxes.clear(); break; } - sleep(Duration::from_millis(10)); + sleep(Duration::from_millis(100)); } self.is_stopped.store(true, Ordering::Relaxed); } @@ -110,24 +112,7 @@ impl SystemState { } } - pub fn remove_mailbox(&self, address: &ActorAddress) { - self.total_actor_count.fetch_sub(1, Ordering::Relaxed); - self.pool_actor_count - .entry(address.pool.clone()) - .and_modify(|v| { - v.fetch_sub(1, Ordering::Relaxed); - }); - self.mailboxes.remove(address); - } - - pub fn add_mailbox( - &self, - address: ActorAddress, - mailbox: Mailbox, - ) -> Result<(), ActorError> - where - A: Handler + 'static, - { + pub fn increase_pool_actor_count(&self, address: &ActorAddress) -> Result<(), ActorError> { let maximum_actor_count = self.max_actors_per_pool.get(&address.pool); if maximum_actor_count.is_none() { return Err(ActorError::ThreadPoolDoesNotExistError); @@ -148,10 +133,36 @@ impl SystemState { current_pool_count.fetch_add(1, Ordering::Relaxed); self.total_actor_count.fetch_add(1, Ordering::Relaxed); - self.mailboxes.insert(address, Arc::new(mailbox)); + return Ok(()); } + pub fn decrease_pool_actor_count(&self, address: &ActorAddress) { + self.pool_actor_count + .entry(address.pool.clone()) + .and_modify(|v| { + v.fetch_sub(1, Ordering::Relaxed); + }); + self.total_actor_count.fetch_sub(1, Ordering::Relaxed); + + } + + pub fn remove_mailbox(&self, address: &ActorAddress) { + self.decrease_pool_actor_count(address); + self.mailboxes.remove(address); + } + + pub fn add_mailbox( + &self, + address: ActorAddress, + mailbox: Mailbox, + ) + where + A: Handler + 'static, + { + self.mailboxes.insert(address, Arc::new(mailbox)); + } + pub fn add_pool_actor_limit(&self, pool_name: String, max_actors: usize) { self.max_actors_per_pool.insert(pool_name, max_actors); } @@ -161,11 +172,18 @@ impl SystemState { if maximum_actor_count.is_none() { return Err(ActorError::ThreadPoolDoesNotExistError); } + let maximum_actor_count = maximum_actor_count.unwrap(); let maximum_actor_count = *maximum_actor_count.value(); - let current_pool_count = self.pool_actor_count.get(pool_name).unwrap(); - let current_pool_count = current_pool_count.value().load(Ordering::Relaxed); + let current_pool_count = self.pool_actor_count.get(pool_name); + + let current_pool_count = if current_pool_count.is_some() { + let current_pool_count = current_pool_count.unwrap(); + current_pool_count.value().load(Ordering::Relaxed) + } else { + 0 as usize + }; if maximum_actor_count == 0 { let result = usize::MAX - current_pool_count; From 7abcdb8ac621b7795fca016902a78d3422860a9f Mon Sep 17 00:00:00 2001 From: Bobonium Date: Sat, 15 Apr 2023 11:33:34 +0200 Subject: [PATCH 05/44] WIP: networking --- examples/webserver.rs | 20 ++++ src/network/mod.rs | 5 +- src/network/network_manager.rs | 161 ++++++++++++++++++++++++++++++++ src/network/tcp_remote_actor.rs | 139 ++++----------------------- 4 files changed, 204 insertions(+), 121 deletions(-) create mode 100644 examples/webserver.rs create mode 100644 src/network/network_manager.rs diff --git a/examples/webserver.rs b/examples/webserver.rs new file mode 100644 index 0000000..d9e5988 --- /dev/null +++ b/examples/webserver.rs @@ -0,0 +1,20 @@ +use tyra::prelude::{ActorSystem, NetworkManagerFactory, ThreadPoolConfig, TyraConfig}; + +fn main() { + // generate config + let mut actor_config = TyraConfig::new().unwrap(); + let cluster = ThreadPoolConfig::new(18, 16, 16, 1.00); + actor_config.thread_pool.config.insert(String::from("tcp-test"), cluster); + // start system with config + let actor_system = ActorSystem::new(actor_config); + // create actor on the system + let _actor = actor_system + .builder() + .set_pool_name("tcp-test") + .spawn("test", NetworkManagerFactory::new(3)) + .unwrap(); + // send a message to the actor + + std::process::exit(actor_system.await_shutdown()); +} + diff --git a/src/network/mod.rs b/src/network/mod.rs index 096eb44..0c21b6b 100644 --- a/src/network/mod.rs +++ b/src/network/mod.rs @@ -1,6 +1,7 @@ pub mod tcp_remote_actor; +pub mod network_manager; pub mod prelude { - pub use crate::network::tcp_remote_actor::TcpRemoteActorFactory; - pub use crate::network::tcp_remote_actor::TcpRemoteActor; + pub use crate::network::network_manager::NetworkManagerFactory; + pub use crate::network::network_manager::NetworkManager; } diff --git a/src/network/network_manager.rs b/src/network/network_manager.rs new file mode 100644 index 0000000..ccefc03 --- /dev/null +++ b/src/network/network_manager.rs @@ -0,0 +1,161 @@ +use std::error::Error; +use std::io::{BufRead, BufReader}; +use std::sync::Arc; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::thread; +use std::net::{TcpListener, TcpStream}; +use std::thread::sleep; +use std::time::Duration; +use threadpool::ThreadPool; +use crate::network::tcp_remote_actor::{NetworkMessage, TcpRemoteActor, TcpRemoteActorFactory}; +use crate::prelude::{Actor, ActorContext, ActorFactory, ActorInitMessage, ActorMessage, ActorResult, ActorWrapper, Handler, SerializedMessage}; +use crate::router::{AddActorMessage, LeastMessageRouter, LeastMessageRouterFactory}; + + +pub struct NetworkManager { + is_stopped: Arc, + is_stopping: Arc, + actors: Vec>, + router: ActorWrapper>, + remote_actor_count: usize, + graceful_shutdown_time_in_seconds: u64 +} + +struct NetworkManagerInitMessage { +} +impl ActorMessage for NetworkManagerInitMessage {} + +impl NetworkManager { + pub fn new(context: ActorContext, graceful_shutdown_time_in_seconds: u64) -> Self { + + context.actor_ref.send(ActorInitMessage::new()).unwrap(); + let router = context.system + .builder() + .set_pool_name(&context.actor_ref.get_address().pool) + .spawn(format!("{}-tcp-router", context.actor_ref.get_address().actor), LeastMessageRouterFactory::new(0, false, true)) + .unwrap(); + + let remote_actor_count = context.system.get_available_actor_count_for_pool(&context.actor_ref.get_address().pool).unwrap(); + let mut actors = Vec::new(); + for i in 0..remote_actor_count { + let actor = context.system + .builder() + .set_pool_name(&context.actor_ref.get_address().pool) + .spawn(format!("{}-tcp-{}", context.actor_ref.get_address().actor, i), TcpRemoteActorFactory::new()) + .unwrap(); + + actors.push(actor.clone()); + router.send(AddActorMessage::new(actor)).unwrap(); + } + + Self { + is_stopped: Arc::new(AtomicBool::new(false)), + is_stopping: Arc::new(AtomicBool::new(false)), + router, + actors, + remote_actor_count, + graceful_shutdown_time_in_seconds, + } + } +} +impl Actor for NetworkManager { + fn pre_stop(&mut self, _context: &ActorContext) { + println!("PRE STOP"); + sleep(Duration::from_secs(self.graceful_shutdown_time_in_seconds as u64)); + println!("manager -> is_stopping =true"); + self.is_stopping.store(true, Ordering::Relaxed); + + } + fn post_stop(&mut self, _context: &ActorContext) { + let _ = self.router.stop(); + for actor in &self.actors { + let _ = actor.stop(); + } + for actor in &self.actors { + actor.wait_for_stop(); + } + + println!("Actors have been stopped!"); + self.is_stopped.store(true, Ordering::Relaxed); + let _ = TcpStream::connect("127.0.0.1:2022"); + } +} + +// define a factory that creates the `Actor` for us +pub struct NetworkManagerFactory { + graceful_shutdown_time_in_seconds: u64 +} +impl NetworkManagerFactory { + pub fn new(graceful_shutdown_time_in_seconds: u64) -> Self { + Self { + graceful_shutdown_time_in_seconds + } + } +} +impl ActorFactory for NetworkManagerFactory { + fn new_actor( + &mut self, + context: ActorContext, + ) -> Result> { + Ok(NetworkManager::new(context, self.graceful_shutdown_time_in_seconds)) + } +} + +// implement our message for the `Actor` +impl Handler for NetworkManager { + fn handle( + &mut self, + _msg: ActorInitMessage, + _context: &ActorContext, + ) -> Result> { + + let is_stopped = self.is_stopped.clone(); + let is_stopping = self.is_stopping.clone(); + let remote_actor_count = self.remote_actor_count; + + let listener = TcpListener::bind("127.0.0.1:2022").unwrap(); + let router = self.router.clone(); + thread::spawn(move || { + let pool = ThreadPool::new(remote_actor_count); + for stream in listener.incoming() { + if is_stopped.load(Ordering::Relaxed) { + println!("NOW THE THREADS DIE!;"); + pool.join(); + println!("ALL DEAD!"); + return; + } + if is_stopping.load(Ordering::Relaxed) { + continue; + } + + match stream { + Ok(mut stream) => { + let router = router.clone(); + pool.execute(move || { + let buf_reader = BufReader::new(&mut stream); + //let http_req = String::new(); + //let http_request = buf_reader.read_line(&mut http_req); + let __http_request: Vec<_> = buf_reader + .lines() + .map(|result| result.unwrap()) + .take_while(|line| !line.is_empty()) + .collect(); + + router.send(NetworkMessage { + id: 1, + stream, + content: SerializedMessage::new(Vec::new()), + }).unwrap(); + }) + }, + Err(_e) => { + println!("SERS"); + }, + } + } + }); + + + Ok(ActorResult::Ok) + } +} \ No newline at end of file diff --git a/src/network/tcp_remote_actor.rs b/src/network/tcp_remote_actor.rs index 806e849..3c63960 100644 --- a/src/network/tcp_remote_actor.rs +++ b/src/network/tcp_remote_actor.rs @@ -1,57 +1,32 @@ use std::error::Error; -use std::io::{BufRead, BufReader, Read, Write}; -use std::net::{TcpListener, TcpStream}; -use std::process::exit; -use std::sync::Arc; -use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; -use std::thread; -use std::thread::{sleep, Thread}; -use std::time::Duration; -use threadpool::ThreadPool; -use crate::actor::actor_address::ActorAddress; -use crate::prelude::{Actor, ActorContext, ActorFactory, ActorInitMessage, ActorMessage, ActorResult, Handler, SerializedMessage}; - -struct NetworkMessage { - address: ActorAddress, - content: SerializedMessage, - id: u32, - stream: TcpStream +use std::io::{Write}; +use std::net::TcpStream; +use crate::prelude::{Actor, ActorContext, ActorFactory, ActorMessage, ActorResult, Handler, SerializedMessage}; + +pub struct NetworkMessage { + pub content: SerializedMessage, + pub id: usize, + pub stream: TcpStream } impl ActorMessage for NetworkMessage {} -pub struct TcpRemoteActor { - listener: Arc, - is_stopped: Arc, - is_stopping: Arc, -} +pub struct TcpRemoteActor {} + impl TcpRemoteActor { pub fn new() -> Self { - println!("SERS!"); - let listener = Arc::new(TcpListener::bind("127.0.0.1:2022").unwrap()); - println!("SERS!"); - - Self { - listener, - is_stopped: Arc::new(AtomicBool::new(false)), - is_stopping: Arc::new(AtomicBool::new(false)), } } } impl Actor for TcpRemoteActor { fn pre_stop(&mut self, _context: &ActorContext) { - println!("PRE-STOP!"); - sleep(Duration::from_secs(3)); - self.is_stopping.store(true, Ordering::Relaxed); - } - fn post_stop(&mut self, _context: &ActorContext) { - println!("POST-STOP!"); - self.is_stopped.store(true, Ordering::Relaxed); + println!("STOPPING TCP REMOTE ACTOR"); } - fn on_system_stop(&mut self, context: &ActorContext) -> Result> { - println!("SYSTEM-STOP"); - Ok(ActorResult::Stop) + fn on_system_stop(&mut self, _context: &ActorContext) -> Result> { + //we intentionally ignore if the actor system is stopped + //we only react if the actor is explicitly stopped by the manager, because there might still be open connections that we don't want to drop + Ok(ActorResult::Ok) } } @@ -65,93 +40,19 @@ impl TcpRemoteActorFactory { impl ActorFactory for TcpRemoteActorFactory { fn new_actor( &mut self, - context: ActorContext, + _context: ActorContext, ) -> Result> { - context.actor_ref.send(ActorInitMessage::new()).unwrap(); Ok(TcpRemoteActor::new()) } } -// implement our message for the `Actor` -impl Handler for TcpRemoteActor { - fn handle( - &mut self, - _msg: ActorInitMessage, - _context: &ActorContext, - ) -> Result> { - println!("AAAAA"); - - let listener = self.listener.clone(); - let is_stopped = self.is_stopped.clone(); - let is_stopping = self.is_stopping.clone(); - let context = _context.clone(); - thread::spawn(move || { - - let mut count = AtomicUsize::new(1); - - - let pool = ThreadPool::new(100); - for stream in listener.incoming() { - //println!("SERS!"); - if is_stopped.load(Ordering::Relaxed) { - println!("EXIT"); - return; - } - if is_stopping.load(Ordering::Relaxed) { - println!("NO MORE NEW CONNECTIONS"); - continue; - } - let counter = count.fetch_add(1, Ordering::Relaxed); - println!("NEW CONNECTION!: {:?}", counter); - - let context = context.clone(); - let mut stream = stream.unwrap(); - pool.execute(move || { - //let context = &context; - - let mut buf_reader = BufReader::new(&mut stream); - let mut http_req = String::new(); - //let http_request = buf_reader.read_line(&mut http_req); - let http_request: Vec<_> = buf_reader - .lines() - .map(|result| result.unwrap()) - .take_while(|line| !line.is_empty()) - .collect(); - - //println!("Request: {:#?}", http_request); - context.actor_ref.send(NetworkMessage{ - id: 1, - stream: stream, - address: context.actor_ref.get_address().clone(), - content: SerializedMessage::new(Vec::new()), - }).unwrap(); - }) - } - }); - - - Ok(ActorResult::Ok) - } -} - impl Handler for TcpRemoteActor { - fn handle(&mut self, mut msg: NetworkMessage, context: &ActorContext) -> Result> { - //println!("HELLO WORLD: {:?}", msg.address.actor); - //println!("ASDF: {:?}", msg.content); - let context = context.clone(); - thread::spawn(move || { - - sleep(Duration::from_secs(3)); - - let response = "HTTP/1.1 200 OK\r\n\r\n

SERS

"; - - msg.stream.write_all(response.as_bytes()).unwrap(); - - context.system.stop(Duration::from_secs(30)); + fn handle(&mut self, mut msg: NetworkMessage, _context: &ActorContext) -> Result> { + println!("RECEIVED MSG!:"); + let response = "HTTP/1.1 200 OK\r\n\r\n

SERS

"; + msg.stream.write_all(response.as_bytes()).unwrap(); - //context.actor_ref.stop().unwrap(); - }); Ok(ActorResult::Ok) } From 778d001f029164cde4ab6a423c44bc4db58c3da9 Mon Sep 17 00:00:00 2001 From: Bobonium Date: Mon, 24 Apr 2023 18:27:08 +0200 Subject: [PATCH 06/44] add alternative approach to networking, based on mio --- Cargo.toml | 4 +- examples/net.rs | 27 +++++ src/lib.rs | 2 + src/net/mod.rs | 13 ++ src/net/net_config.rs | 22 ++++ src/net/net_manager.rs | 264 +++++++++++++++++++++++++++++++++++++++++ src/net/net_worker.rs | 152 ++++++++++++++++++++++++ 7 files changed, 483 insertions(+), 1 deletion(-) create mode 100644 examples/net.rs create mode 100644 src/net/mod.rs create mode 100644 src/net/net_config.rs create mode 100644 src/net/net_manager.rs create mode 100644 src/net/net_worker.rs diff --git a/Cargo.toml b/Cargo.toml index a921ff3..eff4352 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,10 +27,12 @@ flume = "0.10.14" dashmap = "5.4.0" serde = { version = "1.0", features = ["derive"] } serde-encrypt = "0.7.0" -kcp = "0.5.1" thiserror = "1.0" log = "0.4" ctrlc = { version = "3.2.5", features = ["termination"] } +mio = {version = "0.8.6", features = ["os-poll", "os-ext", "net"]} +quiche = "0.17.1" +io-arc = "1.0.0" [dev-dependencies] bincode = "1.3.3" diff --git a/examples/net.rs b/examples/net.rs new file mode 100644 index 0000000..ed32249 --- /dev/null +++ b/examples/net.rs @@ -0,0 +1,27 @@ +use tyra::prelude::{ActorSystem, NetConfig, NetManagerFactory, NetProtocol, ThreadPoolConfig, TyraConfig}; + +fn main() { + // generate config + let mut actor_config = TyraConfig::new().unwrap(); + let cluster = ThreadPoolConfig::new(22, 4, 4, 1.00); + actor_config.thread_pool.config.insert(String::from("mio"), cluster); + // start system with config + let actor_system = ActorSystem::new(actor_config); + // create actor on the system + let mut net_configs = Vec::new(); + net_configs.push(NetConfig::new(NetProtocol::TCP, "0.0.0.0", 2022)); + //net_configs.push(NetConfig::new(NetProtocol::TCP, "10.0.10.10", 2022)); + //net_configs.push(NetConfig::new(NetProtocol::UDP, "10.0.10.10", 2023)); + + + let _actor = actor_system + .builder() + .set_pool_name("mio") + .spawn("test", NetManagerFactory::new(net_configs, 10)) + .unwrap(); + + // send a message to the actor + + std::process::exit(actor_system.await_shutdown()); +} + diff --git a/src/lib.rs b/src/lib.rs index c6e9776..baa7e0a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -110,6 +110,7 @@ mod message; mod routers; mod system; mod network; +mod net; /// core components pub mod prelude { @@ -118,6 +119,7 @@ pub mod prelude { pub use crate::message::prelude::*; pub use crate::system::prelude::*; pub use crate::network::prelude::*; + pub use crate::net::prelude::*; } /// collection of different router implementations diff --git a/src/net/mod.rs b/src/net/mod.rs new file mode 100644 index 0000000..be5cd39 --- /dev/null +++ b/src/net/mod.rs @@ -0,0 +1,13 @@ +mod net_manager; +mod net_config; +mod net_worker; + +pub mod prelude { + pub use crate::net::net_manager::NetManagerFactory; + pub use crate::net::net_manager::NetManager; + pub use crate::net::net_config::NetConfig; + pub use crate::net::net_config::NetProtocol; + + + +} diff --git a/src/net/net_config.rs b/src/net/net_config.rs new file mode 100644 index 0000000..04d9ece --- /dev/null +++ b/src/net/net_config.rs @@ -0,0 +1,22 @@ +#[derive(Clone)] +pub enum NetProtocol { + UDP, + TCP +} + +#[derive(Clone)] +pub struct NetConfig { + pub protocol: NetProtocol, + pub host: String, + pub port: usize, +} + +impl NetConfig { + pub fn new(protocol: NetProtocol, host: impl Into, port: usize) -> Self { + Self { + protocol, + host: host.into(), + port, + } + } +} \ No newline at end of file diff --git a/src/net/net_manager.rs b/src/net/net_manager.rs new file mode 100644 index 0000000..29d296c --- /dev/null +++ b/src/net/net_manager.rs @@ -0,0 +1,264 @@ +use std::collections::HashMap; +use std::error::Error; +use std::io::{BufRead, BufReader}; +use std::net::Shutdown; +use std::sync::Arc; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::thread; +use std::thread::sleep; +use std::time::Duration; +use io_arc::IoArc; +use log::{error, warn}; +use mio::net::{TcpListener, TcpStream}; +use mio::{Events, Interest, Poll, Token}; +use mio::event::Source; +use crate::net::net_worker::{AddTcpConnection, NetWorker, NetWorkerFactory, ReceivedTcpMessage, RemoveTcpConnection}; +use crate::prelude::{Actor, ActorContext, ActorFactory, ActorInitMessage, ActorResult, ActorWrapper, Handler, NetConfig, NetProtocol}; +use crate::router::{AddActorMessage, ShardedRouter, ShardedRouterFactory}; + +pub struct NetManager { + graceful_shutdown_time_in_seconds: usize, + router: ActorWrapper>, + workers: Vec>, + net_configs: Vec, + is_stopping: Arc, + is_stopped: Arc, + +} + +impl NetManager { + pub fn new(context: ActorContext, net_configs: Vec, graceful_shutdown_time_in_seconds: usize) -> Self { + + let pool_name = &context.actor_ref.get_address().pool; + + let worker_count = context.system.get_available_actor_count_for_pool(pool_name).unwrap(); + let mut workers = Vec::new(); + let router = context.system.builder().set_pool_name(pool_name).spawn("net-least-message", ShardedRouterFactory::new( false, false)).unwrap(); + + for i in 0..worker_count - 1 { + let worker = context.system.builder().set_pool_name(pool_name).spawn(format!("net-worker-{}", i), NetWorkerFactory::new()).unwrap(); + router.send(AddActorMessage::new(worker.clone())).unwrap(); + workers.push(worker); + } + + let is_stopping = Arc::new(AtomicBool::new(false)); + let is_stopped = Arc::new(AtomicBool::new(false)); + + return Self { + graceful_shutdown_time_in_seconds, + router, + workers, + net_configs, + is_stopping, + is_stopped, + }; + } +} +impl Actor for NetManager { + fn pre_stop(&mut self, _context: &ActorContext) { + if self.graceful_shutdown_time_in_seconds == 0 { + self.is_stopping.store(true, Ordering::Relaxed); + return; + } + let graceful_stop_in_millis = self.graceful_shutdown_time_in_seconds * 1000; + let iterations = 10; + let iterate_graceful_stop_in_millis = graceful_stop_in_millis / iterations; + + + sleep(Duration::from_millis(iterate_graceful_stop_in_millis as u64)); + + self.is_stopping.store(true, Ordering::Relaxed); + + for _ in 0..iterations { + for net_config in &self.net_configs { + let address = format!("{}:{}", net_config.host, net_config.port); + match net_config.protocol { + NetProtocol::TCP => { + let _ = TcpStream::connect(address.parse().unwrap()); + break; + } + NetProtocol::UDP => { + break; + } + } + } + if self.is_stopped.load(Ordering::Relaxed) { + return; + } + sleep(Duration::from_millis(iterate_graceful_stop_in_millis as u64)); + + } + } + fn post_stop(&mut self, _context: &ActorContext) { + let _ = self.router.stop(); + for worker in &self.workers { + let _ = worker.stop(); + } + for worker in &self.workers { + worker.wait_for_stop(); + } + + self.is_stopped.store(true, Ordering::Relaxed); + for net_config in &self.net_configs { + let address = format!("{}:{}", net_config.host, net_config.port); + match net_config.protocol { + NetProtocol::TCP => { + let _ = TcpStream::connect(address.parse().unwrap()); + break; + } + NetProtocol::UDP => { + break; + } + } + } + } +} + + +pub struct NetManagerFactory { + net_configs: Vec, + graceful_shutdown_time_in_seconds: usize, +} + +impl NetManagerFactory { + pub fn new(net_configs: Vec, graceful_shutdown_time_in_seconds: usize) -> Self { + return Self { + net_configs, + graceful_shutdown_time_in_seconds, + }; + } +} +impl ActorFactory for NetManagerFactory { + fn new_actor(&mut self, context: ActorContext) -> Result> { + context.actor_ref.send(ActorInitMessage::new()).unwrap(); + return Ok(NetManager::new(context, self.net_configs.clone(), self.graceful_shutdown_time_in_seconds)); + } +} + +impl Handler for NetManager { + fn handle(&mut self, _msg: ActorInitMessage, _context: &ActorContext) -> Result> { + let mut tcp_listeners :HashMap = HashMap::new(); + //let mut udp_listeners :HashMap = HashMap::new(); + + let mut poll = Poll::new().unwrap(); + let mut i = 0; + for net_config in &self.net_configs { + let token = Token(i); + i += 1; + + let address = format!("{}:{}", net_config.host, net_config.port); + match net_config.protocol { + NetProtocol::TCP => { + let mut listener = TcpListener::bind(address.parse().unwrap()).unwrap(); + poll.registry().register(&mut listener, token, Interest::READABLE).unwrap(); + tcp_listeners.insert(token, listener); + }, + NetProtocol::UDP => { + //let mut listener = UdpSocket::bind(address.parse().unwrap()).unwrap(); + //poll.registry().register(&mut listener, Token(i + tcp_listeners.len()), Interest::READABLE).unwrap(); + //udp_listeners.insert(token, listener); + } + } + } + + let router = self.router.clone(); + let num_listeners = self.net_configs.len(); + let is_stopping = self.is_stopping.clone(); + let is_stopped = self.is_stopped.clone(); + thread::spawn(move || { + let mut i = num_listeners; + let mut events = Events::with_capacity(1024); + let mut streams = HashMap::new(); + + loop { + + if is_stopped.load(Ordering::Relaxed) { + return; + } + + poll.poll(&mut events, None).unwrap(); + + + + for event in &events { + let stopping = is_stopping.load(Ordering::Relaxed); + if stopping && streams.len() == 0 { + is_stopped.store(true, Ordering::Relaxed); + break; + } + let token = &event.token(); + if token.0 < num_listeners { + let listener = tcp_listeners.get(token).unwrap(); + + loop { + match listener.accept() { + Ok((mut socket, address)) => { + if stopping { + let _ = socket.shutdown(Shutdown::Both); + continue; + } + let res = socket.register(poll.registry(), Token(i), Interest::READABLE); + if res.is_err() { + error!("Could not register TcpStream. {:?}", res.err()); + } + let sock = IoArc::new(socket); + streams.insert(i, (sock.clone(), address.clone())); + let _ = router.send(AddTcpConnection::new(i, sock, address)); + + i += 1; + if i < num_listeners { + i = num_listeners; + } + } + Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => { + break; + } + Err(e) => { + error!("Something went wrong with the Listener. {:?}", e); + break; + } + } + } + } + else { + if event.is_read_closed() || event.is_write_closed() { + let _ = streams.remove(&token.0); + let _ = router.send(RemoveTcpConnection::new(token.0)); + + } + else if event.is_readable() { + let stream = streams.get(&token.0); + if stream.is_none() { + let _ = streams.remove(&token.0); + let _ = router.send(RemoveTcpConnection::new(token.0)); + continue; + } + let (stream, _address) = stream.unwrap(); + let buf_reader = BufReader::new(stream.clone()); + let request: Vec = buf_reader + .lines() + .map(|result| { + match result { + Ok(res) => { + return res; + } + Err(err) => { + warn!("Could not read from stream: {:?}", err); + return String::from(""); + } + } + }) + .take_while(|line| !line.is_empty()) + .collect(); + if !request.is_empty() { + let _ = router.send(ReceivedTcpMessage::new(token.0, request)); + } + + } + } + } + } + }); + return Ok(ActorResult::Ok); + } +} \ No newline at end of file diff --git a/src/net/net_worker.rs b/src/net/net_worker.rs new file mode 100644 index 0000000..ae40234 --- /dev/null +++ b/src/net/net_worker.rs @@ -0,0 +1,152 @@ +use std::collections::HashMap; +use std::error::Error; +use std::io::Write; +use std::net::{Shutdown, SocketAddr}; +use io_arc::IoArc; +use log::{debug, warn}; +use mio::net::TcpStream; +use crate::prelude::{Actor, ActorContext, ActorFactory, ActorMessage, ActorResult, Handler}; + +pub struct NetWorker { + streams: HashMap, SocketAddr)>, + +} + +impl NetWorker { + pub fn new() -> Self { + return Self { + streams: HashMap::new(), + }; + } +} +impl Actor for NetWorker { + fn on_system_stop(&mut self, _context: &ActorContext) -> Result> { + //we intentionally ignore if the actor system is stopped + //we only react if the actor is explicitly stopped by the manager, because there might still be open connections that we don't want to drop + Ok(ActorResult::Ok) + } +} + +pub struct NetWorkerFactory {} +impl ActorFactory for NetWorkerFactory { + fn new_actor(&mut self, _context: ActorContext) -> Result> { + return Ok(NetWorker::new()); + } +} + +impl NetWorkerFactory { + pub fn new() -> Self { + return Self { + + }; + } +} + +pub struct AddTcpConnection { + pub stream_id: usize, + pub stream: IoArc, + pub address: SocketAddr, +} + +impl AddTcpConnection { + pub fn new(stream_id: usize, stream: IoArc, address: SocketAddr) -> Self { + return Self { + stream_id, + stream, + address, + }; + } +} + +pub struct RemoveTcpConnection { + pub stream_id: usize, +} + +impl ActorMessage for AddTcpConnection { + fn get_id(&self) -> usize { + return self.stream_id; + } +} + +impl RemoveTcpConnection { + pub fn new(stream_id: usize) -> Self { + return Self { + stream_id, + }; + } +} + +impl ActorMessage for RemoveTcpConnection { + fn get_id(&self) -> usize { + return self.stream_id; + } +} + +pub struct ReceivedTcpMessage { + pub stream_id: usize, + pub request: Vec, +} + +impl ReceivedTcpMessage { + pub fn new(stream_id: usize, request: Vec) -> Self { + return Self { + stream_id, + request, + }; + } +} + +impl ActorMessage for ReceivedTcpMessage { + fn get_id(&self) -> usize { + return self.stream_id; + } +} + +impl Handler for NetWorker { + fn handle(&mut self, msg: ReceivedTcpMessage, _context: &ActorContext) -> Result> { + let stream = self.streams.get_mut(&msg.stream_id); + if stream.is_none() { + // temporary implementation for our instant http response, later on we won't have to care here if the stream is active, we'll just forward the message + debug!("Stream ID no longer exists, can't reply to request"); + return Ok(ActorResult::Ok); + } + let (stream, _) = stream.unwrap(); + stream.write_all("HTTP/1.1 200 OK\nContent-Type: text/html\nConnection: keep-alive\nContent-Length: 12\r\n\r\nHELLO-WORLD!".as_bytes()).unwrap(); + + // temporary implementation for our instant http response + // drops the connection if keep-alive has not been specified + let mut shutdown_connection = true; + for k in msg.request { + if k == "Connection: Keep-Alive" { + shutdown_connection = false; + break; + } + } + if shutdown_connection { + let _ = stream.as_ref().shutdown(Shutdown::Both); + } + + return Ok(ActorResult::Ok); + } +} + +impl Handler for NetWorker { + fn handle(&mut self, msg: AddTcpConnection, _context: &ActorContext) -> Result> { + let key_already_exists = self.streams.remove(&msg.stream_id); + if key_already_exists.is_some() { + warn!("Stream ID already exists, dropping old one in favor of the new connection."); + let (stream, _) = key_already_exists.unwrap(); + let _ = stream.as_ref().shutdown(Shutdown::Both); + } + + let _ = self.streams.insert(msg.stream_id, (msg.stream, msg.address)); + return Ok(ActorResult::Ok); + } +} + +impl Handler for NetWorker { + fn handle(&mut self, msg: RemoveTcpConnection, _context: &ActorContext) -> Result> { + let _ = self.streams.remove(&msg.stream_id); + return Ok(ActorResult::Ok); + } +} \ No newline at end of file From fd0babcee7ceba3d475bfd6045af36ba90451f68 Mon Sep 17 00:00:00 2001 From: Bobonium Date: Tue, 25 Apr 2023 19:51:21 +0200 Subject: [PATCH 07/44] added support for udp --- examples/net.rs | 3 +- src/net/net_config.rs | 4 +- src/net/net_manager.rs | 97 +++++++++++++++++++++++++++--------------- src/net/net_worker.rs | 85 +++++++++++++++++++++++++++++++++--- 4 files changed, 144 insertions(+), 45 deletions(-) diff --git a/examples/net.rs b/examples/net.rs index ed32249..801a375 100644 --- a/examples/net.rs +++ b/examples/net.rs @@ -9,9 +9,8 @@ fn main() { let actor_system = ActorSystem::new(actor_config); // create actor on the system let mut net_configs = Vec::new(); + net_configs.push(NetConfig::new(NetProtocol::UDP, "0.0.0.0", 2023)); net_configs.push(NetConfig::new(NetProtocol::TCP, "0.0.0.0", 2022)); - //net_configs.push(NetConfig::new(NetProtocol::TCP, "10.0.10.10", 2022)); - //net_configs.push(NetConfig::new(NetProtocol::UDP, "10.0.10.10", 2023)); let _actor = actor_system diff --git a/src/net/net_config.rs b/src/net/net_config.rs index 04d9ece..b2774b2 100644 --- a/src/net/net_config.rs +++ b/src/net/net_config.rs @@ -1,7 +1,7 @@ -#[derive(Clone)] +#[derive(Clone, Ord, Eq, PartialOrd, PartialEq, Copy)] pub enum NetProtocol { + TCP, UDP, - TCP } #[derive(Clone)] diff --git a/src/net/net_manager.rs b/src/net/net_manager.rs index 29d296c..bf49db0 100644 --- a/src/net/net_manager.rs +++ b/src/net/net_manager.rs @@ -2,6 +2,7 @@ use std::collections::HashMap; use std::error::Error; use std::io::{BufRead, BufReader}; use std::net::Shutdown; +use std::ops::Deref; use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; use std::thread; @@ -9,10 +10,10 @@ use std::thread::sleep; use std::time::Duration; use io_arc::IoArc; use log::{error, warn}; -use mio::net::{TcpListener, TcpStream}; +use mio::net::{TcpListener, TcpStream, UdpSocket}; use mio::{Events, Interest, Poll, Token}; use mio::event::Source; -use crate::net::net_worker::{AddTcpConnection, NetWorker, NetWorkerFactory, ReceivedTcpMessage, RemoveTcpConnection}; +use crate::net::net_worker::{AddTcpConnection, AddUdpSocket, NetWorker, NetWorkerFactory, ReceiveTcpMessage, ReceiveUdpMessage, RemoveTcpConnection}; use crate::prelude::{Actor, ActorContext, ActorFactory, ActorInitMessage, ActorResult, ActorWrapper, Handler, NetConfig, NetProtocol}; use crate::router::{AddActorMessage, ShardedRouter, ShardedRouterFactory}; @@ -137,39 +138,47 @@ impl ActorFactory for NetManagerFactory { impl Handler for NetManager { fn handle(&mut self, _msg: ActorInitMessage, _context: &ActorContext) -> Result> { - let mut tcp_listeners :HashMap = HashMap::new(); - //let mut udp_listeners :HashMap = HashMap::new(); + let router = self.router.clone(); + let is_stopping = self.is_stopping.clone(); + let is_stopped = self.is_stopped.clone(); + let mut net_configs = self.net_configs.clone(); - let mut poll = Poll::new().unwrap(); - let mut i = 0; - for net_config in &self.net_configs { - let token = Token(i); - i += 1; + thread::spawn(move || { + let mut tcp_listeners :HashMap = HashMap::new(); + let mut udp_sockets:HashMap> = HashMap::new(); + let mut poll = Poll::new().unwrap(); - let address = format!("{}:{}", net_config.host, net_config.port); - match net_config.protocol { - NetProtocol::TCP => { - let mut listener = TcpListener::bind(address.parse().unwrap()).unwrap(); - poll.registry().register(&mut listener, token, Interest::READABLE).unwrap(); - tcp_listeners.insert(token, listener); - }, - NetProtocol::UDP => { - //let mut listener = UdpSocket::bind(address.parse().unwrap()).unwrap(); - //poll.registry().register(&mut listener, Token(i + tcp_listeners.len()), Interest::READABLE).unwrap(); - //udp_listeners.insert(token, listener); + let mut i = 0; + net_configs.sort_by_key(|c| c.protocol); + for net_config in &net_configs { + let token = Token(i); + i += 1; + + let address = format!("{}:{}", net_config.host, net_config.port).parse().unwrap(); + + match net_config.protocol { + NetProtocol::TCP => { + let mut listener = TcpListener::bind(address).unwrap(); + poll.registry().register(&mut listener, token, Interest::READABLE).unwrap(); + tcp_listeners.insert(token, listener); + }, + NetProtocol::UDP => { + let mut socket = UdpSocket::bind(address).unwrap(); + poll.registry().register(&mut socket, token, Interest::READABLE).unwrap(); + let socket = IoArc::new(socket); + udp_sockets.insert(token, socket.clone()); + let _ = router.send(AddUdpSocket::new(token.0, socket)); + } } } - } + let num_tcp_listeners = tcp_listeners.len(); + let num_total_listeners = net_configs.len(); + - let router = self.router.clone(); - let num_listeners = self.net_configs.len(); - let is_stopping = self.is_stopping.clone(); - let is_stopped = self.is_stopped.clone(); - thread::spawn(move || { - let mut i = num_listeners; let mut events = Events::with_capacity(1024); let mut streams = HashMap::new(); + let mut buf = [0; 65535]; loop { if is_stopped.load(Ordering::Relaxed) { @@ -178,16 +187,14 @@ impl Handler for NetManager { poll.poll(&mut events, None).unwrap(); - - - for event in &events { + for event in events.iter() { let stopping = is_stopping.load(Ordering::Relaxed); if stopping && streams.len() == 0 { is_stopped.store(true, Ordering::Relaxed); break; } let token = &event.token(); - if token.0 < num_listeners { + if token.0 < num_tcp_listeners { let listener = tcp_listeners.get(token).unwrap(); loop { @@ -206,8 +213,8 @@ impl Handler for NetManager { let _ = router.send(AddTcpConnection::new(i, sock, address)); i += 1; - if i < num_listeners { - i = num_listeners; + if i < num_total_listeners { + i = num_total_listeners; } } Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => { @@ -220,6 +227,28 @@ impl Handler for NetManager { } } } + else if token.0 < num_total_listeners { + //UDP handling + let socket = udp_sockets.get(&token); + if socket.is_none() { + error!("Something went wrong with the UDP Socket."); + } + let socket = socket.unwrap(); + + let (len, from) = match socket.as_ref().recv_from(&mut buf) { + Ok(v) => v, + + Err(e) => { + if e.kind() == std::io::ErrorKind::WouldBlock { + continue; + } + panic!("recv() failed: {:?}", e); + }, + }; + let request = String::from_utf8_lossy(&buf[..len]); + let _ = router.send(ReceiveUdpMessage::new(token.0, from, request.into_owned())); + + } else { if event.is_read_closed() || event.is_write_closed() { let _ = streams.remove(&token.0); @@ -251,7 +280,7 @@ impl Handler for NetManager { .take_while(|line| !line.is_empty()) .collect(); if !request.is_empty() { - let _ = router.send(ReceivedTcpMessage::new(token.0, request)); + let _ = router.send(ReceiveTcpMessage::new(token.0, request)); } } diff --git a/src/net/net_worker.rs b/src/net/net_worker.rs index ae40234..fbf0ddb 100644 --- a/src/net/net_worker.rs +++ b/src/net/net_worker.rs @@ -2,20 +2,22 @@ use std::collections::HashMap; use std::error::Error; use std::io::Write; use std::net::{Shutdown, SocketAddr}; +use std::sync::Arc; use io_arc::IoArc; use log::{debug, warn}; -use mio::net::TcpStream; +use mio::net::{TcpStream, UdpSocket}; use crate::prelude::{Actor, ActorContext, ActorFactory, ActorMessage, ActorResult, Handler}; pub struct NetWorker { streams: HashMap, SocketAddr)>, - + sockets: HashMap>, } impl NetWorker { pub fn new() -> Self { return Self { streams: HashMap::new(), + sockets: HashMap::new(), }; } } @@ -82,12 +84,12 @@ impl ActorMessage for RemoveTcpConnection { } } -pub struct ReceivedTcpMessage { +pub struct ReceiveTcpMessage { pub stream_id: usize, pub request: Vec, } -impl ReceivedTcpMessage { +impl ReceiveTcpMessage { pub fn new(stream_id: usize, request: Vec) -> Self { return Self { stream_id, @@ -96,14 +98,55 @@ impl ReceivedTcpMessage { } } -impl ActorMessage for ReceivedTcpMessage { +impl ActorMessage for ReceiveTcpMessage { fn get_id(&self) -> usize { return self.stream_id; } } -impl Handler for NetWorker { - fn handle(&mut self, msg: ReceivedTcpMessage, _context: &ActorContext) -> Result> { +pub struct AddUdpSocket { + socket_id: usize, + socket: IoArc, +} +impl AddUdpSocket { + pub fn new(socket_id: usize, socket: IoArc) -> Self { + return Self { + socket_id, + socket, + }; + } +} + +impl ActorMessage for AddUdpSocket { + fn get_id(&self) -> usize { + return 1; + } +} + +pub struct ReceiveUdpMessage { + socket_id: usize, + source: SocketAddr, + request: String, +} + +impl ReceiveUdpMessage { + pub fn new(socket_id: usize, source: SocketAddr, request: String) -> Self { + return Self { + socket_id, + source, + request, + }; + } +} + +impl ActorMessage for ReceiveUdpMessage { + fn get_id(&self) -> usize { + return 1; + } +} + +impl Handler for NetWorker { + fn handle(&mut self, msg: ReceiveTcpMessage, _context: &ActorContext) -> Result> { let stream = self.streams.get_mut(&msg.stream_id); if stream.is_none() { // temporary implementation for our instant http response, later on we won't have to care here if the stream is active, we'll just forward the message @@ -149,4 +192,32 @@ impl Handler for NetWorker { let _ = self.streams.remove(&msg.stream_id); return Ok(ActorResult::Ok); } +} + +impl Handler for NetWorker { + fn handle(&mut self, msg: ReceiveUdpMessage, context: &ActorContext) -> Result> { + let socket = self.sockets.get_mut(&msg.socket_id); + if socket.is_none() { + // temporary implementation for our instant http response, later on we won't have to care here if the stream is active, we'll just forward the message + debug!("Socket ID no longer exists, can't reply to request"); + return Ok(ActorResult::Ok); + } + let socket = socket.unwrap(); + let _ = socket.as_ref().send_to(msg.request.as_bytes(), msg.source); + + return Ok(ActorResult::Ok); + } +} + +impl Handler for NetWorker { + fn handle(&mut self, msg: AddUdpSocket, context: &ActorContext) -> Result> { + let key_already_exists = self.sockets.remove(&msg.socket_id); + if key_already_exists.is_some() { + warn!("Socket ID already exists, dropping old one in favor of the new."); + let socket = key_already_exists.unwrap(); + } + + let _ = self.sockets.insert(msg.socket_id, msg.socket); + return Ok(ActorResult::Ok); + } } \ No newline at end of file From 5a4604a839e0ee93ad06daca363ffd815832902a Mon Sep 17 00:00:00 2001 From: Bobonium Date: Wed, 26 Apr 2023 20:32:26 +0200 Subject: [PATCH 08/44] net_worker can now be dynamically be provided to net_manager for different network handler implementations --- examples/net.rs | 5 ++-- src/lib.rs | 1 + src/net/mod.rs | 3 +- src/net/net_manager.rs | 65 +++++++++++++++++++++++++++++++----------- src/net/net_worker.rs | 8 +++--- 5 files changed, 58 insertions(+), 24 deletions(-) diff --git a/examples/net.rs b/examples/net.rs index 801a375..56a9e77 100644 --- a/examples/net.rs +++ b/examples/net.rs @@ -1,4 +1,4 @@ -use tyra::prelude::{ActorSystem, NetConfig, NetManagerFactory, NetProtocol, ThreadPoolConfig, TyraConfig}; +use tyra::prelude::{ActorSystem, NetConfig, NetManagerFactory, NetProtocol, NetWorkerFactory, ThreadPoolConfig, TyraConfig}; fn main() { // generate config @@ -13,10 +13,11 @@ fn main() { net_configs.push(NetConfig::new(NetProtocol::TCP, "0.0.0.0", 2022)); + let worker_factory = NetWorkerFactory::new(); let _actor = actor_system .builder() .set_pool_name("mio") - .spawn("test", NetManagerFactory::new(net_configs, 10)) + .spawn("test", NetManagerFactory::new(net_configs, 10, worker_factory)) .unwrap(); // send a message to the actor diff --git a/src/lib.rs b/src/lib.rs index baa7e0a..42f18e9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,6 +25,7 @@ //! impl Actor for HelloWorld {} //! //! // setup required Factory +//! #[derive(Clone)] //! struct HelloWorldFactory {} //! impl ActorFactory for HelloWorldFactory { //! fn new_actor(&mut self, _context: ActorContext) -> Result> { diff --git a/src/net/mod.rs b/src/net/mod.rs index be5cd39..f83ff79 100644 --- a/src/net/mod.rs +++ b/src/net/mod.rs @@ -7,7 +7,8 @@ pub mod prelude { pub use crate::net::net_manager::NetManager; pub use crate::net::net_config::NetConfig; pub use crate::net::net_config::NetProtocol; - + pub use crate::net::net_worker::NetWorkerFactory; + pub use crate::net::net_worker::NetWorker; } diff --git a/src/net/net_manager.rs b/src/net/net_manager.rs index bf49db0..3c5a78f 100644 --- a/src/net/net_manager.rs +++ b/src/net/net_manager.rs @@ -1,8 +1,8 @@ use std::collections::HashMap; use std::error::Error; use std::io::{BufRead, BufReader}; +use std::marker::PhantomData; use std::net::Shutdown; -use std::ops::Deref; use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; use std::thread; @@ -13,22 +13,31 @@ use log::{error, warn}; use mio::net::{TcpListener, TcpStream, UdpSocket}; use mio::{Events, Interest, Poll, Token}; use mio::event::Source; -use crate::net::net_worker::{AddTcpConnection, AddUdpSocket, NetWorker, NetWorkerFactory, ReceiveTcpMessage, ReceiveUdpMessage, RemoveTcpConnection}; +use crate::net::net_worker::{AddTcpConnection, AddUdpSocket, ReceiveTcpMessage, ReceiveUdpMessage, RemoveTcpConnection}; use crate::prelude::{Actor, ActorContext, ActorFactory, ActorInitMessage, ActorResult, ActorWrapper, Handler, NetConfig, NetProtocol}; use crate::router::{AddActorMessage, ShardedRouter, ShardedRouterFactory}; -pub struct NetManager { +pub struct NetManager + where + T: Handler + Handler + Handler + Handler + Handler + 'static, +{ graceful_shutdown_time_in_seconds: usize, - router: ActorWrapper>, - workers: Vec>, + router: ActorWrapper>, + workers: Vec>, net_configs: Vec, is_stopping: Arc, is_stopped: Arc, + } -impl NetManager { - pub fn new(context: ActorContext, net_configs: Vec, graceful_shutdown_time_in_seconds: usize) -> Self { +impl NetManager + where + + T: Handler + Handler + Handler + Handler + Handler + 'static, +{ + pub fn new(context: ActorContext, net_configs: Vec, graceful_shutdown_time_in_seconds: usize, worker_factory: F) -> Self + where F: ActorFactory + Clone + 'static, { let pool_name = &context.actor_ref.get_address().pool; @@ -37,7 +46,7 @@ impl NetManager { let router = context.system.builder().set_pool_name(pool_name).spawn("net-least-message", ShardedRouterFactory::new( false, false)).unwrap(); for i in 0..worker_count - 1 { - let worker = context.system.builder().set_pool_name(pool_name).spawn(format!("net-worker-{}", i), NetWorkerFactory::new()).unwrap(); + let worker = context.system.builder().set_pool_name(pool_name).spawn(format!("net-worker-{}", i), worker_factory.clone()).unwrap(); router.send(AddActorMessage::new(worker.clone())).unwrap(); workers.push(worker); } @@ -55,7 +64,10 @@ impl NetManager { }; } } -impl Actor for NetManager { +impl Actor for NetManager + where + T: Handler + Handler + Handler + Handler + Handler + 'static, +{ fn pre_stop(&mut self, _context: &ActorContext) { if self.graceful_shutdown_time_in_seconds == 0 { self.is_stopping.store(true, Ordering::Relaxed); @@ -115,28 +127,47 @@ impl Actor for NetManager { } } - -pub struct NetManagerFactory { +pub struct NetManagerFactory +where + F: ActorFactory + Clone + 'static, + T: Handler + Handler + Handler + Handler + Handler + 'static, +{ net_configs: Vec, graceful_shutdown_time_in_seconds: usize, + worker_factory: F, + phantom: PhantomData, } -impl NetManagerFactory { - pub fn new(net_configs: Vec, graceful_shutdown_time_in_seconds: usize) -> Self { +impl NetManagerFactory +where + F: ActorFactory + Clone + 'static, + T: Handler + Handler + Handler + Handler + Handler + 'static, + +{ + pub fn new(net_configs: Vec, graceful_shutdown_time_in_seconds: usize, worker_factory: F) -> Self { return Self { net_configs, graceful_shutdown_time_in_seconds, + worker_factory, + phantom: PhantomData, }; } } -impl ActorFactory for NetManagerFactory { - fn new_actor(&mut self, context: ActorContext) -> Result> { +impl ActorFactory> for NetManagerFactory +where + F: ActorFactory + Clone + 'static, + T: Handler + Handler + Handler + Handler + Handler + 'static, +{ + fn new_actor(&mut self, context: ActorContext>) -> Result, Box> { context.actor_ref.send(ActorInitMessage::new()).unwrap(); - return Ok(NetManager::new(context, self.net_configs.clone(), self.graceful_shutdown_time_in_seconds)); + return Ok(NetManager::new(context, self.net_configs.clone(), self.graceful_shutdown_time_in_seconds, self.worker_factory.clone())); } } -impl Handler for NetManager { +impl Handler for NetManager + where + T: Handler + Handler + Handler + Handler + Handler + 'static, +{ fn handle(&mut self, _msg: ActorInitMessage, _context: &ActorContext) -> Result> { let router = self.router.clone(); let is_stopping = self.is_stopping.clone(); diff --git a/src/net/net_worker.rs b/src/net/net_worker.rs index fbf0ddb..e094a77 100644 --- a/src/net/net_worker.rs +++ b/src/net/net_worker.rs @@ -2,12 +2,12 @@ use std::collections::HashMap; use std::error::Error; use std::io::Write; use std::net::{Shutdown, SocketAddr}; -use std::sync::Arc; use io_arc::IoArc; use log::{debug, warn}; use mio::net::{TcpStream, UdpSocket}; use crate::prelude::{Actor, ActorContext, ActorFactory, ActorMessage, ActorResult, Handler}; +#[derive(Clone)] pub struct NetWorker { streams: HashMap, SocketAddr)>, sockets: HashMap>, @@ -29,6 +29,7 @@ impl Actor for NetWorker { } } +#[derive(Clone)] pub struct NetWorkerFactory {} impl ActorFactory for NetWorkerFactory { fn new_actor(&mut self, _context: ActorContext) -> Result> { @@ -195,7 +196,7 @@ impl Handler for NetWorker { } impl Handler for NetWorker { - fn handle(&mut self, msg: ReceiveUdpMessage, context: &ActorContext) -> Result> { + fn handle(&mut self, msg: ReceiveUdpMessage, _context: &ActorContext) -> Result> { let socket = self.sockets.get_mut(&msg.socket_id); if socket.is_none() { // temporary implementation for our instant http response, later on we won't have to care here if the stream is active, we'll just forward the message @@ -210,11 +211,10 @@ impl Handler for NetWorker { } impl Handler for NetWorker { - fn handle(&mut self, msg: AddUdpSocket, context: &ActorContext) -> Result> { + fn handle(&mut self, msg: AddUdpSocket, _context: &ActorContext) -> Result> { let key_already_exists = self.sockets.remove(&msg.socket_id); if key_already_exists.is_some() { warn!("Socket ID already exists, dropping old one in favor of the new."); - let socket = key_already_exists.unwrap(); } let _ = self.sockets.insert(msg.socket_id, msg.socket); From 0dc2a587e1cdafb67600067e1a13e66d173dc19d Mon Sep 17 00:00:00 2001 From: Bobonium Date: Wed, 26 Apr 2023 20:46:20 +0200 Subject: [PATCH 09/44] add spawn_multiple() to builder --- src/actor/actor_builder.rs | 26 ++++++++++++++++++++++++++ src/net/net_manager.rs | 9 +++------ 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/src/actor/actor_builder.rs b/src/actor/actor_builder.rs index e1120c9..b7eb6b9 100644 --- a/src/actor/actor_builder.rs +++ b/src/actor/actor_builder.rs @@ -246,4 +246,30 @@ where } } } + + pub fn spawn_multiple

(&self, name: impl Into + Clone + std::fmt::Display, props: P, spawn_count: usize) -> Result>, ActorError> + where + P: ActorFactory + 'static + Clone, + { + let mut to_return = Vec::new(); + for i in 0..spawn_count { + let name = format!("{}-{}", name.clone(), i); + let res = self.spawn(name, props.clone()); + match res { + Ok(res) => { + to_return.push(res); + } + Err(e) => { + for actor in &to_return { + let _ = actor.stop(); + } + return Err(e); + } + } + } + + return Ok(to_return); + } + + } diff --git a/src/net/net_manager.rs b/src/net/net_manager.rs index 3c5a78f..d0997bd 100644 --- a/src/net/net_manager.rs +++ b/src/net/net_manager.rs @@ -41,14 +41,11 @@ impl NetManager let pool_name = &context.actor_ref.get_address().pool; - let worker_count = context.system.get_available_actor_count_for_pool(pool_name).unwrap(); - let mut workers = Vec::new(); let router = context.system.builder().set_pool_name(pool_name).spawn("net-least-message", ShardedRouterFactory::new( false, false)).unwrap(); - - for i in 0..worker_count - 1 { - let worker = context.system.builder().set_pool_name(pool_name).spawn(format!("net-worker-{}", i), worker_factory.clone()).unwrap(); + let worker_count = context.system.get_available_actor_count_for_pool(pool_name).unwrap(); + let workers = context.system.builder().set_pool_name(pool_name).spawn_multiple("net-worker", worker_factory.clone(), worker_count).unwrap(); + for worker in &workers { router.send(AddActorMessage::new(worker.clone())).unwrap(); - workers.push(worker); } let is_stopping = Arc::new(AtomicBool::new(false)); From 56955342d513f6061a40258bf028ebaa3fc6622c Mon Sep 17 00:00:00 2001 From: Bobonium Date: Wed, 26 Apr 2023 20:52:27 +0200 Subject: [PATCH 10/44] add test and documentation for spawn_multiple --- src/actor/actor_builder.rs | 66 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/src/actor/actor_builder.rs b/src/actor/actor_builder.rs index b7eb6b9..e34eedf 100644 --- a/src/actor/actor_builder.rs +++ b/src/actor/actor_builder.rs @@ -247,6 +247,72 @@ where } } + /// Creates N defined [Actor]s on the [ActorSystem] + /// + /// Requires [ActorFactory] to implement `Clone` + /// + /// # Returns + /// + /// `Ok(Vec>)` if actors were created successfully + /// + /// `Ok(Vec>)` if the actors are already running on the system + /// + /// `Err(ActorError)` see [ActorError](../prelude/enum.ActorError.html) for detailed information. Will also stop any actors that might already have been started + /// + /// + /// # Examples + /// + /// ```rust + /// use tyra::prelude::*; + /// use std::error::Error; + /// use std::time::Duration; + /// + /// struct TestActor {} + /// impl TestActor { + /// pub fn new() -> Self { + /// Self {} + /// } + /// } + /// impl Actor for TestActor {} + /// + /// #[derive(Clone)] + /// struct TestActorFactory {} + /// impl TestActorFactory { + /// pub fn new() -> Self { + /// Self {} + /// } + /// } + /// impl ActorFactory for TestActorFactory { + /// fn new_actor(&mut self, _context: ActorContext) -> Result> { + /// Ok(TestActor::new()) + /// } + /// } + /// + /// #[ntest::timeout(100000)] + /// fn main() { + /// let mut actor_config = TyraConfig::new().unwrap(); + /// actor_config.thread_pool.config.insert(String::from("default"), ThreadPoolConfig::new(2, 1, 1, 1.0)); + /// let actor_system = ActorSystem::new(actor_config); + /// + /// let actor_name = "test"; + /// //this works, because there's no actor called `test` yet on the pool + /// let this_works = actor_system.builder().spawn_multiple(actor_name, TestActorFactory::new(), 2); + /// assert!(this_works.is_ok(), "The actors could not be spawned"); + /// + /// //this works, because there's already an actor called `test` with type `TestActor` on the pool, therefore the result is the same actor that was created in the previous spawn command + /// let this_works_as_well = actor_system.builder().spawn_multiple(actor_name, TestActorFactory::new(), 2); + /// assert!(this_works_as_well.is_ok(), "The `ActorWrapper` could not be fetched"); + /// + /// //this does not work, because the pool is currently configured to only allow two actors + /// let pool_full = actor_system.builder().spawn_multiple("full", TestActorFactory::new(), 10); + /// assert!(pool_full.is_err(), "The actor could not be spawned"); + /// let err = pool_full.err().unwrap(); + /// assert_eq!(err, ActorError::ThreadPoolHasTooManyActorsError, "Error is not correct"); + /// + /// actor_system.stop(Duration::from_millis(3000)); + /// std::process::exit(actor_system.await_shutdown()); + /// } + /// ``` pub fn spawn_multiple

(&self, name: impl Into + Clone + std::fmt::Display, props: P, spawn_count: usize) -> Result>, ActorError> where P: ActorFactory + 'static + Clone, From 458c670181587d33c3190a5acd54371835ab1d0d Mon Sep 17 00:00:00 2001 From: Bobonium Date: Wed, 26 Apr 2023 21:29:29 +0200 Subject: [PATCH 11/44] add SendToAllTargetsMessage to all router implementations --- CHANGELOG.md | 4 +- src/routers/least_message_router.rs | 45 ++++++++++++++++++++++ src/routers/mod.rs | 3 ++ src/routers/round_robin_router.rs | 44 +++++++++++++++++++++ src/routers/send_to_all_targets_message.rs | 20 ++++++++++ src/routers/sharded_router.rs | 31 +++++++++++++++ 6 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 src/routers/send_to_all_targets_message.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 41dfb02..cb8dcd9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,9 @@ - `stop_on_empty_targets` => automatically stops the router if there are no more targets to receive a message to be routed. This does not apply to manually removed targets - Routers now automatically remove stopped actors from their target pool if they have been stopped - Added `.is_mailbox_stopped()`, `is_stopped()` and `wait_for_stop()` to `ActorWrapper` - - Added `general.signal_graceful_timeout_in_seconds` to config + - Added `general.signal_graceful_timeout_in_seconds` to config + - Added `ActorBuilder.spawn_multiple()` + - All routers now support a `SendToAllTargetsMessage` that will forward `M` to all active targets # 1.0.0 diff --git a/src/routers/least_message_router.rs b/src/routers/least_message_router.rs index 7ff1341..28f0474 100644 --- a/src/routers/least_message_router.rs +++ b/src/routers/least_message_router.rs @@ -7,6 +7,8 @@ use crate::routers::add_actor_message::AddActorMessage; use crate::routers::remove_actor_message::RemoveActorMessage; use log::{debug, error}; use std::error::Error; +use crate::message::actor_message::BaseActorMessage; +use crate::router::SendToAllTargetsMessage; pub struct LeastMessageRouter where @@ -244,3 +246,46 @@ where return Ok(ActorResult::Ok); } } + +impl Handler> for LeastMessageRouter + where + A: Actor + Handler + 'static, + M: BaseActorMessage + Clone + 'static, +{ + fn handle( + &mut self, + msg: SendToAllTargetsMessage, + _context: &ActorContext, + ) -> Result> { + if !self.can_route { + return Ok(ActorResult::Ok); + } + + // skip/remove stopped actors + loop { + let route_index = self.next_route_index; + let target = self.route_to.get(self.next_route_index).unwrap(); + + if target.is_stopped() { + self.next_route_index += 1; + if self.next_route_index >= (self.route_to.len() - 1) { + self.next_route_index = 0; + } + self.route_to.remove(route_index); + if self.route_to.len() == 0 && self.stop_on_empty_targets { + debug!("Stopping router, because all targets have been removed"); + return Ok(ActorResult::Stop) + } + } + else { + break; + } + } + + for target in &self.route_to { + let _ = target.send(msg.msg.clone()); + } + + return Ok(ActorResult::Ok); + } +} \ No newline at end of file diff --git a/src/routers/mod.rs b/src/routers/mod.rs index da7cde2..983bff0 100644 --- a/src/routers/mod.rs +++ b/src/routers/mod.rs @@ -4,9 +4,11 @@ mod remove_actor_message; mod round_robin_router; mod sharded_router; mod least_message_router; +mod send_to_all_targets_message; pub mod prelude { pub use crate::routers::add_actor_message::AddActorMessage; + pub use crate::routers::send_to_all_targets_message::SendToAllTargetsMessage; pub use crate::routers::bulk_router_message::BulkRouterMessage; pub use crate::routers::remove_actor_message::RemoveActorMessage; pub use crate::routers::round_robin_router::RoundRobinRouter; @@ -15,4 +17,5 @@ pub mod prelude { pub use crate::routers::sharded_router::ShardedRouterFactory; pub use crate::routers::least_message_router::LeastMessageRouter; pub use crate::routers::least_message_router::LeastMessageRouterFactory; + } diff --git a/src/routers/round_robin_router.rs b/src/routers/round_robin_router.rs index 20a590c..6ecb21e 100644 --- a/src/routers/round_robin_router.rs +++ b/src/routers/round_robin_router.rs @@ -9,6 +9,7 @@ use crate::routers::bulk_router_message::BulkRouterMessage; use crate::routers::remove_actor_message::RemoveActorMessage; use log::{debug, error}; use std::error::Error; +use crate::router::SendToAllTargetsMessage; pub struct RoundRobinRouter where @@ -291,3 +292,46 @@ where return Ok(ActorResult::Ok); } } + +impl Handler> for RoundRobinRouter + where + A: Actor + Handler + 'static, + M: BaseActorMessage + Clone + 'static, +{ + fn handle( + &mut self, + msg: SendToAllTargetsMessage, + _context: &ActorContext, + ) -> Result> { + if !self.can_route { + return Ok(ActorResult::Ok); + } + + // skip/remove stopped actors + loop { + let route_index = self.route_index; + let target = self.route_to.get(self.route_index).unwrap(); + + if target.is_stopped() { + self.route_index += 1; + if self.route_index >= (self.route_to.len() - 1) { + self.route_index = 0; + } + self.route_to.remove(route_index); + if self.route_to.len() == 0 && self.stop_on_empty_targets { + debug!("Stopping router, because all targets have been removed"); + return Ok(ActorResult::Stop) + } + } + else { + break; + } + } + + for target in &self.route_to { + let _ = target.send(msg.msg.clone()); + } + + return Ok(ActorResult::Ok); + } +} \ No newline at end of file diff --git a/src/routers/send_to_all_targets_message.rs b/src/routers/send_to_all_targets_message.rs new file mode 100644 index 0000000..b1d5f49 --- /dev/null +++ b/src/routers/send_to_all_targets_message.rs @@ -0,0 +1,20 @@ +use crate::message::actor_message::BaseActorMessage; + +/// Wraps multiple [ActorMessage](../prelude/trait.ActorMessage.html) to be sent to a Router +pub struct SendToAllTargetsMessage + where + M: BaseActorMessage + 'static, +{ + pub msg: M, +} + +impl BaseActorMessage for SendToAllTargetsMessage where M: BaseActorMessage + 'static {} + +impl SendToAllTargetsMessage + where + M: BaseActorMessage + 'static, +{ + pub fn new(msg: M) -> Self { + Self { msg } + } +} diff --git a/src/routers/sharded_router.rs b/src/routers/sharded_router.rs index 36c1645..4c2bbfe 100644 --- a/src/routers/sharded_router.rs +++ b/src/routers/sharded_router.rs @@ -10,6 +10,7 @@ use crate::routers::remove_actor_message::RemoveActorMessage; use log::{debug, error}; use std::collections::HashMap; use std::error::Error; +use crate::router::SendToAllTargetsMessage; pub struct ShardedRouter where @@ -275,3 +276,33 @@ where return Ok(ActorResult::Ok); } } + +impl Handler> for ShardedRouter + where + A: Actor + Handler + 'static, + M: BaseActorMessage + Clone + 'static, +{ + fn handle( + &mut self, + msg: SendToAllTargetsMessage, + _context: &ActorContext, + ) -> Result> { + if !self.can_route { + return Ok(ActorResult::Ok); + } + + for i in 0..self.route_to.len() { + let target = self.route_to.get(i).unwrap(); + if target.is_stopped() { + self.route_to.remove(i); + self.recalculate_shards(); + } + } + + for target in &self.route_to { + let _ = target.send(msg.msg.clone()); + } + + return Ok(ActorResult::Ok); + } +} \ No newline at end of file From 2a6d741cab645bf6879276036d7653c91f610871 Mon Sep 17 00:00:00 2001 From: Bobonium Date: Fri, 28 Apr 2023 20:07:16 +0200 Subject: [PATCH 12/44] properly shutdown udp sockets if process is killed --- src/net/net_manager.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/net/net_manager.rs b/src/net/net_manager.rs index d0997bd..eb8a312 100644 --- a/src/net/net_manager.rs +++ b/src/net/net_manager.rs @@ -88,6 +88,11 @@ impl Actor for NetManager break; } NetProtocol::UDP => { + let sock = UdpSocket::bind("127.0.0.1:0".parse().unwrap()); + if sock.is_ok() { + let sock = sock.unwrap(); + let _ = sock.send_to(b"", address.parse().unwrap()); + } break; } } From 4c592f789d697c3e4c0bcee220053434a2bb5d6f Mon Sep 17 00:00:00 2001 From: Bobonium Date: Fri, 28 Apr 2023 20:22:03 +0200 Subject: [PATCH 13/44] improve net shutdown behavior --- examples/net.rs | 5 +++-- src/net/net_manager.rs | 34 +++++++++++++++++----------------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/examples/net.rs b/examples/net.rs index 56a9e77..a3eb776 100644 --- a/examples/net.rs +++ b/examples/net.rs @@ -1,3 +1,4 @@ +use std::time::Duration; use tyra::prelude::{ActorSystem, NetConfig, NetManagerFactory, NetProtocol, NetWorkerFactory, ThreadPoolConfig, TyraConfig}; fn main() { @@ -17,10 +18,10 @@ fn main() { let _actor = actor_system .builder() .set_pool_name("mio") - .spawn("test", NetManagerFactory::new(net_configs, 10, worker_factory)) + .spawn("test", NetManagerFactory::new(net_configs, Duration::from_secs(10), Duration::from_secs(3), worker_factory)) .unwrap(); - // send a message to the actor + std::process::exit(actor_system.await_shutdown()); } diff --git a/src/net/net_manager.rs b/src/net/net_manager.rs index eb8a312..31810b8 100644 --- a/src/net/net_manager.rs +++ b/src/net/net_manager.rs @@ -7,7 +7,7 @@ use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; use std::thread; use std::thread::sleep; -use std::time::Duration; +use std::time::{Duration, Instant}; use io_arc::IoArc; use log::{error, warn}; use mio::net::{TcpListener, TcpStream, UdpSocket}; @@ -21,7 +21,8 @@ pub struct NetManager where T: Handler + Handler + Handler + Handler + Handler + 'static, { - graceful_shutdown_time_in_seconds: usize, + graceful_shutdown_time_in_seconds: Duration, + on_stop_udp_timeout: Duration, router: ActorWrapper>, workers: Vec>, net_configs: Vec, @@ -36,7 +37,7 @@ impl NetManager T: Handler + Handler + Handler + Handler + Handler + 'static, { - pub fn new(context: ActorContext, net_configs: Vec, graceful_shutdown_time_in_seconds: usize, worker_factory: F) -> Self + pub fn new(context: ActorContext, net_configs: Vec, graceful_shutdown_time_in_seconds: Duration, on_stop_udp_timeout: Duration, worker_factory: F) -> Self where F: ActorFactory + Clone + 'static, { let pool_name = &context.actor_ref.get_address().pool; @@ -53,6 +54,7 @@ impl NetManager return Self { graceful_shutdown_time_in_seconds, + on_stop_udp_timeout, router, workers, net_configs, @@ -66,16 +68,11 @@ impl Actor for NetManager T: Handler + Handler + Handler + Handler + Handler + 'static, { fn pre_stop(&mut self, _context: &ActorContext) { - if self.graceful_shutdown_time_in_seconds == 0 { - self.is_stopping.store(true, Ordering::Relaxed); - return; - } - let graceful_stop_in_millis = self.graceful_shutdown_time_in_seconds * 1000; let iterations = 10; - let iterate_graceful_stop_in_millis = graceful_stop_in_millis / iterations; + let iterate_graceful_stop = self.graceful_shutdown_time_in_seconds / iterations; - sleep(Duration::from_millis(iterate_graceful_stop_in_millis as u64)); + sleep(iterate_graceful_stop); self.is_stopping.store(true, Ordering::Relaxed); @@ -100,7 +97,7 @@ impl Actor for NetManager if self.is_stopped.load(Ordering::Relaxed) { return; } - sleep(Duration::from_millis(iterate_graceful_stop_in_millis as u64)); + sleep(iterate_graceful_stop); } } @@ -135,7 +132,8 @@ where T: Handler + Handler + Handler + Handler + Handler + 'static, { net_configs: Vec, - graceful_shutdown_time_in_seconds: usize, + graceful_shutdown_time_in_seconds: Duration, + on_stop_udp_timeout: Duration, worker_factory: F, phantom: PhantomData, } @@ -146,10 +144,11 @@ where T: Handler + Handler + Handler + Handler + Handler + 'static, { - pub fn new(net_configs: Vec, graceful_shutdown_time_in_seconds: usize, worker_factory: F) -> Self { + pub fn new(net_configs: Vec, graceful_shutdown_time_in_seconds: Duration, on_stop_udp_timeout: Duration, worker_factory: F) -> Self { return Self { net_configs, graceful_shutdown_time_in_seconds, + on_stop_udp_timeout, worker_factory, phantom: PhantomData, }; @@ -162,7 +161,7 @@ where { fn new_actor(&mut self, context: ActorContext>) -> Result, Box> { context.actor_ref.send(ActorInitMessage::new()).unwrap(); - return Ok(NetManager::new(context, self.net_configs.clone(), self.graceful_shutdown_time_in_seconds, self.worker_factory.clone())); + return Ok(NetManager::new(context, self.net_configs.clone(), self.graceful_shutdown_time_in_seconds, self.on_stop_udp_timeout, self.worker_factory.clone())); } } @@ -175,7 +174,8 @@ impl Handler for NetManager let is_stopping = self.is_stopping.clone(); let is_stopped = self.is_stopped.clone(); let mut net_configs = self.net_configs.clone(); - + let mut last_udp_message_received = Instant::now(); + let on_stop_udp_timeout = self.on_stop_udp_timeout.clone(); thread::spawn(move || { let mut tcp_listeners :HashMap = HashMap::new(); let mut udp_sockets:HashMap> = HashMap::new(); @@ -222,7 +222,7 @@ impl Handler for NetManager for event in events.iter() { let stopping = is_stopping.load(Ordering::Relaxed); - if stopping && streams.len() == 0 { + if stopping && streams.len() == 0 && last_udp_message_received.elapsed() > on_stop_udp_timeout { is_stopped.store(true, Ordering::Relaxed); break; } @@ -280,7 +280,7 @@ impl Handler for NetManager }; let request = String::from_utf8_lossy(&buf[..len]); let _ = router.send(ReceiveUdpMessage::new(token.0, from, request.into_owned())); - + last_udp_message_received = Instant::now(); } else { if event.is_read_closed() || event.is_write_closed() { From 9603fc9bb08c34d8a6779491d6f940096df92e7c Mon Sep 17 00:00:00 2001 From: Bobonium Date: Fri, 28 Apr 2023 20:26:49 +0200 Subject: [PATCH 14/44] net: allow configuration of maximum amount of worker actors --- examples/net.rs | 2 +- src/net/net_manager.rs | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/examples/net.rs b/examples/net.rs index a3eb776..2a5a403 100644 --- a/examples/net.rs +++ b/examples/net.rs @@ -18,7 +18,7 @@ fn main() { let _actor = actor_system .builder() .set_pool_name("mio") - .spawn("test", NetManagerFactory::new(net_configs, Duration::from_secs(10), Duration::from_secs(3), worker_factory)) + .spawn("test", NetManagerFactory::new(net_configs, Duration::from_secs(10), Duration::from_secs(3), worker_factory, 3)) .unwrap(); diff --git a/src/net/net_manager.rs b/src/net/net_manager.rs index 31810b8..232a5de 100644 --- a/src/net/net_manager.rs +++ b/src/net/net_manager.rs @@ -1,3 +1,4 @@ +use std::cmp::min; use std::collections::HashMap; use std::error::Error; use std::io::{BufRead, BufReader}; @@ -37,13 +38,14 @@ impl NetManager T: Handler + Handler + Handler + Handler + Handler + 'static, { - pub fn new(context: ActorContext, net_configs: Vec, graceful_shutdown_time_in_seconds: Duration, on_stop_udp_timeout: Duration, worker_factory: F) -> Self + pub fn new(context: ActorContext, net_configs: Vec, graceful_shutdown_time_in_seconds: Duration, on_stop_udp_timeout: Duration, worker_factory: F, max_worker_count: usize) -> Self where F: ActorFactory + Clone + 'static, { let pool_name = &context.actor_ref.get_address().pool; let router = context.system.builder().set_pool_name(pool_name).spawn("net-least-message", ShardedRouterFactory::new( false, false)).unwrap(); - let worker_count = context.system.get_available_actor_count_for_pool(pool_name).unwrap(); + let max_pool_count = context.system.get_available_actor_count_for_pool(pool_name).unwrap(); + let worker_count = min(max_worker_count, max_pool_count); let workers = context.system.builder().set_pool_name(pool_name).spawn_multiple("net-worker", worker_factory.clone(), worker_count).unwrap(); for worker in &workers { router.send(AddActorMessage::new(worker.clone())).unwrap(); @@ -135,6 +137,7 @@ where graceful_shutdown_time_in_seconds: Duration, on_stop_udp_timeout: Duration, worker_factory: F, + max_worker_count: usize, phantom: PhantomData, } @@ -144,12 +147,13 @@ where T: Handler + Handler + Handler + Handler + Handler + 'static, { - pub fn new(net_configs: Vec, graceful_shutdown_time_in_seconds: Duration, on_stop_udp_timeout: Duration, worker_factory: F) -> Self { + pub fn new(net_configs: Vec, graceful_shutdown_time_in_seconds: Duration, on_stop_udp_timeout: Duration, worker_factory: F, max_worker_count: usize) -> Self { return Self { net_configs, graceful_shutdown_time_in_seconds, on_stop_udp_timeout, worker_factory, + max_worker_count, phantom: PhantomData, }; } @@ -161,7 +165,7 @@ where { fn new_actor(&mut self, context: ActorContext>) -> Result, Box> { context.actor_ref.send(ActorInitMessage::new()).unwrap(); - return Ok(NetManager::new(context, self.net_configs.clone(), self.graceful_shutdown_time_in_seconds, self.on_stop_udp_timeout, self.worker_factory.clone())); + return Ok(NetManager::new(context, self.net_configs.clone(), self.graceful_shutdown_time_in_seconds, self.on_stop_udp_timeout, self.worker_factory.clone(), self.max_worker_count)); } } From 7a875adc09b4647314dcee8e99872a13462348c9 Mon Sep 17 00:00:00 2001 From: Bobonium Date: Fri, 28 Apr 2023 21:33:23 +0200 Subject: [PATCH 15/44] update sharding_router to make use of HashRing --- Cargo.toml | 1 + src/routers/sharded_router.rs | 68 ++++++++++++++++++++--------------- 2 files changed, 40 insertions(+), 29 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index eff4352..fc53c00 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,7 @@ ctrlc = { version = "3.2.5", features = ["termination"] } mio = {version = "0.8.6", features = ["os-poll", "os-ext", "net"]} quiche = "0.17.1" io-arc = "1.0.0" +hashring = "0.3.0" [dev-dependencies] bincode = "1.3.3" diff --git a/src/routers/sharded_router.rs b/src/routers/sharded_router.rs index 4c2bbfe..c2ee60e 100644 --- a/src/routers/sharded_router.rs +++ b/src/routers/sharded_router.rs @@ -7,9 +7,10 @@ use crate::prelude::{Actor, ActorMessage, ActorResult, BulkActorMessage}; use crate::routers::add_actor_message::AddActorMessage; use crate::routers::bulk_router_message::BulkRouterMessage; use crate::routers::remove_actor_message::RemoveActorMessage; -use log::{debug, error}; +use log::{debug, error, info, warn}; use std::collections::HashMap; use std::error::Error; +use hashring::HashRing; use crate::router::SendToAllTargetsMessage; pub struct ShardedRouter @@ -18,6 +19,7 @@ where { num_shards: usize, route_to: Vec>, + hash_ring: HashRing, sharding: HashMap>, can_route: bool, stop_on_system_stop: bool, @@ -121,22 +123,13 @@ where Self { num_shards: 0, route_to: Vec::new(), + hash_ring: HashRing::new(), sharding: HashMap::new(), can_route: false, stop_on_system_stop, stop_on_empty_targets, } } - - fn recalculate_shards(&mut self) { - let num_routees = self.route_to.len(); - self.num_shards = self.route_to.len() * 5; - self.sharding.clear(); - for i in 0..self.num_shards { - let routee = self.route_to.get(i % num_routees).unwrap().clone(); - self.sharding.insert(i, routee); - } - } } impl Actor for ShardedRouter where A: Actor { @@ -165,9 +158,9 @@ where msg: AddActorMessage, _context: &ActorContext, ) -> Result> { - self.route_to.push(msg.actor); + self.hash_ring.add(self.route_to.len()); + self.route_to.push(msg.actor.clone()); self.can_route = true; - self.recalculate_shards(); return Ok(ActorResult::Ok); } } @@ -186,8 +179,8 @@ where .iter() .position(|x| x.get_address() == msg.actor.get_address()) { + let _ = self.hash_ring.remove(&pos); self.route_to.remove(pos); - self.recalculate_shards(); } if self.route_to.len() == 0 { self.can_route = false @@ -210,27 +203,46 @@ where return Ok(ActorResult::Ok); } - let mut shard_id = msg.get_id() % self.num_shards; - let mut forward_to = self.sharding.get(&shard_id).unwrap(); + let hash = msg.get_id(); + let target; loop { - if !forward_to.is_stopped() { + let target_id = self.hash_ring.get(&hash); + if target_id.is_none() { + warn!("Can't find target for hash."); + return Ok(ActorResult::Ok); + } + let target_id = target_id.unwrap(); + + let potential_target = self.route_to.get(target_id.clone()); + if potential_target.is_none() { + warn!("Target does not exist."); + return Ok(ActorResult::Ok); + } + let potential_target = potential_target.unwrap(); + if !potential_target.is_stopped() { + target = potential_target; break; } - self.route_to.remove(shard_id); - if self.route_to.len() == 0 && self.stop_on_empty_targets { - debug!("Stopping router, because all targets have been removed"); - return Ok(ActorResult::Stop) - } - self.recalculate_shards(); - shard_id = msg.get_id() % self.num_shards; - forward_to = self.sharding.get(&shard_id).unwrap(); + + let target_id = target_id.clone(); + self.route_to.remove(target_id.clone()); + self.hash_ring.remove(&target_id); + if self.route_to.len() == 0 { + if self.stop_on_empty_targets { + debug!("Stopping router, because all targets have been stopped"); + return Ok(ActorResult::Stop) + } + self.can_route = false; + info!("Router has no valid targets to route to. Dropping message."); + return Ok(ActorResult::Ok); + } } - let result = forward_to.send(msg); + let result = target.send(msg); if result.is_err() { error!( "Could not forward message to target {}", - forward_to.get_address().actor + target.get_address().actor ); } return Ok(ActorResult::Ok); @@ -255,7 +267,6 @@ where let target = self.route_to.get(i).unwrap(); if target.is_stopped() { self.route_to.remove(i); - self.recalculate_shards(); } } @@ -295,7 +306,6 @@ impl Handler> for ShardedRouter let target = self.route_to.get(i).unwrap(); if target.is_stopped() { self.route_to.remove(i); - self.recalculate_shards(); } } From 942b7bf03ddbece481dc40c7978e72ef74832d0c Mon Sep 17 00:00:00 2001 From: Bobonium Date: Fri, 28 Apr 2023 22:15:08 +0200 Subject: [PATCH 16/44] rework sharded_router and implement Hash requirement to ActorMessage --- CHANGELOG.md | 4 + src/lib.rs | 2 - src/message/actor_init_message.rs | 1 + src/message/actor_message.rs | 22 +-- src/message/actor_stop_message.rs | 1 + src/message/bulk_actor_message.rs | 1 + src/message/delayed_message.rs | 11 ++ src/message/serialized_message.rs | 1 + src/message/sleep_message.rs | 1 + src/message/system_stop_message.rs | 1 + src/net/mod.rs | 1 + src/net/net_manager.rs | 2 +- src/net/net_messages.rs | 122 ++++++++++++++++ src/net/net_worker.rs | 104 +------------ src/network/mod.rs | 7 - src/network/network_manager.rs | 161 --------------------- src/network/tcp_remote_actor.rs | 59 -------- src/routers/add_actor_message.rs | 7 + src/routers/bulk_router_message.rs | 10 ++ src/routers/remove_actor_message.rs | 7 + src/routers/send_to_all_targets_message.rs | 10 ++ src/routers/sharded_router.rs | 2 +- 22 files changed, 194 insertions(+), 343 deletions(-) create mode 100644 src/net/net_messages.rs delete mode 100644 src/network/mod.rs delete mode 100644 src/network/network_manager.rs delete mode 100644 src/network/tcp_remote_actor.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index cb8dcd9..4e86e61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ - `stop_on_system_stop` => if false, the user needs to manually stop the router for a clean and quick shutdown of the system - `stop_on_empty_targets` => automatically stops the router if there are no more targets to receive a message to be routed. This does not apply to manually removed targets - Routers now automatically remove stopped actors from their target pool if they have been stopped + - `Sharded_Router` now makes use of `HashRing` + - `ActorMessage` now needs to implement `Hash` and no longer need to explicitly implement `ActorMessage.get_id()` to be able to properly make use of the `ShardedRouter` + - renamed `ActorMessage.get_id()` to `ActorMessage.get_hash()` and changed return type to u64 + - changed default implementation to `ActorMessage.get_hash()` to make use of `Hash` implementation - Added `.is_mailbox_stopped()`, `is_stopped()` and `wait_for_stop()` to `ActorWrapper` - Added `general.signal_graceful_timeout_in_seconds` to config - Added `ActorBuilder.spawn_multiple()` diff --git a/src/lib.rs b/src/lib.rs index 42f18e9..579dde4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -110,7 +110,6 @@ mod config; mod message; mod routers; mod system; -mod network; mod net; /// core components @@ -119,7 +118,6 @@ pub mod prelude { pub use crate::config::prelude::*; pub use crate::message::prelude::*; pub use crate::system::prelude::*; - pub use crate::network::prelude::*; pub use crate::net::prelude::*; } diff --git a/src/message/actor_init_message.rs b/src/message/actor_init_message.rs index 161cf45..0dcfefa 100644 --- a/src/message/actor_init_message.rs +++ b/src/message/actor_init_message.rs @@ -1,6 +1,7 @@ use crate::prelude::ActorMessage; /// Can be implemented by an Actor through Handler to be used to init an Actor +#[derive(Hash)] pub struct ActorInitMessage {} impl ActorInitMessage { diff --git a/src/message/actor_message.rs b/src/message/actor_message.rs index 31b4938..82e569d 100644 --- a/src/message/actor_message.rs +++ b/src/message/actor_message.rs @@ -1,18 +1,17 @@ +use std::collections::hash_map::DefaultHasher; +use std::hash::{Hash, Hasher}; + /// This trait is used internally by the `ActorSystem` and builds the base for all messaging /// It's automatically implemented by the `ActorMessage` trait that should be used /// /// It is used by Messages defined in the system /// All messages that use this trait directly should also implement a dynamic `Handler` that applies to any `Actor` -pub trait BaseActorMessage: Send + Sync { +pub trait BaseActorMessage: Send + Sync + Hash { } /// This trait is used by Messages defined by the system /// All messages that use this trait should also implement a dynamic `Handler` that applies to any `Actor` -pub trait DefaultActorMessage: Send + Sync { - /// returns the message id - fn get_id(&self) -> usize { - return 0; - } +pub trait DefaultActorMessage: Send + Sync + Hash { } impl BaseActorMessage for A @@ -29,13 +28,16 @@ where /// ```rust /// use tyra::prelude::ActorMessage; /// +/// #[derive(Hash)] /// struct FooBar {} /// impl ActorMessage for FooBar {} /// ``` -pub trait ActorMessage: Send + Sync { - /// returns the message id - fn get_id(&self) -> usize { - return 0; +pub trait ActorMessage: Send + Sync + Hash { + /// returns the message hash + fn get_hash(&self) -> u64 { + let mut hasher = DefaultHasher::new(); + self.hash(&mut hasher); + return hasher.finish(); } } diff --git a/src/message/actor_stop_message.rs b/src/message/actor_stop_message.rs index dc687a9..d540efc 100644 --- a/src/message/actor_stop_message.rs +++ b/src/message/actor_stop_message.rs @@ -1,5 +1,6 @@ use crate::message::actor_message::{DefaultActorMessage}; +#[derive(Hash)] pub struct ActorStopMessage {} impl ActorStopMessage { diff --git a/src/message/bulk_actor_message.rs b/src/message/bulk_actor_message.rs index 115b6fe..4f0a133 100644 --- a/src/message/bulk_actor_message.rs +++ b/src/message/bulk_actor_message.rs @@ -3,6 +3,7 @@ use crate::message::actor_message::BaseActorMessage; /// Wraps multiple [ActorMessage](../prelude/trait.ActorMessage.html) to be sent to an Actor +#[derive(Hash)] pub struct BulkActorMessage where M: BaseActorMessage + 'static, diff --git a/src/message/delayed_message.rs b/src/message/delayed_message.rs index 77dfef2..f9e8017 100644 --- a/src/message/delayed_message.rs +++ b/src/message/delayed_message.rs @@ -1,3 +1,4 @@ +use std::hash::{Hash, Hasher}; use crate::message::actor_message::BaseActorMessage; use crate::prelude::{Actor, ActorMessage, ActorWrapper}; use std::time::{Duration, Instant}; @@ -36,3 +37,13 @@ where } } } + +impl Hash for DelayedMessage + where + M: BaseActorMessage + 'static, + A: Actor, +{ + fn hash(&self, state: &mut H) { + self.msg.hash(state); + } +} \ No newline at end of file diff --git a/src/message/serialized_message.rs b/src/message/serialized_message.rs index abc97dd..969862e 100644 --- a/src/message/serialized_message.rs +++ b/src/message/serialized_message.rs @@ -8,6 +8,7 @@ use crate::message::actor_message::DefaultActorMessage; /// and it may also include some additional fields to make deserialization easier for end users /// /// [ActorSystem.send_to_address](../prelude/struct.ActorSystem.html#method.send_to_address) uses this object to send serialized messages to Actors +#[derive(Hash)] pub struct SerializedMessage { pub content: Vec, } diff --git a/src/message/sleep_message.rs b/src/message/sleep_message.rs index 045da71..db3a457 100644 --- a/src/message/sleep_message.rs +++ b/src/message/sleep_message.rs @@ -2,6 +2,7 @@ use crate::message::actor_message::DefaultActorMessage; use std::time::Duration; /// Puts an actor to sleep for a specified time +#[derive(Hash)] pub struct SleepMessage { pub duration: Duration, } diff --git a/src/message/system_stop_message.rs b/src/message/system_stop_message.rs index bca3cfb..40e5696 100644 --- a/src/message/system_stop_message.rs +++ b/src/message/system_stop_message.rs @@ -1,5 +1,6 @@ use crate::message::actor_message::DefaultActorMessage; +#[derive(Hash)] pub struct SystemStopMessage {} impl SystemStopMessage { diff --git a/src/net/mod.rs b/src/net/mod.rs index f83ff79..bab66bb 100644 --- a/src/net/mod.rs +++ b/src/net/mod.rs @@ -1,6 +1,7 @@ mod net_manager; mod net_config; mod net_worker; +pub mod net_messages; pub mod prelude { pub use crate::net::net_manager::NetManagerFactory; diff --git a/src/net/net_manager.rs b/src/net/net_manager.rs index 232a5de..bbaaf2f 100644 --- a/src/net/net_manager.rs +++ b/src/net/net_manager.rs @@ -14,7 +14,7 @@ use log::{error, warn}; use mio::net::{TcpListener, TcpStream, UdpSocket}; use mio::{Events, Interest, Poll, Token}; use mio::event::Source; -use crate::net::net_worker::{AddTcpConnection, AddUdpSocket, ReceiveTcpMessage, ReceiveUdpMessage, RemoveTcpConnection}; +use crate::net::net_messages::{AddTcpConnection, AddUdpSocket, ReceiveTcpMessage, ReceiveUdpMessage, RemoveTcpConnection}; use crate::prelude::{Actor, ActorContext, ActorFactory, ActorInitMessage, ActorResult, ActorWrapper, Handler, NetConfig, NetProtocol}; use crate::router::{AddActorMessage, ShardedRouter, ShardedRouterFactory}; diff --git a/src/net/net_messages.rs b/src/net/net_messages.rs new file mode 100644 index 0000000..b123281 --- /dev/null +++ b/src/net/net_messages.rs @@ -0,0 +1,122 @@ +use std::hash::{Hash, Hasher}; +use std::net::SocketAddr; +use io_arc::IoArc; +use mio::net::{TcpStream, UdpSocket}; +use crate::prelude::ActorMessage; + +pub struct AddTcpConnection { + pub stream_id: usize, + pub stream: IoArc, + pub address: SocketAddr, +} + +impl Hash for AddTcpConnection +{ + fn hash(&self, state: &mut H) { + self.stream_id.hash(state); + } +} + +impl AddTcpConnection { + pub fn new(stream_id: usize, stream: IoArc, address: SocketAddr) -> Self { + return Self { + stream_id, + stream, + address, + }; + } +} + +pub struct RemoveTcpConnection { + pub stream_id: usize, +} + +impl Hash for RemoveTcpConnection +{ + fn hash(&self, state: &mut H) { + self.stream_id.hash(state); + } +} + +impl ActorMessage for AddTcpConnection {} + +impl RemoveTcpConnection { + pub fn new(stream_id: usize) -> Self { + return Self { + stream_id, + }; + } +} + +impl ActorMessage for RemoveTcpConnection {} + +pub struct ReceiveTcpMessage { + pub stream_id: usize, + pub request: Vec, +} + +impl Hash for ReceiveTcpMessage +{ + fn hash(&self, state: &mut H) { + self.stream_id.hash(state); + } +} + +impl ReceiveTcpMessage { + pub fn new(stream_id: usize, request: Vec) -> Self { + return Self { + stream_id, + request, + }; + } +} + +impl ActorMessage for ReceiveTcpMessage {} + +pub struct AddUdpSocket { + pub socket_id: usize, + pub socket: IoArc, +} + +impl Hash for AddUdpSocket +{ + fn hash(&self, state: &mut H) { + self.socket_id.hash(state); + } +} + +impl AddUdpSocket { + pub fn new(socket_id: usize, socket: IoArc) -> Self { + return Self { + socket_id, + socket, + }; + } +} + +impl ActorMessage for AddUdpSocket {} + +pub struct ReceiveUdpMessage { + pub socket_id: usize, + pub source: SocketAddr, + pub request: String, +} + +impl Hash for ReceiveUdpMessage +{ + fn hash(&self, state: &mut H) { + self.socket_id.hash(state); + } +} + +impl ReceiveUdpMessage { + pub fn new(socket_id: usize, source: SocketAddr, request: String) -> Self { + return Self { + socket_id, + source, + request, + }; + } +} + +impl ActorMessage for ReceiveUdpMessage {} \ No newline at end of file diff --git a/src/net/net_worker.rs b/src/net/net_worker.rs index e094a77..e1474cf 100644 --- a/src/net/net_worker.rs +++ b/src/net/net_worker.rs @@ -5,7 +5,8 @@ use std::net::{Shutdown, SocketAddr}; use io_arc::IoArc; use log::{debug, warn}; use mio::net::{TcpStream, UdpSocket}; -use crate::prelude::{Actor, ActorContext, ActorFactory, ActorMessage, ActorResult, Handler}; +use crate::net::net_messages::{AddTcpConnection, AddUdpSocket, ReceiveTcpMessage, ReceiveUdpMessage, RemoveTcpConnection}; +use crate::prelude::{Actor, ActorContext, ActorFactory, ActorResult, Handler}; #[derive(Clone)] pub struct NetWorker { @@ -45,107 +46,6 @@ impl NetWorkerFactory { } } -pub struct AddTcpConnection { - pub stream_id: usize, - pub stream: IoArc, - pub address: SocketAddr, -} - -impl AddTcpConnection { - pub fn new(stream_id: usize, stream: IoArc, address: SocketAddr) -> Self { - return Self { - stream_id, - stream, - address, - }; - } -} - -pub struct RemoveTcpConnection { - pub stream_id: usize, -} - -impl ActorMessage for AddTcpConnection { - fn get_id(&self) -> usize { - return self.stream_id; - } -} - -impl RemoveTcpConnection { - pub fn new(stream_id: usize) -> Self { - return Self { - stream_id, - }; - } -} - -impl ActorMessage for RemoveTcpConnection { - fn get_id(&self) -> usize { - return self.stream_id; - } -} - -pub struct ReceiveTcpMessage { - pub stream_id: usize, - pub request: Vec, -} - -impl ReceiveTcpMessage { - pub fn new(stream_id: usize, request: Vec) -> Self { - return Self { - stream_id, - request, - }; - } -} - -impl ActorMessage for ReceiveTcpMessage { - fn get_id(&self) -> usize { - return self.stream_id; - } -} - -pub struct AddUdpSocket { - socket_id: usize, - socket: IoArc, -} -impl AddUdpSocket { - pub fn new(socket_id: usize, socket: IoArc) -> Self { - return Self { - socket_id, - socket, - }; - } -} - -impl ActorMessage for AddUdpSocket { - fn get_id(&self) -> usize { - return 1; - } -} - -pub struct ReceiveUdpMessage { - socket_id: usize, - source: SocketAddr, - request: String, -} - -impl ReceiveUdpMessage { - pub fn new(socket_id: usize, source: SocketAddr, request: String) -> Self { - return Self { - socket_id, - source, - request, - }; - } -} - -impl ActorMessage for ReceiveUdpMessage { - fn get_id(&self) -> usize { - return 1; - } -} - impl Handler for NetWorker { fn handle(&mut self, msg: ReceiveTcpMessage, _context: &ActorContext) -> Result> { let stream = self.streams.get_mut(&msg.stream_id); diff --git a/src/network/mod.rs b/src/network/mod.rs deleted file mode 100644 index 0c21b6b..0000000 --- a/src/network/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub mod tcp_remote_actor; -pub mod network_manager; - -pub mod prelude { - pub use crate::network::network_manager::NetworkManagerFactory; - pub use crate::network::network_manager::NetworkManager; -} diff --git a/src/network/network_manager.rs b/src/network/network_manager.rs deleted file mode 100644 index ccefc03..0000000 --- a/src/network/network_manager.rs +++ /dev/null @@ -1,161 +0,0 @@ -use std::error::Error; -use std::io::{BufRead, BufReader}; -use std::sync::Arc; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::thread; -use std::net::{TcpListener, TcpStream}; -use std::thread::sleep; -use std::time::Duration; -use threadpool::ThreadPool; -use crate::network::tcp_remote_actor::{NetworkMessage, TcpRemoteActor, TcpRemoteActorFactory}; -use crate::prelude::{Actor, ActorContext, ActorFactory, ActorInitMessage, ActorMessage, ActorResult, ActorWrapper, Handler, SerializedMessage}; -use crate::router::{AddActorMessage, LeastMessageRouter, LeastMessageRouterFactory}; - - -pub struct NetworkManager { - is_stopped: Arc, - is_stopping: Arc, - actors: Vec>, - router: ActorWrapper>, - remote_actor_count: usize, - graceful_shutdown_time_in_seconds: u64 -} - -struct NetworkManagerInitMessage { -} -impl ActorMessage for NetworkManagerInitMessage {} - -impl NetworkManager { - pub fn new(context: ActorContext, graceful_shutdown_time_in_seconds: u64) -> Self { - - context.actor_ref.send(ActorInitMessage::new()).unwrap(); - let router = context.system - .builder() - .set_pool_name(&context.actor_ref.get_address().pool) - .spawn(format!("{}-tcp-router", context.actor_ref.get_address().actor), LeastMessageRouterFactory::new(0, false, true)) - .unwrap(); - - let remote_actor_count = context.system.get_available_actor_count_for_pool(&context.actor_ref.get_address().pool).unwrap(); - let mut actors = Vec::new(); - for i in 0..remote_actor_count { - let actor = context.system - .builder() - .set_pool_name(&context.actor_ref.get_address().pool) - .spawn(format!("{}-tcp-{}", context.actor_ref.get_address().actor, i), TcpRemoteActorFactory::new()) - .unwrap(); - - actors.push(actor.clone()); - router.send(AddActorMessage::new(actor)).unwrap(); - } - - Self { - is_stopped: Arc::new(AtomicBool::new(false)), - is_stopping: Arc::new(AtomicBool::new(false)), - router, - actors, - remote_actor_count, - graceful_shutdown_time_in_seconds, - } - } -} -impl Actor for NetworkManager { - fn pre_stop(&mut self, _context: &ActorContext) { - println!("PRE STOP"); - sleep(Duration::from_secs(self.graceful_shutdown_time_in_seconds as u64)); - println!("manager -> is_stopping =true"); - self.is_stopping.store(true, Ordering::Relaxed); - - } - fn post_stop(&mut self, _context: &ActorContext) { - let _ = self.router.stop(); - for actor in &self.actors { - let _ = actor.stop(); - } - for actor in &self.actors { - actor.wait_for_stop(); - } - - println!("Actors have been stopped!"); - self.is_stopped.store(true, Ordering::Relaxed); - let _ = TcpStream::connect("127.0.0.1:2022"); - } -} - -// define a factory that creates the `Actor` for us -pub struct NetworkManagerFactory { - graceful_shutdown_time_in_seconds: u64 -} -impl NetworkManagerFactory { - pub fn new(graceful_shutdown_time_in_seconds: u64) -> Self { - Self { - graceful_shutdown_time_in_seconds - } - } -} -impl ActorFactory for NetworkManagerFactory { - fn new_actor( - &mut self, - context: ActorContext, - ) -> Result> { - Ok(NetworkManager::new(context, self.graceful_shutdown_time_in_seconds)) - } -} - -// implement our message for the `Actor` -impl Handler for NetworkManager { - fn handle( - &mut self, - _msg: ActorInitMessage, - _context: &ActorContext, - ) -> Result> { - - let is_stopped = self.is_stopped.clone(); - let is_stopping = self.is_stopping.clone(); - let remote_actor_count = self.remote_actor_count; - - let listener = TcpListener::bind("127.0.0.1:2022").unwrap(); - let router = self.router.clone(); - thread::spawn(move || { - let pool = ThreadPool::new(remote_actor_count); - for stream in listener.incoming() { - if is_stopped.load(Ordering::Relaxed) { - println!("NOW THE THREADS DIE!;"); - pool.join(); - println!("ALL DEAD!"); - return; - } - if is_stopping.load(Ordering::Relaxed) { - continue; - } - - match stream { - Ok(mut stream) => { - let router = router.clone(); - pool.execute(move || { - let buf_reader = BufReader::new(&mut stream); - //let http_req = String::new(); - //let http_request = buf_reader.read_line(&mut http_req); - let __http_request: Vec<_> = buf_reader - .lines() - .map(|result| result.unwrap()) - .take_while(|line| !line.is_empty()) - .collect(); - - router.send(NetworkMessage { - id: 1, - stream, - content: SerializedMessage::new(Vec::new()), - }).unwrap(); - }) - }, - Err(_e) => { - println!("SERS"); - }, - } - } - }); - - - Ok(ActorResult::Ok) - } -} \ No newline at end of file diff --git a/src/network/tcp_remote_actor.rs b/src/network/tcp_remote_actor.rs deleted file mode 100644 index 3c63960..0000000 --- a/src/network/tcp_remote_actor.rs +++ /dev/null @@ -1,59 +0,0 @@ -use std::error::Error; -use std::io::{Write}; -use std::net::TcpStream; -use crate::prelude::{Actor, ActorContext, ActorFactory, ActorMessage, ActorResult, Handler, SerializedMessage}; - -pub struct NetworkMessage { - pub content: SerializedMessage, - pub id: usize, - pub stream: TcpStream -} - -impl ActorMessage for NetworkMessage {} - -pub struct TcpRemoteActor {} - -impl TcpRemoteActor { - pub fn new() -> Self { - Self { - } - } -} -impl Actor for TcpRemoteActor { - fn pre_stop(&mut self, _context: &ActorContext) { - println!("STOPPING TCP REMOTE ACTOR"); - } - fn on_system_stop(&mut self, _context: &ActorContext) -> Result> { - //we intentionally ignore if the actor system is stopped - //we only react if the actor is explicitly stopped by the manager, because there might still be open connections that we don't want to drop - Ok(ActorResult::Ok) - } -} - -// define a factory that creates the `Actor` for us -pub struct TcpRemoteActorFactory {} -impl TcpRemoteActorFactory { - pub fn new() -> Self { - Self {} - } -} -impl ActorFactory for TcpRemoteActorFactory { - fn new_actor( - &mut self, - _context: ActorContext, - ) -> Result> { - Ok(TcpRemoteActor::new()) - } -} - -impl Handler for TcpRemoteActor { - fn handle(&mut self, mut msg: NetworkMessage, _context: &ActorContext) -> Result> { - println!("RECEIVED MSG!:"); - let response = "HTTP/1.1 200 OK\r\n\r\n

SERS

"; - - msg.stream.write_all(response.as_bytes()).unwrap(); - - Ok(ActorResult::Ok) - - } -} \ No newline at end of file diff --git a/src/routers/add_actor_message.rs b/src/routers/add_actor_message.rs index d42d93d..aa7e7cb 100644 --- a/src/routers/add_actor_message.rs +++ b/src/routers/add_actor_message.rs @@ -1,3 +1,4 @@ +use std::hash::{Hash, Hasher}; use crate::actor::actor_wrapper::ActorWrapper; use crate::message::actor_message::BaseActorMessage; use crate::prelude::Actor; @@ -20,3 +21,9 @@ where } impl BaseActorMessage for AddActorMessage where A: Actor {} + +impl Hash for AddActorMessage +where + A: Actor { + fn hash(&self, _state: &mut H) {} +} \ No newline at end of file diff --git a/src/routers/bulk_router_message.rs b/src/routers/bulk_router_message.rs index 6cea1f0..6676155 100644 --- a/src/routers/bulk_router_message.rs +++ b/src/routers/bulk_router_message.rs @@ -1,3 +1,4 @@ +use std::hash::{Hash, Hasher}; use crate::message::actor_message::BaseActorMessage; /// Wraps multiple [ActorMessage](../prelude/trait.ActorMessage.html) to be sent to a Router @@ -18,3 +19,12 @@ where Self { data } } } + +impl Hash for BulkRouterMessage +where + M: BaseActorMessage + 'static +{ + fn hash(&self, state: &mut H) { + self.data.hash(state); + } +} \ No newline at end of file diff --git a/src/routers/remove_actor_message.rs b/src/routers/remove_actor_message.rs index ba29c13..d08b717 100644 --- a/src/routers/remove_actor_message.rs +++ b/src/routers/remove_actor_message.rs @@ -1,3 +1,4 @@ +use std::hash::{Hash, Hasher}; use crate::actor::actor_wrapper::ActorWrapper; use crate::message::actor_message::BaseActorMessage; use crate::prelude::Actor; @@ -20,3 +21,9 @@ where } impl BaseActorMessage for RemoveActorMessage where A: Actor {} + +impl Hash for RemoveActorMessage + where + A: Actor { + fn hash(&self, _state: &mut H) {} +} \ No newline at end of file diff --git a/src/routers/send_to_all_targets_message.rs b/src/routers/send_to_all_targets_message.rs index b1d5f49..a8eb6b3 100644 --- a/src/routers/send_to_all_targets_message.rs +++ b/src/routers/send_to_all_targets_message.rs @@ -1,3 +1,4 @@ +use std::hash::{Hash, Hasher}; use crate::message::actor_message::BaseActorMessage; /// Wraps multiple [ActorMessage](../prelude/trait.ActorMessage.html) to be sent to a Router @@ -18,3 +19,12 @@ impl SendToAllTargetsMessage Self { msg } } } + +impl Hash for SendToAllTargetsMessage + where + M: BaseActorMessage + 'static +{ + fn hash(&self, state: &mut H) { + self.msg.hash(state); + } +} \ No newline at end of file diff --git a/src/routers/sharded_router.rs b/src/routers/sharded_router.rs index c2ee60e..b6ef7bd 100644 --- a/src/routers/sharded_router.rs +++ b/src/routers/sharded_router.rs @@ -203,7 +203,7 @@ where return Ok(ActorResult::Ok); } - let hash = msg.get_id(); + let hash = msg.get_hash(); let target; loop { let target_id = self.hash_ring.get(&hash); From bc8936b2308dcee9eb2d0c50b14d9b650d342b48 Mon Sep 17 00:00:00 2001 From: Bobonium Date: Sun, 30 Apr 2023 14:10:59 +0200 Subject: [PATCH 17/44] fix broken tests and examples --- examples/benchmark_bulk_router.rs | 3 +++ examples/benchmark_router_round_robin.rs | 3 +++ examples/benchmark_single_actor.rs | 1 + ...nchmark_single_actor_process_after_send.rs | 1 + ..._actor_process_after_send_single_thread.rs | 1 + .../benchmark_single_actor_single_thread.rs | 1 + examples/quickstart.rs | 2 ++ examples/serialize.rs | 2 +- examples/webserver.rs | 20 ------------------- src/actor/handler.rs | 1 + src/lib.rs | 1 + src/routers/least_message_router.rs | 1 + src/routers/round_robin_router.rs | 1 + src/routers/sharded_router.rs | 1 + src/system/actor_system.rs | 1 + 15 files changed, 19 insertions(+), 21 deletions(-) delete mode 100644 examples/webserver.rs diff --git a/examples/benchmark_bulk_router.rs b/examples/benchmark_bulk_router.rs index 61f0631..d906306 100644 --- a/examples/benchmark_bulk_router.rs +++ b/examples/benchmark_bulk_router.rs @@ -5,14 +5,17 @@ use std::time::{Duration, Instant}; use tyra::prelude::*; use tyra::router::{AddActorMessage, BulkRouterMessage, RoundRobinRouterFactory}; +#[derive(Hash)] struct MessageA {} impl ActorMessage for MessageA {} +#[derive(Hash)] struct Finish {} impl ActorMessage for Finish {} +#[derive(Hash)] struct Start {} impl ActorMessage for Start {} diff --git a/examples/benchmark_router_round_robin.rs b/examples/benchmark_router_round_robin.rs index bc00b0e..cc59aee 100644 --- a/examples/benchmark_router_round_robin.rs +++ b/examples/benchmark_router_round_robin.rs @@ -4,14 +4,17 @@ use std::time::{Duration, Instant}; use tyra::prelude::*; use tyra::router::{AddActorMessage, RoundRobinRouterFactory}; +#[derive(Hash)] struct MessageA {} impl ActorMessage for MessageA {} +#[derive(Hash)] struct Finish {} impl ActorMessage for Finish {} +#[derive(Hash)] struct Start {} impl ActorMessage for Start {} diff --git a/examples/benchmark_single_actor.rs b/examples/benchmark_single_actor.rs index 7e81fd4..7d56299 100644 --- a/examples/benchmark_single_actor.rs +++ b/examples/benchmark_single_actor.rs @@ -3,6 +3,7 @@ use std::process::exit; use std::time::{Duration, Instant}; use tyra::prelude::*; +#[derive(Hash)] struct MessageA {} impl ActorMessage for MessageA {} diff --git a/examples/benchmark_single_actor_process_after_send.rs b/examples/benchmark_single_actor_process_after_send.rs index fd271ce..ef40a49 100644 --- a/examples/benchmark_single_actor_process_after_send.rs +++ b/examples/benchmark_single_actor_process_after_send.rs @@ -3,6 +3,7 @@ use std::process::exit; use std::time::{Duration, Instant}; use tyra::prelude::*; +#[derive(Hash)] struct MessageA {} impl ActorMessage for MessageA {} diff --git a/examples/benchmark_single_actor_process_after_send_single_thread.rs b/examples/benchmark_single_actor_process_after_send_single_thread.rs index fee271b..b72583d 100644 --- a/examples/benchmark_single_actor_process_after_send_single_thread.rs +++ b/examples/benchmark_single_actor_process_after_send_single_thread.rs @@ -3,6 +3,7 @@ use std::process::exit; use std::time::{Duration, Instant}; use tyra::prelude::*; +#[derive(Hash)] struct MessageA {} impl ActorMessage for MessageA {} diff --git a/examples/benchmark_single_actor_single_thread.rs b/examples/benchmark_single_actor_single_thread.rs index 3e1142d..278c7a7 100644 --- a/examples/benchmark_single_actor_single_thread.rs +++ b/examples/benchmark_single_actor_single_thread.rs @@ -3,6 +3,7 @@ use std::process::exit; use std::time::{Duration, Instant}; use tyra::prelude::*; +#[derive(Hash)] struct MessageA {} impl ActorMessage for MessageA {} diff --git a/examples/quickstart.rs b/examples/quickstart.rs index 2e905a5..0e87b9a 100644 --- a/examples/quickstart.rs +++ b/examples/quickstart.rs @@ -3,6 +3,8 @@ use std::time::Duration; use tyra::prelude::*; // define an `ActorMessage` that can be sent to `Actors` that implement the corresponding `Handler` + +#[derive(Hash)] struct TestMessage {} impl TestMessage { pub fn new() -> Self { diff --git a/examples/serialize.rs b/examples/serialize.rs index 09d78f2..f795f79 100644 --- a/examples/serialize.rs +++ b/examples/serialize.rs @@ -4,7 +4,7 @@ use std::process::exit; use std::time::{Duration, Instant}; use tyra::prelude::*; -#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] +#[derive(Serialize, Deserialize, PartialEq, Debug, Clone, Hash)] struct TestMsg { content: String, } diff --git a/examples/webserver.rs b/examples/webserver.rs deleted file mode 100644 index d9e5988..0000000 --- a/examples/webserver.rs +++ /dev/null @@ -1,20 +0,0 @@ -use tyra::prelude::{ActorSystem, NetworkManagerFactory, ThreadPoolConfig, TyraConfig}; - -fn main() { - // generate config - let mut actor_config = TyraConfig::new().unwrap(); - let cluster = ThreadPoolConfig::new(18, 16, 16, 1.00); - actor_config.thread_pool.config.insert(String::from("tcp-test"), cluster); - // start system with config - let actor_system = ActorSystem::new(actor_config); - // create actor on the system - let _actor = actor_system - .builder() - .set_pool_name("tcp-test") - .spawn("test", NetworkManagerFactory::new(3)) - .unwrap(); - // send a message to the actor - - std::process::exit(actor_system.await_shutdown()); -} - diff --git a/src/actor/handler.rs b/src/actor/handler.rs index b0b32df..b3be63c 100644 --- a/src/actor/handler.rs +++ b/src/actor/handler.rs @@ -20,6 +20,7 @@ use std::error::Error; /// struct TestActor {} /// impl Actor for TestActor {} /// +/// #[derive(Hash)] /// struct FooBar {} /// impl ActorMessage for FooBar {} /// diff --git a/src/lib.rs b/src/lib.rs index 579dde4..696eaab 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,6 +17,7 @@ //! use std::error::Error; //! //! // define message +//! #[derive(Hash)] //! struct FooBar {} //! impl ActorMessage for FooBar {} //! diff --git a/src/routers/least_message_router.rs b/src/routers/least_message_router.rs index 28f0474..a8cd234 100644 --- a/src/routers/least_message_router.rs +++ b/src/routers/least_message_router.rs @@ -35,6 +35,7 @@ where /// use std::time::Duration; /// use tyra::router::{LeastMessageRouterFactory, AddActorMessage}; /// // define message +/// #[derive(Hash)] /// struct FooBar {} /// impl ActorMessage for FooBar {} /// diff --git a/src/routers/round_robin_router.rs b/src/routers/round_robin_router.rs index 6ecb21e..19adbb1 100644 --- a/src/routers/round_robin_router.rs +++ b/src/routers/round_robin_router.rs @@ -36,6 +36,7 @@ where /// use tyra::router::{RoundRobinRouterFactory, AddActorMessage}; /// /// // define message +/// #[derive(Hash)] /// struct FooBar {} /// impl ActorMessage for FooBar {} /// diff --git a/src/routers/sharded_router.rs b/src/routers/sharded_router.rs index b6ef7bd..8755d5c 100644 --- a/src/routers/sharded_router.rs +++ b/src/routers/sharded_router.rs @@ -40,6 +40,7 @@ where /// use tyra::router::{ShardedRouterFactory, AddActorMessage}; /// /// // define message +/// #[derive(Hash)] /// struct FooBar {} /// impl ActorMessage for FooBar {} /// diff --git a/src/system/actor_system.rs b/src/system/actor_system.rs index 6324215..5af7a2f 100644 --- a/src/system/actor_system.rs +++ b/src/system/actor_system.rs @@ -184,6 +184,7 @@ impl ActorSystem { /// /// struct TestActor {} /// + /// #[derive(Hash)] /// struct HelloWorld {} /// impl ActorMessage for HelloWorld {} /// impl Actor for TestActor { From d32f56d1de87f8103f1fe99dd1a947d381043d32 Mon Sep 17 00:00:00 2001 From: Bobonium Date: Sun, 30 Apr 2023 15:16:41 +0200 Subject: [PATCH 18/44] harden net implementation by handling all potential errors --- examples/net.rs | 1 - src/net/net_manager.rs | 67 ++++++++++++++++++++++++++++++++++++------ 2 files changed, 58 insertions(+), 10 deletions(-) diff --git a/examples/net.rs b/examples/net.rs index 2a5a403..c8cad33 100644 --- a/examples/net.rs +++ b/examples/net.rs @@ -22,7 +22,6 @@ fn main() { .unwrap(); - std::process::exit(actor_system.await_shutdown()); } diff --git a/src/net/net_manager.rs b/src/net/net_manager.rs index bbaaf2f..20bf70f 100644 --- a/src/net/net_manager.rs +++ b/src/net/net_manager.rs @@ -10,7 +10,7 @@ use std::thread; use std::thread::sleep; use std::time::{Duration, Instant}; use io_arc::IoArc; -use log::{error, warn}; +use log::{debug, error, warn}; use mio::net::{TcpListener, TcpStream, UdpSocket}; use mio::{Events, Interest, Poll, Token}; use mio::event::Source; @@ -121,6 +121,11 @@ impl Actor for NetManager break; } NetProtocol::UDP => { + let sock = UdpSocket::bind("127.0.0.1:0".parse().unwrap()); + if sock.is_ok() { + let sock = sock.unwrap(); + let _ = sock.send_to(b"", address.parse().unwrap()); + } break; } } @@ -173,17 +178,26 @@ impl Handler for NetManager where T: Handler + Handler + Handler + Handler + Handler + 'static, { - fn handle(&mut self, _msg: ActorInitMessage, _context: &ActorContext) -> Result> { + fn handle(&mut self, _msg: ActorInitMessage, context: &ActorContext) -> Result> { let router = self.router.clone(); let is_stopping = self.is_stopping.clone(); let is_stopped = self.is_stopped.clone(); let mut net_configs = self.net_configs.clone(); let mut last_udp_message_received = Instant::now(); let on_stop_udp_timeout = self.on_stop_udp_timeout.clone(); + let context = context.clone(); thread::spawn(move || { + let mut tcp_listeners :HashMap = HashMap::new(); let mut udp_sockets:HashMap> = HashMap::new(); - let mut poll = Poll::new().unwrap(); + let poll = Poll::new(); + if poll.is_err() { + error!("Can't start Poll Port: {:?}", poll.err()); + is_stopped.store(true, Ordering::Relaxed); + let _ = context.actor_ref.stop(); + return; + } + let mut poll = poll.unwrap(); let mut i = 0; net_configs.sort_by_key(|c| c.protocol); @@ -195,13 +209,39 @@ impl Handler for NetManager match net_config.protocol { NetProtocol::TCP => { - let mut listener = TcpListener::bind(address).unwrap(); - poll.registry().register(&mut listener, token, Interest::READABLE).unwrap(); + let listener = TcpListener::bind(address); + if listener.is_err() { + error!("Can't open TCP Port: {:?}", listener.err()); + is_stopped.store(true, Ordering::Relaxed); + let _ = context.actor_ref.stop(); + return; + } + let mut listener = listener.unwrap(); + let res = poll.registry().register(&mut listener, token, Interest::READABLE); + if res.is_err() { + error!("Can't register TCP listener: {:?}", res.err()); + is_stopped.store(true, Ordering::Relaxed); + let _ = context.actor_ref.stop(); + return; + } tcp_listeners.insert(token, listener); }, NetProtocol::UDP => { - let mut socket = UdpSocket::bind(address).unwrap(); - poll.registry().register(&mut socket, token, Interest::READABLE).unwrap(); + let socket = UdpSocket::bind(address); + if socket.is_err() { + error!("Can't open TCP Port: {:?}", socket.err()); + is_stopped.store(true, Ordering::Relaxed); + let _ = context.actor_ref.stop(); + return; + } + let mut socket = socket.unwrap(); + let res = poll.registry().register(&mut socket, token, Interest::READABLE); + if res.is_err() { + error!("Can't register UDP Socket: {:?}", res.err()); + is_stopped.store(true, Ordering::Relaxed); + let _ = context.actor_ref.stop(); + return; + } let socket = IoArc::new(socket); udp_sockets.insert(token, socket.clone()); let _ = router.send(AddUdpSocket::new(token.0, socket)); @@ -222,7 +262,11 @@ impl Handler for NetManager return; } - poll.poll(&mut events, None).unwrap(); + let res = poll.poll(&mut events, None); + if res.is_err() { + debug!("Can't poll Network Events"); + continue + } for event in events.iter() { let stopping = is_stopping.load(Ordering::Relaxed); @@ -232,7 +276,12 @@ impl Handler for NetManager } let token = &event.token(); if token.0 < num_tcp_listeners { - let listener = tcp_listeners.get(token).unwrap(); + let listener = tcp_listeners.get(token); + if listener.is_none() { + warn!("Can't find TcpListener for {:?}", token); + continue; + } + let listener = listener.unwrap(); loop { match listener.accept() { From b4b821d73d67c8127ce15de4143c2e31d2892818 Mon Sep 17 00:00:00 2001 From: Bobonium Date: Tue, 2 May 2023 20:51:24 +0200 Subject: [PATCH 19/44] WIP: rewrite ActorWrapper to be compatible with remoting --- src/actor/actor_address.rs | 4 +- src/actor/actor_builder.rs | 6 +- src/actor/actor_wrapper.rs | 172 ------------------------ src/actor/context.rs | 4 +- src/actor/executor.rs | 4 +- src/actor/mod.rs | 2 - src/lib.rs | 2 + src/routers/add_actor_message.rs | 4 +- src/routers/least_message_router.rs | 4 +- src/routers/remove_actor_message.rs | 4 +- src/routers/round_robin_router.rs | 4 +- src/routers/sharded_router.rs | 4 +- src/system/system_state.rs | 4 +- src/wrapper/actor_wrapper.rs | 200 ++++++++++++++++++++++++++++ src/wrapper/local_actor_wrapper.rs | 141 ++++++++++++++++++++ src/wrapper/mod.rs | 7 + src/wrapper/remote_actor_wrapper.rs | 85 ++++++++++++ 17 files changed, 458 insertions(+), 193 deletions(-) delete mode 100644 src/actor/actor_wrapper.rs create mode 100644 src/wrapper/actor_wrapper.rs create mode 100644 src/wrapper/local_actor_wrapper.rs create mode 100644 src/wrapper/mod.rs create mode 100644 src/wrapper/remote_actor_wrapper.rs diff --git a/src/actor/actor_address.rs b/src/actor/actor_address.rs index 1c07888..9c2e329 100644 --- a/src/actor/actor_address.rs +++ b/src/actor/actor_address.rs @@ -1,4 +1,6 @@ -#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone)] +use serde::{Deserialize, Serialize}; + +#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Serialize, Deserialize)] pub struct ActorAddress { pub remote: String, pub system: String, diff --git a/src/actor/actor_builder.rs b/src/actor/actor_builder.rs index e34eedf..376d1a8 100644 --- a/src/actor/actor_builder.rs +++ b/src/actor/actor_builder.rs @@ -1,11 +1,10 @@ use crate::actor::actor_address::ActorAddress; use crate::actor::actor_config::ActorConfig; use crate::actor::actor_factory::ActorFactory; -use crate::actor::actor_wrapper::ActorWrapper; use crate::actor::executor::{Executor, ExecutorTrait}; use crate::actor::mailbox::Mailbox; use crate::config::tyra_config::DEFAULT_POOL; -use crate::prelude::{Actor, Handler, SerializedMessage}; +use crate::prelude::{Actor, Handler, ActorWrapper, SerializedMessage}; use crate::system::actor_error::ActorError; use crate::system::actor_system::ActorSystem; use crate::system::internal_actor_manager::InternalActorManager; @@ -196,7 +195,7 @@ where if self.system_state.is_mailbox_active(&actor_address) { return self .system_state - .get_actor_ref(actor_address, self.internal_actor_manager.clone()); + .get_actor_ref(actor_address, self.internal_actor_manager.clone(), self.system.clone()); } let result = self.system_state.increase_pool_actor_count(&actor_address); @@ -221,6 +220,7 @@ where actor_address.clone(), self.wakeup_manager.clone(), self.internal_actor_manager.clone(), + self.system_state.clone(), ); let actor_handler = Executor::new( diff --git a/src/actor/actor_wrapper.rs b/src/actor/actor_wrapper.rs deleted file mode 100644 index 6651238..0000000 --- a/src/actor/actor_wrapper.rs +++ /dev/null @@ -1,172 +0,0 @@ -use crate::actor::actor_address::ActorAddress; -use crate::actor::actor_send_error::ActorSendError; -use crate::actor::handler::Handler; -use crate::actor::mailbox::Mailbox; -use crate::message::actor_message::BaseActorMessage; -use crate::message::actor_stop_message::ActorStopMessage; -use crate::message::sleep_message::SleepMessage; -use crate::prelude::Actor; -use crate::system::internal_actor_manager::InternalActorManager; -use crate::system::wakeup_manager::WakeupManager; -use std::fmt::{Debug, Formatter}; -use std::panic::UnwindSafe; -use std::thread::sleep; -use std::time::Duration; - -/// Wrapper used to interact with [Actor] -pub struct ActorWrapper -where - A: Actor, -{ - mailbox: Mailbox, - address: ActorAddress, - wakeup_manager: WakeupManager, - internal_actor_manager: Box, -} - -impl Debug for ActorWrapper -where - A: Actor, -{ - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "") - } -} - -impl UnwindSafe for ActorWrapper where A: Actor {} - -impl ActorWrapper -where - A: Actor + UnwindSafe, -{ - /// Automatically called by the [ActorBuilder.build](../prelude/struct.ActorBuilder.html#method.build) - pub fn new( - mailbox: Mailbox, - address: ActorAddress, - wakeup_manager: WakeupManager, - internal_actor_manager: InternalActorManager, - ) -> Self { - Self { - mailbox, - address, - wakeup_manager, - internal_actor_manager: Box::new(internal_actor_manager), - } - } - - /// Sends a message to the actor that is then processed through the corresponding Handler implementation - /// Blocks until message has been sent, or fails if the target has been stopped - /// It is NOT recommended to use this to send messages to Actors with a limited mailbox. Use send_timeout() or send_after() for these cases - pub fn send(&self, msg: M) -> Result<(), ActorSendError> - where - A: Handler, - M: BaseActorMessage + 'static, - { - if self.mailbox.is_stopped() { - return Err(ActorSendError::AlreadyStoppedError); - } - - let result = self.mailbox.send(msg); - - if result.is_err() { - return result; - } - - if self.mailbox.is_sleeping() { - self.wakeup_manager.wakeup(self.address.clone()); - } - - return Ok(()); - } - - /// Same as send, but with a user defined timeout - pub fn send_timeout(&self, msg: M, timeout: Duration) -> Result<(), ActorSendError> - where - A: Handler, - M: BaseActorMessage + 'static, - { - if self.mailbox.is_stopped() { - return Err(ActorSendError::AlreadyStoppedError); - } - - let result = self.mailbox.send_timeout(msg, timeout); - - if result.is_err() { - return result; - } - - if self.mailbox.is_sleeping() { - self.wakeup_manager.wakeup(self.address.clone()); - } - - return Ok(()); - } - - /// Sends a message to the actor after a specified delay - pub fn send_after(&self, msg: M, delay: Duration) -> Result<(), ActorSendError> - where - A: Handler + 'static, - M: BaseActorMessage + 'static, - { - if self.mailbox.is_stopped() { - return Err(ActorSendError::AlreadyStoppedError); - } - - self.internal_actor_manager - .send_after(msg, self.clone(), delay); - - return Ok(()); - } - - /// Tells the actor to stop accepting message and to shutdown after all existing messages have been processed - pub fn stop(&self) -> Result<(), ActorSendError> { - return self.send(ActorStopMessage::new()); - } - - /// Tells the actor to sleep for the specified duration - pub fn sleep(&self, duration: Duration) -> Result<(), ActorSendError> { - return self.send(SleepMessage { duration }); - } - - /// Returns a reference to the address of the actor - pub fn get_address(&self) -> &ActorAddress { - &self.address - } - - /// Returns the current mailbox size - pub fn get_mailbox_size(&self) -> usize { - return self.mailbox.len(); - } - - /// Returns true if an actor is no longer accepting messages - pub fn is_mailbox_stopped(&self) -> bool { - return self.mailbox.is_stopped() - } - - /// Returns true if an actor has been completely stopped after processing all messages that are still within the queue - pub fn is_stopped(&self) -> bool { - return self.get_mailbox_size() == 0 && self.mailbox.is_stopped() - } - - /// Blocks until the actor has been stopped - pub fn wait_for_stop(&self) { - let _ = self.stop(); - while !self.is_stopped() { - sleep(Duration::from_millis(25)); - } - } -} - -impl Clone for ActorWrapper -where - A: Actor + UnwindSafe, -{ - fn clone(&self) -> Self { - Self { - wakeup_manager: self.wakeup_manager.clone(), - mailbox: self.mailbox.clone(), - address: self.address.clone(), - internal_actor_manager: self.internal_actor_manager.clone(), - } - } -} diff --git a/src/actor/context.rs b/src/actor/context.rs index 6d8e8b7..591b033 100644 --- a/src/actor/context.rs +++ b/src/actor/context.rs @@ -1,5 +1,5 @@ -use crate::actor::actor_wrapper::ActorWrapper; -use crate::prelude::Actor; + +use crate::prelude::{Actor, ActorWrapper}; use crate::system::actor_system::ActorSystem; use std::panic::UnwindSafe; diff --git a/src/actor/executor.rs b/src/actor/executor.rs index 5d440c9..5505291 100644 --- a/src/actor/executor.rs +++ b/src/actor/executor.rs @@ -2,14 +2,14 @@ use crate::actor::actor_address::ActorAddress; use crate::actor::actor_config::ActorConfig; use crate::actor::actor_factory::ActorFactory; use crate::actor::actor_state::ActorState; -use crate::actor::actor_wrapper::ActorWrapper; + use crate::actor::context::ActorContext; use crate::actor::handler::Handler; use crate::actor::mailbox::Mailbox; use crate::message::actor_message::BaseActorMessage; use crate::message::envelope::{MessageEnvelope, MessageEnvelopeTrait}; use crate::message::system_stop_message::SystemStopMessage; -use crate::prelude::{Actor, ActorPanicSource, ActorResult}; +use crate::prelude::{Actor, ActorPanicSource, ActorResult, ActorWrapper}; use crate::system::actor_error::ActorError; use crate::system::actor_system::ActorSystem; use log::debug; diff --git a/src/actor/mod.rs b/src/actor/mod.rs index 71c518c..13e5eee 100644 --- a/src/actor/mod.rs +++ b/src/actor/mod.rs @@ -7,7 +7,6 @@ pub mod actor_panic_source; pub mod actor_result; pub mod actor_send_error; pub mod actor_state; -pub mod actor_wrapper; pub mod context; pub mod executor; pub mod handler; @@ -20,7 +19,6 @@ pub mod prelude { pub use crate::actor::actor_panic_source::ActorPanicSource; pub use crate::actor::actor_result::ActorResult; pub use crate::actor::actor_send_error::ActorSendError; - pub use crate::actor::actor_wrapper::ActorWrapper; pub use crate::actor::context::ActorContext; pub use crate::actor::handler::Handler; } diff --git a/src/lib.rs b/src/lib.rs index 696eaab..70cd0ee 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -112,6 +112,7 @@ mod message; mod routers; mod system; mod net; +mod wrapper; /// core components pub mod prelude { @@ -120,6 +121,7 @@ pub mod prelude { pub use crate::message::prelude::*; pub use crate::system::prelude::*; pub use crate::net::prelude::*; + pub use crate::wrapper::prelude::*; } /// collection of different router implementations diff --git a/src/routers/add_actor_message.rs b/src/routers/add_actor_message.rs index aa7e7cb..5747cce 100644 --- a/src/routers/add_actor_message.rs +++ b/src/routers/add_actor_message.rs @@ -1,7 +1,7 @@ use std::hash::{Hash, Hasher}; -use crate::actor::actor_wrapper::ActorWrapper; + use crate::message::actor_message::BaseActorMessage; -use crate::prelude::Actor; +use crate::prelude::{Actor, ActorWrapper}; /// Adds an Actor to the Router pub struct AddActorMessage diff --git a/src/routers/least_message_router.rs b/src/routers/least_message_router.rs index a8cd234..d3b2327 100644 --- a/src/routers/least_message_router.rs +++ b/src/routers/least_message_router.rs @@ -1,8 +1,8 @@ use crate::actor::actor_factory::ActorFactory; -use crate::actor::actor_wrapper::ActorWrapper; + use crate::actor::context::ActorContext; use crate::actor::handler::Handler; -use crate::prelude::{Actor, ActorMessage, ActorResult}; +use crate::prelude::{Actor, ActorMessage, ActorResult, ActorWrapper}; use crate::routers::add_actor_message::AddActorMessage; use crate::routers::remove_actor_message::RemoveActorMessage; use log::{debug, error}; diff --git a/src/routers/remove_actor_message.rs b/src/routers/remove_actor_message.rs index d08b717..146dac1 100644 --- a/src/routers/remove_actor_message.rs +++ b/src/routers/remove_actor_message.rs @@ -1,7 +1,7 @@ use std::hash::{Hash, Hasher}; -use crate::actor::actor_wrapper::ActorWrapper; + use crate::message::actor_message::BaseActorMessage; -use crate::prelude::Actor; +use crate::prelude::{Actor, ActorWrapper}; /// Removes an Actor from the Router pub struct RemoveActorMessage diff --git a/src/routers/round_robin_router.rs b/src/routers/round_robin_router.rs index 19adbb1..0c210b9 100644 --- a/src/routers/round_robin_router.rs +++ b/src/routers/round_robin_router.rs @@ -1,9 +1,9 @@ use crate::actor::actor_factory::ActorFactory; -use crate::actor::actor_wrapper::ActorWrapper; + use crate::actor::context::ActorContext; use crate::actor::handler::Handler; use crate::message::actor_message::{ActorMessage, BaseActorMessage}; -use crate::prelude::{Actor, ActorResult, BulkActorMessage}; +use crate::prelude::{Actor, ActorResult, BulkActorMessage, ActorWrapper}; use crate::routers::add_actor_message::AddActorMessage; use crate::routers::bulk_router_message::BulkRouterMessage; use crate::routers::remove_actor_message::RemoveActorMessage; diff --git a/src/routers/sharded_router.rs b/src/routers/sharded_router.rs index 8755d5c..4f1855d 100644 --- a/src/routers/sharded_router.rs +++ b/src/routers/sharded_router.rs @@ -1,9 +1,9 @@ use crate::actor::actor_factory::ActorFactory; -use crate::actor::actor_wrapper::ActorWrapper; + use crate::actor::context::ActorContext; use crate::actor::handler::Handler; use crate::message::actor_message::BaseActorMessage; -use crate::prelude::{Actor, ActorMessage, ActorResult, BulkActorMessage}; +use crate::prelude::{Actor, ActorMessage, ActorResult, BulkActorMessage, ActorWrapper}; use crate::routers::add_actor_message::AddActorMessage; use crate::routers::bulk_router_message::BulkRouterMessage; use crate::routers::remove_actor_message::RemoveActorMessage; diff --git a/src/system/system_state.rs b/src/system/system_state.rs index 309f1b6..c2d0797 100644 --- a/src/system/system_state.rs +++ b/src/system/system_state.rs @@ -1,7 +1,7 @@ use crate::actor::actor_address::ActorAddress; use crate::actor::mailbox::{BaseMailbox, Mailbox}; use crate::message::serialized_message::SerializedMessage; -use crate::prelude::{ActorWrapper, Handler}; +use crate::prelude::{ActorSystem, ActorWrapper, Handler}; use crate::system::actor_error::ActorError; use crate::system::internal_actor_manager::InternalActorManager; use crate::system::wakeup_manager::WakeupManager; @@ -198,6 +198,7 @@ impl SystemState { &self, address: ActorAddress, internal_actor_manager: InternalActorManager, + system: ActorSystem, ) -> Result, ActorError> where A: Handler + 'static, @@ -209,6 +210,7 @@ impl SystemState { address, self.wakeup_manager.clone(), internal_actor_manager, + self.clone(), )), None => Err(ActorError::InvalidActorTypeError), }; diff --git a/src/wrapper/actor_wrapper.rs b/src/wrapper/actor_wrapper.rs new file mode 100644 index 0000000..dad193b --- /dev/null +++ b/src/wrapper/actor_wrapper.rs @@ -0,0 +1,200 @@ +use std::fmt::{Debug, Formatter, Pointer}; +use std::hash::{Hash, Hasher}; +use std::marker::PhantomData; +use std::panic::UnwindSafe; +use std::time::Duration; +use serde::{Deserialize, Serialize}; +use crate::actor::actor_address::ActorAddress; +use crate::actor::mailbox::Mailbox; +use crate::message::actor_message::BaseActorMessage; +use crate::message::sleep_message::SleepMessage; +use crate::actor::actor_send_error::ActorSendError; +use crate::actor::actor::Actor; +use crate::actor::handler::Handler; +use crate::prelude::ActorSystem; +use crate::system::internal_actor_manager::InternalActorManager; +use crate::system::system_state::SystemState; +use crate::system::wakeup_manager::WakeupManager; +use crate::wrapper::local_actor_wrapper::LocalActorWrapper; +use crate::wrapper::remote_actor_wrapper::RemoteActorWrapper; + +#[derive(Serialize, Deserialize)] +pub struct ActorWrapper + where + A: Actor, +{ + is_local: bool, + address: ActorAddress, + remote: RemoteActorWrapper, + #[serde(skip)] + local: Option>, + +} + +impl Debug for ActorWrapper + where + A: Actor, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "") + } +} + +impl UnwindSafe for ActorWrapper where A: Actor {} + +impl ActorWrapper + where + A: Actor + UnwindSafe, +{ + /// Automatically called by the [ActorBuilder.build](../prelude/struct.ActorBuilder.html#method.build) + pub fn new( + mailbox: Mailbox, + address: ActorAddress, + wakeup_manager: WakeupManager, + internal_actor_manager: InternalActorManager, + system_state: SystemState, + ) -> Self { + let is_local = true; + let local = Some(LocalActorWrapper::new(mailbox, wakeup_manager, internal_actor_manager)); + let remote = RemoteActorWrapper::new(system_state); + Self { + is_local, + address, + remote, + local, + } + } + + /// Sends a message to the actor that is then processed through the corresponding Handler implementation + /// Blocks until message has been sent, or fails if the target has been stopped + /// It is NOT recommended to use this to send messages to Actors with a limited mailbox. Use send_timeout() or send_after() for these cases + pub fn send(&self, msg: M) -> Result<(), ActorSendError> + where + A: Handler, + M: BaseActorMessage + 'static, + { + if self.is_local && self.local.is_some() { + return self.local.as_ref().unwrap().send(msg, self.address.clone()); + } + else { + return self.remote.send(msg, &self.address); + } + } + + /// Same as send, but with a user defined timeout + pub fn send_timeout(&self, msg: M, timeout: Duration) -> Result<(), ActorSendError> + where + A: Handler, + M: BaseActorMessage + 'static, + { + if self.is_local && self.local.is_some() { + return self.local.as_ref().unwrap().send_timeout(msg, timeout, self.address.clone()); + } + else { + return self.remote.send_timeout(msg, timeout, self.address.clone()); + } + } + + /// Sends a message to the actor after a specified delay + pub fn send_after(&self, msg: M, delay: Duration) -> Result<(), ActorSendError> + where + A: Handler + 'static, + M: BaseActorMessage + 'static, + { + if self.is_local && self.local.is_some() { + return self.local.as_ref().unwrap().send_after(msg, delay); + } + else { + return self.remote.send_after(msg, delay, self.address.clone()); + } + } + + /// Tells the actor to stop accepting message and to shutdown after all existing messages have been processed + pub fn stop(&self) -> Result<(), ActorSendError> { + if self.is_local && self.local.is_some() { + return self.local.as_ref().unwrap().stop(self.address.clone()); + } + else { + return self.remote.stop(); + } + } + + /// Tells the actor to sleep for the specified duration + pub fn sleep(&self, duration: Duration) -> Result<(), ActorSendError> { + if self.is_local && self.local.is_some() { + return self.local.as_ref().unwrap().send(SleepMessage{ duration}, self.address.clone()); + } + else { + return self.remote.send(SleepMessage{ duration}, &self.address); + } + } + + /// Returns a reference to the address of the actor + /// Returns a reference to the address of the actor + pub fn get_address(&self) -> &ActorAddress { + &self.address + } + + /// Returns the current mailbox size + pub fn get_mailbox_size(&self) -> usize { + if self.is_local && self.local.is_some() { + return self.local.as_ref().unwrap().get_mailbox_size(); + } + else { + return self.remote.get_mailbox_size(); + } + } + + /// Returns true if an actor is no longer accepting messages + pub fn is_mailbox_stopped(&self) -> bool { + if self.is_local && self.local.is_some() { + return self.local.as_ref().unwrap().is_mailbox_stopped(); + } + else { + return self.remote.is_mailbox_stopped(); + } + } + + /// Returns true if an actor has been completely stopped after processing all messages that are still within the queue + pub fn is_stopped(&self) -> bool { + if self.is_local && self.local.is_some() { + return self.local.as_ref().unwrap().is_stopped(); + } + else { + return self.remote.is_stopped(); + } + } + + /// Blocks until the actor has been stopped + pub fn wait_for_stop(&self) { + if self.is_local && self.local.is_some() { + return self.local.as_ref().unwrap().wait_for_stop(self.address.clone()); + } + else { + return self.remote.wait_for_stop(); + } + } +} + +impl Clone for ActorWrapper + where + A: Actor + UnwindSafe, +{ + fn clone(&self) -> Self { + Self { + is_local: self.is_local.clone(), + remote: self.remote.clone(), + local: self.local.clone(), + address: self.address.clone(), + } + } +} + +impl Hash for ActorWrapper + where + A: Actor + UnwindSafe, +{ + fn hash(&self, state: &mut H) { + self.address.hash(state); + } +} \ No newline at end of file diff --git a/src/wrapper/local_actor_wrapper.rs b/src/wrapper/local_actor_wrapper.rs new file mode 100644 index 0000000..e0fb3a4 --- /dev/null +++ b/src/wrapper/local_actor_wrapper.rs @@ -0,0 +1,141 @@ +use std::marker::PhantomData; +use std::panic::UnwindSafe; +use std::thread::sleep; +use std::time::Duration; +use serde::{Deserialize, Serialize}; +use crate::actor::actor_address::ActorAddress; +use crate::actor::mailbox::Mailbox; +use crate::message::actor_message::BaseActorMessage; +use crate::message::actor_stop_message::ActorStopMessage; +use crate::message::sleep_message::SleepMessage; +use crate::actor::actor_send_error::ActorSendError; +use crate::actor::actor::Actor; +use crate::actor::handler::Handler; +use crate::system::internal_actor_manager::InternalActorManager; +use crate::system::wakeup_manager::WakeupManager; + +pub struct LocalActorWrapper + where + A: Actor, +{ + mailbox: Mailbox, + wakeup_manager: WakeupManager, + internal_actor_manager: Box, +} + + +impl LocalActorWrapper + where + A: Actor + UnwindSafe, +{ + pub fn new( + mailbox: Mailbox, + wakeup_manager: WakeupManager, + internal_actor_manager: InternalActorManager, + ) -> Self { + Self { + mailbox, + wakeup_manager, + internal_actor_manager: Box::new(internal_actor_manager), + } + } + + pub fn send(&self, msg: M, address: ActorAddress) -> Result<(), ActorSendError> + where + A: Handler, + M: BaseActorMessage + 'static, + { + if self.mailbox.is_stopped() { + return Err(ActorSendError::AlreadyStoppedError); + } + + let result = self.mailbox.send(msg); + + if result.is_err() { + return result; + } + + if self.mailbox.is_sleeping() { + self.wakeup_manager.wakeup(address); + } + + return Ok(()); + } + + pub fn send_timeout(&self, msg: M, timeout: Duration, address: ActorAddress) -> Result<(), ActorSendError> + where + A: Handler, + M: BaseActorMessage + 'static, + { + if self.mailbox.is_stopped() { + return Err(ActorSendError::AlreadyStoppedError); + } + + let result = self.mailbox.send_timeout(msg, timeout); + + if result.is_err() { + return result; + } + + if self.mailbox.is_sleeping() { + self.wakeup_manager.wakeup(address); + } + + return Ok(()); + } + + pub fn send_after(&self, msg: M, delay: Duration) -> Result<(), ActorSendError> + where + A: Handler + 'static, + M: BaseActorMessage + 'static, + { + if self.mailbox.is_stopped() { + return Err(ActorSendError::AlreadyStoppedError); + } + + //self.internal_actor_manager + // .send_after(msg, self.clone(), delay); + + return Ok(()); + } + + pub fn stop(&self, address: ActorAddress) -> Result<(), ActorSendError> { + return self.send(ActorStopMessage::new(), address); + } + + pub fn sleep(&self, duration: Duration, address: ActorAddress) -> Result<(), ActorSendError> { + return self.send(SleepMessage { duration }, address); + } + + pub fn get_mailbox_size(&self) -> usize { + return self.mailbox.len(); + } + + pub fn is_mailbox_stopped(&self) -> bool { + return self.mailbox.is_stopped() + } + + pub fn is_stopped(&self) -> bool { + return self.get_mailbox_size() == 0 && self.mailbox.is_stopped() + } + + pub fn wait_for_stop(&self, address: ActorAddress) { + let _ = self.stop(address); + while !self.is_stopped() { + sleep(Duration::from_millis(25)); + } + } +} + +impl Clone for LocalActorWrapper + where + A: Actor + UnwindSafe, +{ + fn clone(&self) -> Self { + Self { + wakeup_manager: self.wakeup_manager.clone(), + mailbox: self.mailbox.clone(), + internal_actor_manager: self.internal_actor_manager.clone(), + } + } +} \ No newline at end of file diff --git a/src/wrapper/mod.rs b/src/wrapper/mod.rs new file mode 100644 index 0000000..d0cb689 --- /dev/null +++ b/src/wrapper/mod.rs @@ -0,0 +1,7 @@ +mod local_actor_wrapper; +mod remote_actor_wrapper; +pub mod actor_wrapper; + +pub mod prelude { + pub use crate::wrapper::actor_wrapper::ActorWrapper; +} \ No newline at end of file diff --git a/src/wrapper/remote_actor_wrapper.rs b/src/wrapper/remote_actor_wrapper.rs new file mode 100644 index 0000000..6aa999a --- /dev/null +++ b/src/wrapper/remote_actor_wrapper.rs @@ -0,0 +1,85 @@ +use std::hash::{Hash, Hasher}; +use std::panic::UnwindSafe; +use std::time::Duration; +use serde::{Deserialize, Serialize}; +use crate::actor::actor_address::ActorAddress; +use crate::message::actor_message::BaseActorMessage; +use crate::message::actor_stop_message::ActorStopMessage; +use crate::actor::actor_send_error::ActorSendError; +use crate::actor::actor::Actor; +use crate::actor::handler::Handler; +use crate::system::system_state::SystemState; + +#[derive(Serialize, Deserialize, Clone)] +pub struct RemoteActorWrapper { + #[serde(skip)] + system_state: Option, +} + +impl RemoteActorWrapper +{ + pub fn new( + system_state: SystemState, + ) -> Self { + let system_state = Some(system_state); + return Self { + system_state + }; + } + + pub fn send(&self, msg: M, address: &ActorAddress) -> Result<(), ActorSendError> + where + M: BaseActorMessage + 'static, + { + //todo this needs to forward to the NetWorker + //the Networker should then forward this to the remote actor system + //the remote actor system should then forward the message using system_state + //self.system_state.as_ref().unwrap().send_to_address(address, msg ); + return Ok(()); + } + + pub fn send_timeout(&self, msg: M, timeout: Duration, address: ActorAddress) -> Result<(), ActorSendError> + where + M: BaseActorMessage + 'static, + { + return Ok(()); + } + + pub fn send_after(&self, msg: M, delay: Duration, address: ActorAddress) -> Result<(), ActorSendError> + where + M: BaseActorMessage + 'static, + { + return Ok(()); + } + + pub fn stop(&self) -> Result<(), ActorSendError> { + return Ok(()); + } + + pub fn sleep(&self, duration: Duration, address: ActorAddress) -> Result<(), ActorSendError> { + return Ok(()); + } + + pub fn get_mailbox_size(&self) -> usize { + return 0; + } + + pub fn is_mailbox_stopped(&self) -> bool { + return true; + } + + pub fn is_stopped(&self) -> bool { + return true; + } + + pub fn wait_for_stop(&self) { + return; + } +} + +impl Hash for RemoteActorWrapper +{ + fn hash(&self, state: &mut H) { + return; + } +} \ No newline at end of file From 2ab3757454cf180a3719a48479e8e13bafcfaab2 Mon Sep 17 00:00:00 2001 From: Bobonium Date: Tue, 2 May 2023 23:14:19 +0200 Subject: [PATCH 20/44] require all ActorMessages to be serializable --- CHANGELOG.md | 3 +++ Cargo.toml | 2 +- examples/benchmark_bulk_router.rs | 7 ++++--- examples/benchmark_router_round_robin.rs | 7 ++++--- examples/benchmark_single_actor.rs | 3 ++- .../benchmark_single_actor_process_after_send.rs | 3 ++- ..._single_actor_process_after_send_single_thread.rs | 3 ++- examples/benchmark_single_actor_single_thread.rs | 3 ++- examples/quickstart.rs | 3 ++- src/actor/handler.rs | 3 ++- src/lib.rs | 3 ++- src/message/actor_init_message.rs | 3 ++- src/message/actor_message.rs | 12 +++++++----- src/message/actor_stop_message.rs | 3 ++- src/message/bulk_actor_message.rs | 3 ++- src/message/delayed_message.rs | 4 ++++ src/message/serialized_message.rs | 3 ++- src/message/sleep_message.rs | 3 ++- src/message/system_stop_message.rs | 3 ++- src/net/net_messages.rs | 9 +++++++++ src/routers/add_actor_message.rs | 7 ++++++- src/routers/bulk_router_message.rs | 2 ++ src/routers/least_message_router.rs | 3 ++- src/routers/remove_actor_message.rs | 3 +++ src/routers/round_robin_router.rs | 3 ++- src/routers/send_to_all_targets_message.rs | 2 ++ src/routers/sharded_router.rs | 5 +++-- src/system/actor_system.rs | 3 ++- src/wrapper/remote_actor_wrapper.rs | 4 +++- 29 files changed, 83 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e86e61..047e880 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,9 @@ - `ActorMessage` now needs to implement `Hash` and no longer need to explicitly implement `ActorMessage.get_id()` to be able to properly make use of the `ShardedRouter` - renamed `ActorMessage.get_id()` to `ActorMessage.get_hash()` and changed return type to u64 - changed default implementation to `ActorMessage.get_hash()` to make use of `Hash` implementation + - `ActorMessage` now needs to implement `Serialize` + - this requirement comes from the fact, that all messages need to be serializable in theory to be able to be sent to other actor systems + - if a message is really intended to be sent it should obviously also implement `Deserialize` - Added `.is_mailbox_stopped()`, `is_stopped()` and `wait_for_stop()` to `ActorWrapper` - Added `general.signal_graceful_timeout_in_seconds` to config - Added `ActorBuilder.spawn_multiple()` diff --git a/Cargo.toml b/Cargo.toml index fc53c00..bb0cde2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,7 @@ mio = {version = "0.8.6", features = ["os-poll", "os-ext", "net"]} quiche = "0.17.1" io-arc = "1.0.0" hashring = "0.3.0" +bincode = "1.3.3" [dev-dependencies] -bincode = "1.3.3" ntest = "0.9.0" \ No newline at end of file diff --git a/examples/benchmark_bulk_router.rs b/examples/benchmark_bulk_router.rs index d906306..9a7239e 100644 --- a/examples/benchmark_bulk_router.rs +++ b/examples/benchmark_bulk_router.rs @@ -2,20 +2,21 @@ use std::error::Error; use std::process::exit; use std::thread::sleep; use std::time::{Duration, Instant}; +use serde::Serialize; use tyra::prelude::*; use tyra::router::{AddActorMessage, BulkRouterMessage, RoundRobinRouterFactory}; -#[derive(Hash)] +#[derive(Hash, Serialize)] struct MessageA {} impl ActorMessage for MessageA {} -#[derive(Hash)] +#[derive(Hash, Serialize)] struct Finish {} impl ActorMessage for Finish {} -#[derive(Hash)] +#[derive(Hash, Serialize)] struct Start {} impl ActorMessage for Start {} diff --git a/examples/benchmark_router_round_robin.rs b/examples/benchmark_router_round_robin.rs index cc59aee..7024fd5 100644 --- a/examples/benchmark_router_round_robin.rs +++ b/examples/benchmark_router_round_robin.rs @@ -1,20 +1,21 @@ use std::error::Error; use std::process::exit; use std::time::{Duration, Instant}; +use serde::Serialize; use tyra::prelude::*; use tyra::router::{AddActorMessage, RoundRobinRouterFactory}; -#[derive(Hash)] +#[derive(Hash, Serialize)] struct MessageA {} impl ActorMessage for MessageA {} -#[derive(Hash)] +#[derive(Hash, Serialize)] struct Finish {} impl ActorMessage for Finish {} -#[derive(Hash)] +#[derive(Hash, Serialize)] struct Start {} impl ActorMessage for Start {} diff --git a/examples/benchmark_single_actor.rs b/examples/benchmark_single_actor.rs index 7d56299..df0316b 100644 --- a/examples/benchmark_single_actor.rs +++ b/examples/benchmark_single_actor.rs @@ -1,9 +1,10 @@ use std::error::Error; use std::process::exit; use std::time::{Duration, Instant}; +use serde::Serialize; use tyra::prelude::*; -#[derive(Hash)] +#[derive(Hash, Serialize)] struct MessageA {} impl ActorMessage for MessageA {} diff --git a/examples/benchmark_single_actor_process_after_send.rs b/examples/benchmark_single_actor_process_after_send.rs index ef40a49..bdd760a 100644 --- a/examples/benchmark_single_actor_process_after_send.rs +++ b/examples/benchmark_single_actor_process_after_send.rs @@ -1,9 +1,10 @@ use std::error::Error; use std::process::exit; use std::time::{Duration, Instant}; +use serde::Serialize; use tyra::prelude::*; -#[derive(Hash)] +#[derive(Hash, Serialize)] struct MessageA {} impl ActorMessage for MessageA {} diff --git a/examples/benchmark_single_actor_process_after_send_single_thread.rs b/examples/benchmark_single_actor_process_after_send_single_thread.rs index b72583d..9b6ba19 100644 --- a/examples/benchmark_single_actor_process_after_send_single_thread.rs +++ b/examples/benchmark_single_actor_process_after_send_single_thread.rs @@ -1,9 +1,10 @@ use std::error::Error; use std::process::exit; use std::time::{Duration, Instant}; +use serde::Serialize; use tyra::prelude::*; -#[derive(Hash)] +#[derive(Hash, Serialize)] struct MessageA {} impl ActorMessage for MessageA {} diff --git a/examples/benchmark_single_actor_single_thread.rs b/examples/benchmark_single_actor_single_thread.rs index 278c7a7..4089701 100644 --- a/examples/benchmark_single_actor_single_thread.rs +++ b/examples/benchmark_single_actor_single_thread.rs @@ -1,9 +1,10 @@ use std::error::Error; use std::process::exit; use std::time::{Duration, Instant}; +use serde::Serialize; use tyra::prelude::*; -#[derive(Hash)] +#[derive(Hash, Serialize)] struct MessageA {} impl ActorMessage for MessageA {} diff --git a/examples/quickstart.rs b/examples/quickstart.rs index 0e87b9a..8e37d38 100644 --- a/examples/quickstart.rs +++ b/examples/quickstart.rs @@ -1,10 +1,11 @@ use std::error::Error; use std::time::Duration; +use serde::Serialize; use tyra::prelude::*; // define an `ActorMessage` that can be sent to `Actors` that implement the corresponding `Handler` -#[derive(Hash)] +#[derive(Hash, Serialize)] struct TestMessage {} impl TestMessage { pub fn new() -> Self { diff --git a/src/actor/handler.rs b/src/actor/handler.rs index b3be63c..35ac12b 100644 --- a/src/actor/handler.rs +++ b/src/actor/handler.rs @@ -15,12 +15,13 @@ use std::error::Error; /// /// ```rust /// use std::error::Error; +/// use serde::Serialize; /// use tyra::prelude::{TyraConfig, ActorSystem, ActorFactory, ActorContext, SerializedMessage, Handler, Actor, ActorResult, ActorMessage}; /// /// struct TestActor {} /// impl Actor for TestActor {} /// -/// #[derive(Hash)] +/// #[derive(Hash, Serialize)] /// struct FooBar {} /// impl ActorMessage for FooBar {} /// diff --git a/src/lib.rs b/src/lib.rs index 70cd0ee..636c4a6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,9 +15,10 @@ //! use std::process::exit; //! use std::time::Duration; //! use std::error::Error; +//! use serde::Serialize; //! //! // define message -//! #[derive(Hash)] +//! #[derive(Hash, Serialize)] //! struct FooBar {} //! impl ActorMessage for FooBar {} //! diff --git a/src/message/actor_init_message.rs b/src/message/actor_init_message.rs index 0dcfefa..5542f2a 100644 --- a/src/message/actor_init_message.rs +++ b/src/message/actor_init_message.rs @@ -1,7 +1,8 @@ +use serde::{Deserialize, Serialize}; use crate::prelude::ActorMessage; /// Can be implemented by an Actor through Handler to be used to init an Actor -#[derive(Hash)] +#[derive(Hash, Serialize, Deserialize)] pub struct ActorInitMessage {} impl ActorInitMessage { diff --git a/src/message/actor_message.rs b/src/message/actor_message.rs index 82e569d..72ce8ea 100644 --- a/src/message/actor_message.rs +++ b/src/message/actor_message.rs @@ -1,17 +1,18 @@ use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; +use serde::Serialize; /// This trait is used internally by the `ActorSystem` and builds the base for all messaging /// It's automatically implemented by the `ActorMessage` trait that should be used /// /// It is used by Messages defined in the system /// All messages that use this trait directly should also implement a dynamic `Handler` that applies to any `Actor` -pub trait BaseActorMessage: Send + Sync + Hash { +pub trait BaseActorMessage: Send + Sync + Hash + Serialize { } /// This trait is used by Messages defined by the system /// All messages that use this trait should also implement a dynamic `Handler` that applies to any `Actor` -pub trait DefaultActorMessage: Send + Sync + Hash { +pub trait DefaultActorMessage: Send + Sync + Hash + Serialize { } impl BaseActorMessage for A @@ -26,13 +27,14 @@ where /// Basic usage: /// /// ```rust +/// use serde::Serialize; /// use tyra::prelude::ActorMessage; /// -/// #[derive(Hash)] +/// #[derive(Hash, Serialize)] /// struct FooBar {} /// impl ActorMessage for FooBar {} /// ``` -pub trait ActorMessage: Send + Sync + Hash { +pub trait ActorMessage: Send + Sync + Hash + Serialize { /// returns the message hash fn get_hash(&self) -> u64 { let mut hasher = DefaultHasher::new(); @@ -44,5 +46,5 @@ pub trait ActorMessage: Send + Sync + Hash { /// this should be `BaseActorMessage` but it's currently not possible because of https://github.com/rust-lang/rust/issues/20400 impl DefaultActorMessage for A where - A: ActorMessage + A: ActorMessage + Serialize {} \ No newline at end of file diff --git a/src/message/actor_stop_message.rs b/src/message/actor_stop_message.rs index d540efc..747fb29 100644 --- a/src/message/actor_stop_message.rs +++ b/src/message/actor_stop_message.rs @@ -1,6 +1,7 @@ +use serde::{Deserialize, Serialize}; use crate::message::actor_message::{DefaultActorMessage}; -#[derive(Hash)] +#[derive(Hash, Serialize, Deserialize)] pub struct ActorStopMessage {} impl ActorStopMessage { diff --git a/src/message/bulk_actor_message.rs b/src/message/bulk_actor_message.rs index 4f0a133..2183fa7 100644 --- a/src/message/bulk_actor_message.rs +++ b/src/message/bulk_actor_message.rs @@ -1,9 +1,10 @@ +use serde::Serialize; /// Bulk Actor Message, that can wrap and send multiple [ActorMessage](../prelude/trait.ActorMessage.html) at once /// use crate::message::actor_message::BaseActorMessage; /// Wraps multiple [ActorMessage](../prelude/trait.ActorMessage.html) to be sent to an Actor -#[derive(Hash)] +#[derive(Hash, Serialize)] pub struct BulkActorMessage where M: BaseActorMessage + 'static, diff --git a/src/message/delayed_message.rs b/src/message/delayed_message.rs index f9e8017..4af20c7 100644 --- a/src/message/delayed_message.rs +++ b/src/message/delayed_message.rs @@ -2,16 +2,20 @@ use std::hash::{Hash, Hasher}; use crate::message::actor_message::BaseActorMessage; use crate::prelude::{Actor, ActorMessage, ActorWrapper}; use std::time::{Duration, Instant}; +use serde::Serialize; /// Wraps an [ActorMessage](../prelude/trait.ActorMessage.html) to be sent at a later time +#[derive(Serialize)] pub struct DelayedMessage where M: BaseActorMessage + 'static, A: Actor, { pub msg: M, + #[serde(skip)] pub destination: ActorWrapper, pub delay: Duration, + #[serde(skip)] pub started: Instant, } diff --git a/src/message/serialized_message.rs b/src/message/serialized_message.rs index 969862e..2bd34ba 100644 --- a/src/message/serialized_message.rs +++ b/src/message/serialized_message.rs @@ -1,3 +1,4 @@ +use serde::{Deserialize, Serialize}; use crate::message::actor_message::DefaultActorMessage; /// For Remote message handling @@ -8,7 +9,7 @@ use crate::message::actor_message::DefaultActorMessage; /// and it may also include some additional fields to make deserialization easier for end users /// /// [ActorSystem.send_to_address](../prelude/struct.ActorSystem.html#method.send_to_address) uses this object to send serialized messages to Actors -#[derive(Hash)] +#[derive(Hash, Serialize, Deserialize)] pub struct SerializedMessage { pub content: Vec, } diff --git a/src/message/sleep_message.rs b/src/message/sleep_message.rs index db3a457..3d81ee5 100644 --- a/src/message/sleep_message.rs +++ b/src/message/sleep_message.rs @@ -1,8 +1,9 @@ use crate::message::actor_message::DefaultActorMessage; use std::time::Duration; +use serde::{Deserialize, Serialize}; /// Puts an actor to sleep for a specified time -#[derive(Hash)] +#[derive(Hash, Serialize, Deserialize)] pub struct SleepMessage { pub duration: Duration, } diff --git a/src/message/system_stop_message.rs b/src/message/system_stop_message.rs index 40e5696..c0a75bf 100644 --- a/src/message/system_stop_message.rs +++ b/src/message/system_stop_message.rs @@ -1,6 +1,7 @@ +use serde::{Deserialize, Serialize}; use crate::message::actor_message::DefaultActorMessage; -#[derive(Hash)] +#[derive(Hash, Serialize, Deserialize)] pub struct SystemStopMessage {} impl SystemStopMessage { diff --git a/src/net/net_messages.rs b/src/net/net_messages.rs index b123281..d22b9f4 100644 --- a/src/net/net_messages.rs +++ b/src/net/net_messages.rs @@ -2,10 +2,13 @@ use std::hash::{Hash, Hasher}; use std::net::SocketAddr; use io_arc::IoArc; use mio::net::{TcpStream, UdpSocket}; +use serde::Serialize; use crate::prelude::ActorMessage; +#[derive(Serialize)] pub struct AddTcpConnection { pub stream_id: usize, + #[serde(skip)] pub stream: IoArc, pub address: SocketAddr, } @@ -27,6 +30,7 @@ impl AddTcpConnection { } } +#[derive(Serialize)] pub struct RemoveTcpConnection { pub stream_id: usize, } @@ -50,6 +54,7 @@ impl RemoveTcpConnection { impl ActorMessage for RemoveTcpConnection {} +#[derive(Serialize)] pub struct ReceiveTcpMessage { pub stream_id: usize, pub request: Vec, @@ -73,8 +78,10 @@ impl ReceiveTcpMessage { impl ActorMessage for ReceiveTcpMessage {} +#[derive(Serialize)] pub struct AddUdpSocket { pub socket_id: usize, + #[serde(skip)] pub socket: IoArc, } @@ -96,8 +103,10 @@ impl AddUdpSocket { impl ActorMessage for AddUdpSocket {} +#[derive(Serialize)] pub struct ReceiveUdpMessage { pub socket_id: usize, + #[serde(skip)] pub source: SocketAddr, pub request: String, } diff --git a/src/routers/add_actor_message.rs b/src/routers/add_actor_message.rs index 5747cce..06b2036 100644 --- a/src/routers/add_actor_message.rs +++ b/src/routers/add_actor_message.rs @@ -1,13 +1,16 @@ use std::hash::{Hash, Hasher}; +use serde::{Deserialize, Serialize}; use crate::message::actor_message::BaseActorMessage; use crate::prelude::{Actor, ActorWrapper}; /// Adds an Actor to the Router +#[derive(Serialize)] pub struct AddActorMessage where A: Actor, { + #[serde(skip)] pub actor: ActorWrapper, } @@ -16,7 +19,9 @@ where A: Actor, { pub fn new(actor: ActorWrapper) -> Self { - Self { actor } + return Self { + actor + }; } } diff --git a/src/routers/bulk_router_message.rs b/src/routers/bulk_router_message.rs index 6676155..8c98161 100644 --- a/src/routers/bulk_router_message.rs +++ b/src/routers/bulk_router_message.rs @@ -1,7 +1,9 @@ use std::hash::{Hash, Hasher}; +use serde::Serialize; use crate::message::actor_message::BaseActorMessage; /// Wraps multiple [ActorMessage](../prelude/trait.ActorMessage.html) to be sent to a Router +#[derive(Serialize)] pub struct BulkRouterMessage where M: BaseActorMessage + 'static, diff --git a/src/routers/least_message_router.rs b/src/routers/least_message_router.rs index d3b2327..75539f3 100644 --- a/src/routers/least_message_router.rs +++ b/src/routers/least_message_router.rs @@ -33,9 +33,10 @@ where /// use tyra::prelude::*; /// use std::process::exit; /// use std::time::Duration; +/// use serde::Serialize; /// use tyra::router::{LeastMessageRouterFactory, AddActorMessage}; /// // define message -/// #[derive(Hash)] +/// #[derive(Hash, Serialize)] /// struct FooBar {} /// impl ActorMessage for FooBar {} /// diff --git a/src/routers/remove_actor_message.rs b/src/routers/remove_actor_message.rs index 146dac1..afb3198 100644 --- a/src/routers/remove_actor_message.rs +++ b/src/routers/remove_actor_message.rs @@ -1,13 +1,16 @@ use std::hash::{Hash, Hasher}; +use serde::Serialize; use crate::message::actor_message::BaseActorMessage; use crate::prelude::{Actor, ActorWrapper}; /// Removes an Actor from the Router +#[derive(Serialize)] pub struct RemoveActorMessage where A: Actor, { + #[serde(skip)] pub actor: ActorWrapper, } diff --git a/src/routers/round_robin_router.rs b/src/routers/round_robin_router.rs index 0c210b9..47433cb 100644 --- a/src/routers/round_robin_router.rs +++ b/src/routers/round_robin_router.rs @@ -33,10 +33,11 @@ where /// use tyra::prelude::*; /// use std::process::exit; /// use std::time::Duration; +/// use serde::Serialize; /// use tyra::router::{RoundRobinRouterFactory, AddActorMessage}; /// /// // define message -/// #[derive(Hash)] +/// #[derive(Hash, Serialize)] /// struct FooBar {} /// impl ActorMessage for FooBar {} /// diff --git a/src/routers/send_to_all_targets_message.rs b/src/routers/send_to_all_targets_message.rs index a8eb6b3..3108f6d 100644 --- a/src/routers/send_to_all_targets_message.rs +++ b/src/routers/send_to_all_targets_message.rs @@ -1,7 +1,9 @@ use std::hash::{Hash, Hasher}; +use serde::Serialize; use crate::message::actor_message::BaseActorMessage; /// Wraps multiple [ActorMessage](../prelude/trait.ActorMessage.html) to be sent to a Router +#[derive(Serialize)] pub struct SendToAllTargetsMessage where M: BaseActorMessage + 'static, diff --git a/src/routers/sharded_router.rs b/src/routers/sharded_router.rs index 4f1855d..9b82f2c 100644 --- a/src/routers/sharded_router.rs +++ b/src/routers/sharded_router.rs @@ -37,10 +37,11 @@ where /// use tyra::prelude::*; /// use std::process::exit; /// use std::time::Duration; +/// use serde::Serialize; /// use tyra::router::{ShardedRouterFactory, AddActorMessage}; /// /// // define message -/// #[derive(Hash)] +/// #[derive(Hash, Serialize)] /// struct FooBar {} /// impl ActorMessage for FooBar {} /// @@ -160,7 +161,7 @@ where _context: &ActorContext, ) -> Result> { self.hash_ring.add(self.route_to.len()); - self.route_to.push(msg.actor.clone()); + self.route_to.push(msg.actor); self.can_route = true; return Ok(ActorResult::Ok); } diff --git a/src/system/actor_system.rs b/src/system/actor_system.rs index 5af7a2f..4ee9c8a 100644 --- a/src/system/actor_system.rs +++ b/src/system/actor_system.rs @@ -180,11 +180,12 @@ impl ActorSystem { /// /// ```rust /// use std::error::Error; + /// use serde::Serialize; /// use tyra::prelude::{TyraConfig, ActorSystem, ActorFactory, ActorContext, SerializedMessage, Handler, Actor, ActorResult, ActorMessage}; /// /// struct TestActor {} /// - /// #[derive(Hash)] + /// #[derive(Hash, Serialize)] /// struct HelloWorld {} /// impl ActorMessage for HelloWorld {} /// impl Actor for TestActor { diff --git a/src/wrapper/remote_actor_wrapper.rs b/src/wrapper/remote_actor_wrapper.rs index 6aa999a..a8330cc 100644 --- a/src/wrapper/remote_actor_wrapper.rs +++ b/src/wrapper/remote_actor_wrapper.rs @@ -8,6 +8,7 @@ use crate::message::actor_stop_message::ActorStopMessage; use crate::actor::actor_send_error::ActorSendError; use crate::actor::actor::Actor; use crate::actor::handler::Handler; +use crate::prelude::SerializedMessage; use crate::system::system_state::SystemState; #[derive(Serialize, Deserialize, Clone)] @@ -34,7 +35,8 @@ impl RemoteActorWrapper //todo this needs to forward to the NetWorker //the Networker should then forward this to the remote actor system //the remote actor system should then forward the message using system_state - //self.system_state.as_ref().unwrap().send_to_address(address, msg ); + let serialized = bincode::serialize(&msg).unwrap(); + self.system_state.as_ref().unwrap().send_to_address(address, SerializedMessage::new(serialized)); return Ok(()); } From 8e80988a93c8a564bfc87706c4a6b1c6799060b4 Mon Sep 17 00:00:00 2001 From: Bobonium Date: Sun, 7 May 2023 09:27:41 +0200 Subject: [PATCH 21/44] fix delayed messages no longer working after reworking ActorWrapper; make ActorAddress public; add ActorBuilder.get_existing(); improve serialize example --- CHANGELOG.md | 2 + examples/serialize.rs | 10 +-- src/actor/actor_address.rs | 11 +++ src/actor/actor_builder.rs | 106 +++++++++++++++++++++++++++-- src/actor/mod.rs | 1 + src/system/actor_error.rs | 4 ++ src/wrapper/actor_wrapper.rs | 2 +- src/wrapper/local_actor_wrapper.rs | 7 +- 8 files changed, 126 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 047e880..076a843 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,8 @@ - Added `general.signal_graceful_timeout_in_seconds` to config - Added `ActorBuilder.spawn_multiple()` - All routers now support a `SendToAllTargetsMessage` that will forward `M` to all active targets + - Users can now use `ActorBuilder.get_existing(address: ActorAddress)` to get an `ActorWrapper` for an already existing `Actor` + - `ActorBuilder.spawn()` will continue to return the `ActorWrapper` for already existing actors # 1.0.0 diff --git a/examples/serialize.rs b/examples/serialize.rs index f795f79..4ca910b 100644 --- a/examples/serialize.rs +++ b/examples/serialize.rs @@ -36,10 +36,11 @@ impl Handler for RemoteActor { fn handle( &mut self, msg: TestMsg, - _context: &ActorContext, + context: &ActorContext, ) -> Result> { println!("{}", msg.content); - Ok(ActorResult::Ok) + context.system.stop(Duration::from_secs(10)); + Ok(ActorResult::Stop) } } @@ -65,13 +66,8 @@ fn main() { }; let serialized = bincode::serialize(&msg).unwrap(); actor_system.send_to_address(x.get_address(), SerializedMessage::new(serialized)); - let start = Instant::now(); - actor_system.stop(Duration::from_secs(10)); let result = actor_system.await_shutdown(); - let duration = start.elapsed(); - println!("It took {:?} to send stop", duration); - exit(result); } diff --git a/src/actor/actor_address.rs b/src/actor/actor_address.rs index 9c2e329..cf0c574 100644 --- a/src/actor/actor_address.rs +++ b/src/actor/actor_address.rs @@ -7,3 +7,14 @@ pub struct ActorAddress { pub pool: String, pub actor: String, } + +impl ActorAddress { + pub fn new(remote: impl Into, system: impl Into, pool: impl Into, actor: impl Into) -> Self { + return Self { + remote: remote.into(), + system: system.into(), + pool: pool.into(), + actor: actor.into(), + } + } +} \ No newline at end of file diff --git a/src/actor/actor_builder.rs b/src/actor/actor_builder.rs index 376d1a8..ffe3c6f 100644 --- a/src/actor/actor_builder.rs +++ b/src/actor/actor_builder.rs @@ -185,12 +185,7 @@ where where P: ActorFactory + 'static, { - let actor_address = ActorAddress { - actor: name.into(), - system: String::from(self.system.get_name()), - pool: self.actor_config.pool_name.clone(), - remote: String::from("local"), - }; + let actor_address = ActorAddress::new("local", self.system.get_name(), self.actor_config.pool_name.clone(), name); if self.system_state.is_mailbox_active(&actor_address) { return self @@ -247,6 +242,105 @@ where } } + /// Gets the defined [Actor] for the [ActorAddress] on the [ActorSystem] + /// + /// # Returns + /// + /// `Ok(ActorWrapper)` if actor already exists on the system + /// + /// `Err(ActorError)` see [ActorError](../prelude/enum.ActorError.html) for detailed information + /// + /// # Examples + /// + /// ```rust + /// use tyra::prelude::*; + /// use std::error::Error; + /// use std::time::Duration; + /// + /// struct TestActor {} + /// impl TestActor { + /// pub fn new() -> Self { + /// Self {} + /// } + /// } + /// impl Actor for TestActor {} + /// + /// struct TestActorFactory {} + /// impl TestActorFactory { + /// pub fn new() -> Self { + /// Self {} + /// } + /// } + /// impl ActorFactory for TestActorFactory { + /// fn new_actor(&mut self, _context: ActorContext) -> Result> { + /// Ok(TestActor::new()) + /// } + /// } + /// + /// struct BrokenActor {} + /// impl BrokenActor { + /// pub fn new() -> Self { + /// Self {} + /// } + /// } + /// impl Actor for BrokenActor {} + /// + /// struct BrokenActorFactory {} + /// impl BrokenActorFactory { + /// pub fn new() -> Self { + /// Self {} + /// } + /// } + /// impl ActorFactory for BrokenActorFactory { + /// fn new_actor(&mut self, _context: ActorContext) -> Result> { + /// let error = std::io::Error::from_raw_os_error(1337); + /// return Err(Box::new(error)); + /// } + /// } + /// + /// #[ntest::timeout(100000)] + /// fn main() { + /// let mut actor_config = TyraConfig::new().unwrap(); + /// let actor_system = ActorSystem::new(actor_config); + /// + /// let pool_name = "default"; + /// let actor_name = "test"; + /// let address = ActorAddress::new("local", actor_system.get_name(), pool_name, actor_name); + /// + /// //this does not work, because the actor does not exist yet + /// let this_is_not_working :Result, ActorError> = actor_system.builder().get_existing(address.clone()); + /// assert!(this_is_not_working.is_err(), "The BrokenActor existed"); + /// let err = this_is_not_working.err().unwrap(); + /// assert_eq!(err, ActorError::DoesNotExistError, "Error is not correct"); + /// + /// //this works, because there's no actor called `test` yet on the pool + /// let this_works = actor_system.builder().set_pool_name(pool_name).spawn(actor_name, TestActorFactory::new()); + /// assert!(this_works.is_ok(), "The actor could not be spawned"); + /// + /// //this does not work, because the actor type does not match + /// let this_is_not_working :Result, ActorError> = actor_system.builder().get_existing(address.clone()); + /// assert!(this_is_not_working.is_err(), "The BrokenActor existed"); + /// let err = this_is_not_working.err().unwrap(); + /// assert_eq!(err, ActorError::InvalidActorTypeError, "Error is not correct"); + /// + /// //this does work, because the actor type matches + /// let this_works :Result, ActorError> = actor_system.builder().get_existing(address.clone()); + /// assert!(this_works.is_ok(), "The TestActor did not exist"); + /// + /// actor_system.stop(Duration::from_millis(3000)); + /// std::process::exit(actor_system.await_shutdown()); + /// } + /// ``` + pub fn get_existing(&self, actor_address: ActorAddress) -> Result, ActorError> + { + if self.system_state.is_mailbox_active(&actor_address) { + return self + .system_state + .get_actor_ref(actor_address, self.internal_actor_manager.clone(), self.system.clone()); + } + return Err(ActorError::DoesNotExistError); + } + /// Creates N defined [Actor]s on the [ActorSystem] /// /// Requires [ActorFactory] to implement `Clone` diff --git a/src/actor/mod.rs b/src/actor/mod.rs index 13e5eee..4b743ee 100644 --- a/src/actor/mod.rs +++ b/src/actor/mod.rs @@ -14,6 +14,7 @@ pub mod mailbox; pub mod prelude { pub use crate::actor::actor::Actor; + pub use crate::actor::actor_address::ActorAddress; pub use crate::actor::actor_builder::ActorBuilder; pub use crate::actor::actor_factory::ActorFactory; pub use crate::actor::actor_panic_source::ActorPanicSource; diff --git a/src/system/actor_error.rs b/src/system/actor_error.rs index e08c4c5..8a8a0d0 100644 --- a/src/system/actor_error.rs +++ b/src/system/actor_error.rs @@ -17,4 +17,8 @@ pub enum ActorError { /// Triggered by [ActorBuilder.spawn](../prelude/struct.ActorBuilder.html#method.spawn) if the actor can't be spawned, because the thread-pool does not exist #[error("Actor could not be started, because thread-pool does not exist")] ThreadPoolDoesNotExistError, + + /// Triggered by [ActorBuilder.get_existing](../prelude/struct.ActorBuilder.html#method.get_existing) if the provided ActorAddress does not exist + #[error("Actor does not exist")] + DoesNotExistError, } diff --git a/src/wrapper/actor_wrapper.rs b/src/wrapper/actor_wrapper.rs index dad193b..adfb1b9 100644 --- a/src/wrapper/actor_wrapper.rs +++ b/src/wrapper/actor_wrapper.rs @@ -102,7 +102,7 @@ impl ActorWrapper M: BaseActorMessage + 'static, { if self.is_local && self.local.is_some() { - return self.local.as_ref().unwrap().send_after(msg, delay); + return self.local.as_ref().unwrap().send_after(msg, delay, self.clone()); } else { return self.remote.send_after(msg, delay, self.address.clone()); diff --git a/src/wrapper/local_actor_wrapper.rs b/src/wrapper/local_actor_wrapper.rs index e0fb3a4..0530c13 100644 --- a/src/wrapper/local_actor_wrapper.rs +++ b/src/wrapper/local_actor_wrapper.rs @@ -11,6 +11,7 @@ use crate::message::sleep_message::SleepMessage; use crate::actor::actor_send_error::ActorSendError; use crate::actor::actor::Actor; use crate::actor::handler::Handler; +use crate::prelude::ActorWrapper; use crate::system::internal_actor_manager::InternalActorManager; use crate::system::wakeup_manager::WakeupManager; @@ -84,7 +85,7 @@ impl LocalActorWrapper return Ok(()); } - pub fn send_after(&self, msg: M, delay: Duration) -> Result<(), ActorSendError> + pub fn send_after(&self, msg: M, delay: Duration, destination: ActorWrapper) -> Result<(), ActorSendError> where A: Handler + 'static, M: BaseActorMessage + 'static, @@ -93,8 +94,8 @@ impl LocalActorWrapper return Err(ActorSendError::AlreadyStoppedError); } - //self.internal_actor_manager - // .send_after(msg, self.clone(), delay); + self.internal_actor_manager + .send_after(msg, destination, delay); return Ok(()); } From b503d425e4d758bc26e5fbfc9b9c67483dd9bf62 Mon Sep 17 00:00:00 2001 From: Bobonium Date: Sun, 7 May 2023 09:55:58 +0200 Subject: [PATCH 22/44] fix documentation --- src/wrapper/actor_wrapper.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wrapper/actor_wrapper.rs b/src/wrapper/actor_wrapper.rs index adfb1b9..3d4bdd9 100644 --- a/src/wrapper/actor_wrapper.rs +++ b/src/wrapper/actor_wrapper.rs @@ -46,7 +46,7 @@ impl ActorWrapper where A: Actor + UnwindSafe, { - /// Automatically called by the [ActorBuilder.build](../prelude/struct.ActorBuilder.html#method.build) + /// Automatically called by the [ActorBuilder.spawn](../prelude/struct.ActorBuilder.html#method.spawn) pub fn new( mailbox: Mailbox, address: ActorAddress, From f63633dbfdae5ae429741b0bf650dcf0de8cfbaa Mon Sep 17 00:00:00 2001 From: Bobonium Date: Sun, 7 May 2023 09:58:47 +0200 Subject: [PATCH 23/44] remove redundant bool from actor_wrapper --- src/wrapper/actor_wrapper.rs | 58 +++++++++++------------------------- 1 file changed, 18 insertions(+), 40 deletions(-) diff --git a/src/wrapper/actor_wrapper.rs b/src/wrapper/actor_wrapper.rs index 3d4bdd9..2a72240 100644 --- a/src/wrapper/actor_wrapper.rs +++ b/src/wrapper/actor_wrapper.rs @@ -23,7 +23,6 @@ pub struct ActorWrapper where A: Actor, { - is_local: bool, address: ActorAddress, remote: RemoteActorWrapper, #[serde(skip)] @@ -54,11 +53,9 @@ impl ActorWrapper internal_actor_manager: InternalActorManager, system_state: SystemState, ) -> Self { - let is_local = true; let local = Some(LocalActorWrapper::new(mailbox, wakeup_manager, internal_actor_manager)); let remote = RemoteActorWrapper::new(system_state); Self { - is_local, address, remote, local, @@ -73,12 +70,10 @@ impl ActorWrapper A: Handler, M: BaseActorMessage + 'static, { - if self.is_local && self.local.is_some() { + if self.local.is_some() { return self.local.as_ref().unwrap().send(msg, self.address.clone()); } - else { - return self.remote.send(msg, &self.address); - } + return self.remote.send(msg, &self.address); } /// Same as send, but with a user defined timeout @@ -87,12 +82,10 @@ impl ActorWrapper A: Handler, M: BaseActorMessage + 'static, { - if self.is_local && self.local.is_some() { + if self.local.is_some() { return self.local.as_ref().unwrap().send_timeout(msg, timeout, self.address.clone()); } - else { - return self.remote.send_timeout(msg, timeout, self.address.clone()); - } + return self.remote.send_timeout(msg, timeout, self.address.clone()); } /// Sends a message to the actor after a specified delay @@ -101,32 +94,26 @@ impl ActorWrapper A: Handler + 'static, M: BaseActorMessage + 'static, { - if self.is_local && self.local.is_some() { + if self.local.is_some() { return self.local.as_ref().unwrap().send_after(msg, delay, self.clone()); } - else { - return self.remote.send_after(msg, delay, self.address.clone()); - } + return self.remote.send_after(msg, delay, self.address.clone()); } /// Tells the actor to stop accepting message and to shutdown after all existing messages have been processed pub fn stop(&self) -> Result<(), ActorSendError> { - if self.is_local && self.local.is_some() { + if self.local.is_some() { return self.local.as_ref().unwrap().stop(self.address.clone()); } - else { - return self.remote.stop(); - } + return self.remote.stop(); } /// Tells the actor to sleep for the specified duration pub fn sleep(&self, duration: Duration) -> Result<(), ActorSendError> { - if self.is_local && self.local.is_some() { + if self.local.is_some() { return self.local.as_ref().unwrap().send(SleepMessage{ duration}, self.address.clone()); } - else { - return self.remote.send(SleepMessage{ duration}, &self.address); - } + return self.remote.send(SleepMessage{ duration}, &self.address); } /// Returns a reference to the address of the actor @@ -137,42 +124,34 @@ impl ActorWrapper /// Returns the current mailbox size pub fn get_mailbox_size(&self) -> usize { - if self.is_local && self.local.is_some() { + if self.local.is_some() { return self.local.as_ref().unwrap().get_mailbox_size(); } - else { - return self.remote.get_mailbox_size(); - } + return self.remote.get_mailbox_size(); } /// Returns true if an actor is no longer accepting messages pub fn is_mailbox_stopped(&self) -> bool { - if self.is_local && self.local.is_some() { + if self.local.is_some() { return self.local.as_ref().unwrap().is_mailbox_stopped(); } - else { - return self.remote.is_mailbox_stopped(); - } + return self.remote.is_mailbox_stopped(); } /// Returns true if an actor has been completely stopped after processing all messages that are still within the queue pub fn is_stopped(&self) -> bool { - if self.is_local && self.local.is_some() { + if self.local.is_some() { return self.local.as_ref().unwrap().is_stopped(); } - else { - return self.remote.is_stopped(); - } + return self.remote.is_stopped(); } /// Blocks until the actor has been stopped pub fn wait_for_stop(&self) { - if self.is_local && self.local.is_some() { + if self.local.is_some() { return self.local.as_ref().unwrap().wait_for_stop(self.address.clone()); } - else { - return self.remote.wait_for_stop(); - } + return self.remote.wait_for_stop(); } } @@ -182,7 +161,6 @@ impl Clone for ActorWrapper { fn clone(&self) -> Self { Self { - is_local: self.is_local.clone(), remote: self.remote.clone(), local: self.local.clone(), address: self.address.clone(), From 596f30a3e082f76c06be8aee04b7ccd0315bfb18 Mon Sep 17 00:00:00 2001 From: Bobonium Date: Sun, 7 May 2023 10:05:36 +0200 Subject: [PATCH 24/44] get rid of warnings --- examples/serialize.rs | 2 +- src/actor/actor_builder.rs | 4 ++-- src/routers/add_actor_message.rs | 2 +- src/system/system_state.rs | 3 +-- src/wrapper/actor_wrapper.rs | 9 +++------ src/wrapper/local_actor_wrapper.rs | 2 -- src/wrapper/remote_actor_wrapper.rs | 12 ++++-------- 7 files changed, 12 insertions(+), 22 deletions(-) diff --git a/examples/serialize.rs b/examples/serialize.rs index 4ca910b..79268b8 100644 --- a/examples/serialize.rs +++ b/examples/serialize.rs @@ -1,7 +1,7 @@ use serde::{Deserialize, Serialize}; use std::error::Error; use std::process::exit; -use std::time::{Duration, Instant}; +use std::time::Duration; use tyra::prelude::*; #[derive(Serialize, Deserialize, PartialEq, Debug, Clone, Hash)] diff --git a/src/actor/actor_builder.rs b/src/actor/actor_builder.rs index ffe3c6f..796c7bc 100644 --- a/src/actor/actor_builder.rs +++ b/src/actor/actor_builder.rs @@ -190,7 +190,7 @@ where if self.system_state.is_mailbox_active(&actor_address) { return self .system_state - .get_actor_ref(actor_address, self.internal_actor_manager.clone(), self.system.clone()); + .get_actor_ref(actor_address, self.internal_actor_manager.clone()); } let result = self.system_state.increase_pool_actor_count(&actor_address); @@ -336,7 +336,7 @@ where if self.system_state.is_mailbox_active(&actor_address) { return self .system_state - .get_actor_ref(actor_address, self.internal_actor_manager.clone(), self.system.clone()); + .get_actor_ref(actor_address, self.internal_actor_manager.clone()); } return Err(ActorError::DoesNotExistError); } diff --git a/src/routers/add_actor_message.rs b/src/routers/add_actor_message.rs index 06b2036..e965e4d 100644 --- a/src/routers/add_actor_message.rs +++ b/src/routers/add_actor_message.rs @@ -1,5 +1,5 @@ use std::hash::{Hash, Hasher}; -use serde::{Deserialize, Serialize}; +use serde::{Serialize}; use crate::message::actor_message::BaseActorMessage; use crate::prelude::{Actor, ActorWrapper}; diff --git a/src/system/system_state.rs b/src/system/system_state.rs index c2d0797..5fede8a 100644 --- a/src/system/system_state.rs +++ b/src/system/system_state.rs @@ -1,7 +1,7 @@ use crate::actor::actor_address::ActorAddress; use crate::actor::mailbox::{BaseMailbox, Mailbox}; use crate::message::serialized_message::SerializedMessage; -use crate::prelude::{ActorSystem, ActorWrapper, Handler}; +use crate::prelude::{ActorWrapper, Handler}; use crate::system::actor_error::ActorError; use crate::system::internal_actor_manager::InternalActorManager; use crate::system::wakeup_manager::WakeupManager; @@ -198,7 +198,6 @@ impl SystemState { &self, address: ActorAddress, internal_actor_manager: InternalActorManager, - system: ActorSystem, ) -> Result, ActorError> where A: Handler + 'static, diff --git a/src/wrapper/actor_wrapper.rs b/src/wrapper/actor_wrapper.rs index 2a72240..ac95574 100644 --- a/src/wrapper/actor_wrapper.rs +++ b/src/wrapper/actor_wrapper.rs @@ -1,17 +1,14 @@ -use std::fmt::{Debug, Formatter, Pointer}; +use std::fmt::{Debug, Formatter}; use std::hash::{Hash, Hasher}; -use std::marker::PhantomData; use std::panic::UnwindSafe; use std::time::Duration; use serde::{Deserialize, Serialize}; use crate::actor::actor_address::ActorAddress; use crate::actor::mailbox::Mailbox; use crate::message::actor_message::BaseActorMessage; -use crate::message::sleep_message::SleepMessage; use crate::actor::actor_send_error::ActorSendError; use crate::actor::actor::Actor; use crate::actor::handler::Handler; -use crate::prelude::ActorSystem; use crate::system::internal_actor_manager::InternalActorManager; use crate::system::system_state::SystemState; use crate::system::wakeup_manager::WakeupManager; @@ -111,9 +108,9 @@ impl ActorWrapper /// Tells the actor to sleep for the specified duration pub fn sleep(&self, duration: Duration) -> Result<(), ActorSendError> { if self.local.is_some() { - return self.local.as_ref().unwrap().send(SleepMessage{ duration}, self.address.clone()); + return self.local.as_ref().unwrap().sleep(duration, self.address.clone()); } - return self.remote.send(SleepMessage{ duration}, &self.address); + return self.remote.sleep(duration, self.address.clone()); } /// Returns a reference to the address of the actor diff --git a/src/wrapper/local_actor_wrapper.rs b/src/wrapper/local_actor_wrapper.rs index 0530c13..8e27f9d 100644 --- a/src/wrapper/local_actor_wrapper.rs +++ b/src/wrapper/local_actor_wrapper.rs @@ -1,8 +1,6 @@ -use std::marker::PhantomData; use std::panic::UnwindSafe; use std::thread::sleep; use std::time::Duration; -use serde::{Deserialize, Serialize}; use crate::actor::actor_address::ActorAddress; use crate::actor::mailbox::Mailbox; use crate::message::actor_message::BaseActorMessage; diff --git a/src/wrapper/remote_actor_wrapper.rs b/src/wrapper/remote_actor_wrapper.rs index a8330cc..9ad026d 100644 --- a/src/wrapper/remote_actor_wrapper.rs +++ b/src/wrapper/remote_actor_wrapper.rs @@ -1,13 +1,9 @@ use std::hash::{Hash, Hasher}; -use std::panic::UnwindSafe; use std::time::Duration; use serde::{Deserialize, Serialize}; use crate::actor::actor_address::ActorAddress; use crate::message::actor_message::BaseActorMessage; -use crate::message::actor_stop_message::ActorStopMessage; use crate::actor::actor_send_error::ActorSendError; -use crate::actor::actor::Actor; -use crate::actor::handler::Handler; use crate::prelude::SerializedMessage; use crate::system::system_state::SystemState; @@ -40,14 +36,14 @@ impl RemoteActorWrapper return Ok(()); } - pub fn send_timeout(&self, msg: M, timeout: Duration, address: ActorAddress) -> Result<(), ActorSendError> + pub fn send_timeout(&self, _msg: M, _timeout: Duration, _address: ActorAddress) -> Result<(), ActorSendError> where M: BaseActorMessage + 'static, { return Ok(()); } - pub fn send_after(&self, msg: M, delay: Duration, address: ActorAddress) -> Result<(), ActorSendError> + pub fn send_after(&self, _msg: M, _delay: Duration, _address: ActorAddress) -> Result<(), ActorSendError> where M: BaseActorMessage + 'static, { @@ -58,7 +54,7 @@ impl RemoteActorWrapper return Ok(()); } - pub fn sleep(&self, duration: Duration, address: ActorAddress) -> Result<(), ActorSendError> { + pub fn sleep(&self, _duration: Duration, _address: ActorAddress) -> Result<(), ActorSendError> { return Ok(()); } @@ -81,7 +77,7 @@ impl RemoteActorWrapper impl Hash for RemoteActorWrapper { - fn hash(&self, state: &mut H) { + fn hash(&self, _state: &mut H) { return; } } \ No newline at end of file From 4f8a09053179af614ae2e654870fe148ac3c34e4 Mon Sep 17 00:00:00 2001 From: Bobonium Date: Sun, 7 May 2023 10:06:01 +0200 Subject: [PATCH 25/44] cargo fmt --- examples/benchmark_bulk_router.rs | 2 +- examples/benchmark_router_round_robin.rs | 2 +- examples/benchmark_single_actor.rs | 2 +- ...nchmark_single_actor_process_after_send.rs | 2 +- ..._actor_process_after_send_single_thread.rs | 2 +- .../benchmark_single_actor_single_thread.rs | 2 +- examples/net.rs | 24 +- examples/quickstart.rs | 2 +- src/actor/actor_address.rs | 11 +- src/actor/actor_builder.rs | 31 ++- src/actor/context.rs | 1 - src/config/tyra_config.rs | 3 +- src/lib.rs | 4 +- src/message/actor_init_message.rs | 2 +- src/message/actor_message.rs | 18 +- src/message/actor_stop_message.rs | 2 +- src/message/bulk_actor_message.rs | 2 +- src/message/delayed_message.rs | 12 +- src/message/serialized_message.rs | 2 +- src/message/sleep_message.rs | 2 +- src/message/system_stop_message.rs | 2 +- src/net/mod.rs | 12 +- src/net/net_config.rs | 2 +- src/net/net_manager.rs | 222 ++++++++++++------ src/net/net_messages.rs | 37 +-- src/net/net_worker.rs | 62 +++-- src/routers/add_actor_message.rs | 11 +- src/routers/bulk_router_message.rs | 8 +- src/routers/least_message_router.rs | 59 +++-- src/routers/mod.rs | 11 +- src/routers/remove_actor_message.rs | 9 +- src/routers/round_robin_router.rs | 41 ++-- src/routers/send_to_all_targets_message.rs | 18 +- src/routers/sharded_router.rs | 29 ++- src/system/actor_system.rs | 5 +- src/system/delay_actor.rs | 2 +- src/system/internal_actor_manager.rs | 16 +- src/system/system_state.rs | 8 +- src/wrapper/actor_wrapper.rs | 87 ++++--- src/wrapper/local_actor_wrapper.rs | 63 ++--- src/wrapper/mod.rs | 4 +- src/wrapper/remote_actor_wrapper.rs | 55 +++-- 42 files changed, 524 insertions(+), 367 deletions(-) diff --git a/examples/benchmark_bulk_router.rs b/examples/benchmark_bulk_router.rs index 9a7239e..7af2413 100644 --- a/examples/benchmark_bulk_router.rs +++ b/examples/benchmark_bulk_router.rs @@ -1,8 +1,8 @@ +use serde::Serialize; use std::error::Error; use std::process::exit; use std::thread::sleep; use std::time::{Duration, Instant}; -use serde::Serialize; use tyra::prelude::*; use tyra::router::{AddActorMessage, BulkRouterMessage, RoundRobinRouterFactory}; diff --git a/examples/benchmark_router_round_robin.rs b/examples/benchmark_router_round_robin.rs index 7024fd5..20f09ed 100644 --- a/examples/benchmark_router_round_robin.rs +++ b/examples/benchmark_router_round_robin.rs @@ -1,7 +1,7 @@ +use serde::Serialize; use std::error::Error; use std::process::exit; use std::time::{Duration, Instant}; -use serde::Serialize; use tyra::prelude::*; use tyra::router::{AddActorMessage, RoundRobinRouterFactory}; diff --git a/examples/benchmark_single_actor.rs b/examples/benchmark_single_actor.rs index df0316b..7244b37 100644 --- a/examples/benchmark_single_actor.rs +++ b/examples/benchmark_single_actor.rs @@ -1,7 +1,7 @@ +use serde::Serialize; use std::error::Error; use std::process::exit; use std::time::{Duration, Instant}; -use serde::Serialize; use tyra::prelude::*; #[derive(Hash, Serialize)] diff --git a/examples/benchmark_single_actor_process_after_send.rs b/examples/benchmark_single_actor_process_after_send.rs index bdd760a..daed727 100644 --- a/examples/benchmark_single_actor_process_after_send.rs +++ b/examples/benchmark_single_actor_process_after_send.rs @@ -1,7 +1,7 @@ +use serde::Serialize; use std::error::Error; use std::process::exit; use std::time::{Duration, Instant}; -use serde::Serialize; use tyra::prelude::*; #[derive(Hash, Serialize)] diff --git a/examples/benchmark_single_actor_process_after_send_single_thread.rs b/examples/benchmark_single_actor_process_after_send_single_thread.rs index 9b6ba19..406e6a2 100644 --- a/examples/benchmark_single_actor_process_after_send_single_thread.rs +++ b/examples/benchmark_single_actor_process_after_send_single_thread.rs @@ -1,7 +1,7 @@ +use serde::Serialize; use std::error::Error; use std::process::exit; use std::time::{Duration, Instant}; -use serde::Serialize; use tyra::prelude::*; #[derive(Hash, Serialize)] diff --git a/examples/benchmark_single_actor_single_thread.rs b/examples/benchmark_single_actor_single_thread.rs index 4089701..0ee64e9 100644 --- a/examples/benchmark_single_actor_single_thread.rs +++ b/examples/benchmark_single_actor_single_thread.rs @@ -1,7 +1,7 @@ +use serde::Serialize; use std::error::Error; use std::process::exit; use std::time::{Duration, Instant}; -use serde::Serialize; use tyra::prelude::*; #[derive(Hash, Serialize)] diff --git a/examples/net.rs b/examples/net.rs index c8cad33..35373ec 100644 --- a/examples/net.rs +++ b/examples/net.rs @@ -1,11 +1,17 @@ use std::time::Duration; -use tyra::prelude::{ActorSystem, NetConfig, NetManagerFactory, NetProtocol, NetWorkerFactory, ThreadPoolConfig, TyraConfig}; +use tyra::prelude::{ + ActorSystem, NetConfig, NetManagerFactory, NetProtocol, NetWorkerFactory, ThreadPoolConfig, + TyraConfig, +}; fn main() { // generate config let mut actor_config = TyraConfig::new().unwrap(); let cluster = ThreadPoolConfig::new(22, 4, 4, 1.00); - actor_config.thread_pool.config.insert(String::from("mio"), cluster); + actor_config + .thread_pool + .config + .insert(String::from("mio"), cluster); // start system with config let actor_system = ActorSystem::new(actor_config); // create actor on the system @@ -13,15 +19,21 @@ fn main() { net_configs.push(NetConfig::new(NetProtocol::UDP, "0.0.0.0", 2023)); net_configs.push(NetConfig::new(NetProtocol::TCP, "0.0.0.0", 2022)); - let worker_factory = NetWorkerFactory::new(); let _actor = actor_system .builder() .set_pool_name("mio") - .spawn("test", NetManagerFactory::new(net_configs, Duration::from_secs(10), Duration::from_secs(3), worker_factory, 3)) + .spawn( + "test", + NetManagerFactory::new( + net_configs, + Duration::from_secs(10), + Duration::from_secs(3), + worker_factory, + 3, + ), + ) .unwrap(); - std::process::exit(actor_system.await_shutdown()); } - diff --git a/examples/quickstart.rs b/examples/quickstart.rs index 8e37d38..5dd4dbc 100644 --- a/examples/quickstart.rs +++ b/examples/quickstart.rs @@ -1,6 +1,6 @@ +use serde::Serialize; use std::error::Error; use std::time::Duration; -use serde::Serialize; use tyra::prelude::*; // define an `ActorMessage` that can be sent to `Actors` that implement the corresponding `Handler` diff --git a/src/actor/actor_address.rs b/src/actor/actor_address.rs index cf0c574..7fb3055 100644 --- a/src/actor/actor_address.rs +++ b/src/actor/actor_address.rs @@ -9,12 +9,17 @@ pub struct ActorAddress { } impl ActorAddress { - pub fn new(remote: impl Into, system: impl Into, pool: impl Into, actor: impl Into) -> Self { + pub fn new( + remote: impl Into, + system: impl Into, + pool: impl Into, + actor: impl Into, + ) -> Self { return Self { remote: remote.into(), system: system.into(), pool: pool.into(), actor: actor.into(), - } + }; } -} \ No newline at end of file +} diff --git a/src/actor/actor_builder.rs b/src/actor/actor_builder.rs index 796c7bc..f5c300f 100644 --- a/src/actor/actor_builder.rs +++ b/src/actor/actor_builder.rs @@ -4,7 +4,7 @@ use crate::actor::actor_factory::ActorFactory; use crate::actor::executor::{Executor, ExecutorTrait}; use crate::actor::mailbox::Mailbox; use crate::config::tyra_config::DEFAULT_POOL; -use crate::prelude::{Actor, Handler, ActorWrapper, SerializedMessage}; +use crate::prelude::{Actor, ActorWrapper, Handler, SerializedMessage}; use crate::system::actor_error::ActorError; use crate::system::actor_system::ActorSystem; use crate::system::internal_actor_manager::InternalActorManager; @@ -185,7 +185,12 @@ where where P: ActorFactory + 'static, { - let actor_address = ActorAddress::new("local", self.system.get_name(), self.actor_config.pool_name.clone(), name); + let actor_address = ActorAddress::new( + "local", + self.system.get_name(), + self.actor_config.pool_name.clone(), + name, + ); if self.system_state.is_mailbox_active(&actor_address) { return self @@ -230,8 +235,10 @@ where match actor_handler { Ok(a) => { - self.system_state.add_mailbox(actor_address.clone(), mailbox); - self.wakeup_manager.add_inactive_actor(a.get_address(), Arc::new(RwLock::new(a))); + self.system_state + .add_mailbox(actor_address.clone(), mailbox); + self.wakeup_manager + .add_inactive_actor(a.get_address(), Arc::new(RwLock::new(a))); self.existing.insert(actor_address, actor_ref.clone()); return Ok(actor_ref); } @@ -331,8 +338,7 @@ where /// std::process::exit(actor_system.await_shutdown()); /// } /// ``` - pub fn get_existing(&self, actor_address: ActorAddress) -> Result, ActorError> - { + pub fn get_existing(&self, actor_address: ActorAddress) -> Result, ActorError> { if self.system_state.is_mailbox_active(&actor_address) { return self .system_state @@ -407,9 +413,14 @@ where /// std::process::exit(actor_system.await_shutdown()); /// } /// ``` - pub fn spawn_multiple

(&self, name: impl Into + Clone + std::fmt::Display, props: P, spawn_count: usize) -> Result>, ActorError> - where - P: ActorFactory + 'static + Clone, + pub fn spawn_multiple

( + &self, + name: impl Into + Clone + std::fmt::Display, + props: P, + spawn_count: usize, + ) -> Result>, ActorError> + where + P: ActorFactory + 'static + Clone, { let mut to_return = Vec::new(); for i in 0..spawn_count { @@ -430,6 +441,4 @@ where return Ok(to_return); } - - } diff --git a/src/actor/context.rs b/src/actor/context.rs index 591b033..a021c55 100644 --- a/src/actor/context.rs +++ b/src/actor/context.rs @@ -1,4 +1,3 @@ - use crate::prelude::{Actor, ActorWrapper}; use crate::system::actor_system::ActorSystem; use std::panic::UnwindSafe; diff --git a/src/config/tyra_config.rs b/src/config/tyra_config.rs index 39cd019..cf34275 100644 --- a/src/config/tyra_config.rs +++ b/src/config/tyra_config.rs @@ -1,6 +1,6 @@ -use std::path::Path; use crate::config::global_config::GeneralConfig; use crate::config::pool_config::PoolConfig; +use std::path::Path; use config::{Config, ConfigError, File, FileFormat}; use serde::{Deserialize, Serialize}; @@ -34,7 +34,6 @@ impl TyraConfig { /// config.general.name = String::from("HelloWorld"); /// ``` pub fn new() -> Result { - let default: &str = std::include_str!("default.toml"); let mut config = Config::builder(); diff --git a/src/lib.rs b/src/lib.rs index 636c4a6..a778961 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -110,9 +110,9 @@ mod actor; mod config; mod message; +mod net; mod routers; mod system; -mod net; mod wrapper; /// core components @@ -120,8 +120,8 @@ pub mod prelude { pub use crate::actor::prelude::*; pub use crate::config::prelude::*; pub use crate::message::prelude::*; - pub use crate::system::prelude::*; pub use crate::net::prelude::*; + pub use crate::system::prelude::*; pub use crate::wrapper::prelude::*; } diff --git a/src/message/actor_init_message.rs b/src/message/actor_init_message.rs index 5542f2a..ff1ec83 100644 --- a/src/message/actor_init_message.rs +++ b/src/message/actor_init_message.rs @@ -1,5 +1,5 @@ -use serde::{Deserialize, Serialize}; use crate::prelude::ActorMessage; +use serde::{Deserialize, Serialize}; /// Can be implemented by an Actor through Handler to be used to init an Actor #[derive(Hash, Serialize, Deserialize)] diff --git a/src/message/actor_message.rs b/src/message/actor_message.rs index 72ce8ea..91841b4 100644 --- a/src/message/actor_message.rs +++ b/src/message/actor_message.rs @@ -1,24 +1,19 @@ +use serde::Serialize; use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; -use serde::Serialize; /// This trait is used internally by the `ActorSystem` and builds the base for all messaging /// It's automatically implemented by the `ActorMessage` trait that should be used /// /// It is used by Messages defined in the system /// All messages that use this trait directly should also implement a dynamic `Handler` that applies to any `Actor` -pub trait BaseActorMessage: Send + Sync + Hash + Serialize { -} +pub trait BaseActorMessage: Send + Sync + Hash + Serialize {} /// This trait is used by Messages defined by the system /// All messages that use this trait should also implement a dynamic `Handler` that applies to any `Actor` -pub trait DefaultActorMessage: Send + Sync + Hash + Serialize { -} +pub trait DefaultActorMessage: Send + Sync + Hash + Serialize {} -impl BaseActorMessage for A -where - A: DefaultActorMessage -{} +impl BaseActorMessage for A where A: DefaultActorMessage {} /// Core trait to define Messages /// @@ -44,7 +39,4 @@ pub trait ActorMessage: Send + Sync + Hash + Serialize { } /// this should be `BaseActorMessage` but it's currently not possible because of https://github.com/rust-lang/rust/issues/20400 -impl DefaultActorMessage for A - where - A: ActorMessage + Serialize -{} \ No newline at end of file +impl DefaultActorMessage for A where A: ActorMessage + Serialize {} diff --git a/src/message/actor_stop_message.rs b/src/message/actor_stop_message.rs index 747fb29..ae09a65 100644 --- a/src/message/actor_stop_message.rs +++ b/src/message/actor_stop_message.rs @@ -1,5 +1,5 @@ +use crate::message::actor_message::DefaultActorMessage; use serde::{Deserialize, Serialize}; -use crate::message::actor_message::{DefaultActorMessage}; #[derive(Hash, Serialize, Deserialize)] pub struct ActorStopMessage {} diff --git a/src/message/bulk_actor_message.rs b/src/message/bulk_actor_message.rs index 2183fa7..4c86d49 100644 --- a/src/message/bulk_actor_message.rs +++ b/src/message/bulk_actor_message.rs @@ -1,7 +1,7 @@ -use serde::Serialize; /// Bulk Actor Message, that can wrap and send multiple [ActorMessage](../prelude/trait.ActorMessage.html) at once /// use crate::message::actor_message::BaseActorMessage; +use serde::Serialize; /// Wraps multiple [ActorMessage](../prelude/trait.ActorMessage.html) to be sent to an Actor #[derive(Hash, Serialize)] diff --git a/src/message/delayed_message.rs b/src/message/delayed_message.rs index 4af20c7..9980dcd 100644 --- a/src/message/delayed_message.rs +++ b/src/message/delayed_message.rs @@ -1,8 +1,8 @@ -use std::hash::{Hash, Hasher}; use crate::message::actor_message::BaseActorMessage; use crate::prelude::{Actor, ActorMessage, ActorWrapper}; -use std::time::{Duration, Instant}; use serde::Serialize; +use std::hash::{Hash, Hasher}; +use std::time::{Duration, Instant}; /// Wraps an [ActorMessage](../prelude/trait.ActorMessage.html) to be sent at a later time #[derive(Serialize)] @@ -43,11 +43,11 @@ where } impl Hash for DelayedMessage - where - M: BaseActorMessage + 'static, - A: Actor, +where + M: BaseActorMessage + 'static, + A: Actor, { fn hash(&self, state: &mut H) { self.msg.hash(state); } -} \ No newline at end of file +} diff --git a/src/message/serialized_message.rs b/src/message/serialized_message.rs index 2bd34ba..ac19f88 100644 --- a/src/message/serialized_message.rs +++ b/src/message/serialized_message.rs @@ -1,5 +1,5 @@ -use serde::{Deserialize, Serialize}; use crate::message::actor_message::DefaultActorMessage; +use serde::{Deserialize, Serialize}; /// For Remote message handling /// diff --git a/src/message/sleep_message.rs b/src/message/sleep_message.rs index 3d81ee5..224a316 100644 --- a/src/message/sleep_message.rs +++ b/src/message/sleep_message.rs @@ -1,6 +1,6 @@ use crate::message::actor_message::DefaultActorMessage; -use std::time::Duration; use serde::{Deserialize, Serialize}; +use std::time::Duration; /// Puts an actor to sleep for a specified time #[derive(Hash, Serialize, Deserialize)] diff --git a/src/message/system_stop_message.rs b/src/message/system_stop_message.rs index c0a75bf..5516a4e 100644 --- a/src/message/system_stop_message.rs +++ b/src/message/system_stop_message.rs @@ -1,5 +1,5 @@ -use serde::{Deserialize, Serialize}; use crate::message::actor_message::DefaultActorMessage; +use serde::{Deserialize, Serialize}; #[derive(Hash, Serialize, Deserialize)] pub struct SystemStopMessage {} diff --git a/src/net/mod.rs b/src/net/mod.rs index bab66bb..60bed6f 100644 --- a/src/net/mod.rs +++ b/src/net/mod.rs @@ -1,15 +1,13 @@ -mod net_manager; mod net_config; -mod net_worker; +mod net_manager; pub mod net_messages; +mod net_worker; pub mod prelude { - pub use crate::net::net_manager::NetManagerFactory; - pub use crate::net::net_manager::NetManager; pub use crate::net::net_config::NetConfig; pub use crate::net::net_config::NetProtocol; - pub use crate::net::net_worker::NetWorkerFactory; + pub use crate::net::net_manager::NetManager; + pub use crate::net::net_manager::NetManagerFactory; pub use crate::net::net_worker::NetWorker; - - + pub use crate::net::net_worker::NetWorkerFactory; } diff --git a/src/net/net_config.rs b/src/net/net_config.rs index b2774b2..a60263f 100644 --- a/src/net/net_config.rs +++ b/src/net/net_config.rs @@ -19,4 +19,4 @@ impl NetConfig { port, } } -} \ No newline at end of file +} diff --git a/src/net/net_manager.rs b/src/net/net_manager.rs index 20bf70f..764765d 100644 --- a/src/net/net_manager.rs +++ b/src/net/net_manager.rs @@ -1,26 +1,36 @@ +use crate::net::net_messages::{ + AddTcpConnection, AddUdpSocket, ReceiveTcpMessage, ReceiveUdpMessage, RemoveTcpConnection, +}; +use crate::prelude::{ + Actor, ActorContext, ActorFactory, ActorInitMessage, ActorResult, ActorWrapper, Handler, + NetConfig, NetProtocol, +}; +use crate::router::{AddActorMessage, ShardedRouter, ShardedRouterFactory}; +use io_arc::IoArc; +use log::{debug, error, warn}; +use mio::event::Source; +use mio::net::{TcpListener, TcpStream, UdpSocket}; +use mio::{Events, Interest, Poll, Token}; use std::cmp::min; use std::collections::HashMap; use std::error::Error; use std::io::{BufRead, BufReader}; use std::marker::PhantomData; use std::net::Shutdown; -use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; use std::thread; use std::thread::sleep; use std::time::{Duration, Instant}; -use io_arc::IoArc; -use log::{debug, error, warn}; -use mio::net::{TcpListener, TcpStream, UdpSocket}; -use mio::{Events, Interest, Poll, Token}; -use mio::event::Source; -use crate::net::net_messages::{AddTcpConnection, AddUdpSocket, ReceiveTcpMessage, ReceiveUdpMessage, RemoveTcpConnection}; -use crate::prelude::{Actor, ActorContext, ActorFactory, ActorInitMessage, ActorResult, ActorWrapper, Handler, NetConfig, NetProtocol}; -use crate::router::{AddActorMessage, ShardedRouter, ShardedRouterFactory}; pub struct NetManager - where - T: Handler + Handler + Handler + Handler + Handler + 'static, +where + T: Handler + + Handler + + Handler + + Handler + + Handler + + 'static, { graceful_shutdown_time_in_seconds: Duration, on_stop_udp_timeout: Duration, @@ -29,24 +39,47 @@ pub struct NetManager net_configs: Vec, is_stopping: Arc, is_stopped: Arc, - - } impl NetManager - where - - T: Handler + Handler + Handler + Handler + Handler + 'static, +where + T: Handler + + Handler + + Handler + + Handler + + Handler + + 'static, { - pub fn new(context: ActorContext, net_configs: Vec, graceful_shutdown_time_in_seconds: Duration, on_stop_udp_timeout: Duration, worker_factory: F, max_worker_count: usize) -> Self - where F: ActorFactory + Clone + 'static, { - + pub fn new( + context: ActorContext, + net_configs: Vec, + graceful_shutdown_time_in_seconds: Duration, + on_stop_udp_timeout: Duration, + worker_factory: F, + max_worker_count: usize, + ) -> Self + where + F: ActorFactory + Clone + 'static, + { let pool_name = &context.actor_ref.get_address().pool; - let router = context.system.builder().set_pool_name(pool_name).spawn("net-least-message", ShardedRouterFactory::new( false, false)).unwrap(); - let max_pool_count = context.system.get_available_actor_count_for_pool(pool_name).unwrap(); + let router = context + .system + .builder() + .set_pool_name(pool_name) + .spawn("net-least-message", ShardedRouterFactory::new(false, false)) + .unwrap(); + let max_pool_count = context + .system + .get_available_actor_count_for_pool(pool_name) + .unwrap(); let worker_count = min(max_worker_count, max_pool_count); - let workers = context.system.builder().set_pool_name(pool_name).spawn_multiple("net-worker", worker_factory.clone(), worker_count).unwrap(); + let workers = context + .system + .builder() + .set_pool_name(pool_name) + .spawn_multiple("net-worker", worker_factory.clone(), worker_count) + .unwrap(); for worker in &workers { router.send(AddActorMessage::new(worker.clone())).unwrap(); } @@ -66,14 +99,18 @@ impl NetManager } } impl Actor for NetManager - where - T: Handler + Handler + Handler + Handler + Handler + 'static, +where + T: Handler + + Handler + + Handler + + Handler + + Handler + + 'static, { fn pre_stop(&mut self, _context: &ActorContext) { let iterations = 10; let iterate_graceful_stop = self.graceful_shutdown_time_in_seconds / iterations; - sleep(iterate_graceful_stop); self.is_stopping.store(true, Ordering::Relaxed); @@ -100,7 +137,6 @@ impl Actor for NetManager return; } sleep(iterate_graceful_stop); - } } fn post_stop(&mut self, _context: &ActorContext) { @@ -136,7 +172,12 @@ impl Actor for NetManager pub struct NetManagerFactory where F: ActorFactory + Clone + 'static, - T: Handler + Handler + Handler + Handler + Handler + 'static, + T: Handler + + Handler + + Handler + + Handler + + Handler + + 'static, { net_configs: Vec, graceful_shutdown_time_in_seconds: Duration, @@ -149,10 +190,20 @@ where impl NetManagerFactory where F: ActorFactory + Clone + 'static, - T: Handler + Handler + Handler + Handler + Handler + 'static, - + T: Handler + + Handler + + Handler + + Handler + + Handler + + 'static, { - pub fn new(net_configs: Vec, graceful_shutdown_time_in_seconds: Duration, on_stop_udp_timeout: Duration, worker_factory: F, max_worker_count: usize) -> Self { + pub fn new( + net_configs: Vec, + graceful_shutdown_time_in_seconds: Duration, + on_stop_udp_timeout: Duration, + worker_factory: F, + max_worker_count: usize, + ) -> Self { return Self { net_configs, graceful_shutdown_time_in_seconds, @@ -166,30 +217,53 @@ where impl ActorFactory> for NetManagerFactory where F: ActorFactory + Clone + 'static, - T: Handler + Handler + Handler + Handler + Handler + 'static, + T: Handler + + Handler + + Handler + + Handler + + Handler + + 'static, { - fn new_actor(&mut self, context: ActorContext>) -> Result, Box> { + fn new_actor( + &mut self, + context: ActorContext>, + ) -> Result, Box> { context.actor_ref.send(ActorInitMessage::new()).unwrap(); - return Ok(NetManager::new(context, self.net_configs.clone(), self.graceful_shutdown_time_in_seconds, self.on_stop_udp_timeout, self.worker_factory.clone(), self.max_worker_count)); + return Ok(NetManager::new( + context, + self.net_configs.clone(), + self.graceful_shutdown_time_in_seconds, + self.on_stop_udp_timeout, + self.worker_factory.clone(), + self.max_worker_count, + )); } } impl Handler for NetManager - where - T: Handler + Handler + Handler + Handler + Handler + 'static, +where + T: Handler + + Handler + + Handler + + Handler + + Handler + + 'static, { - fn handle(&mut self, _msg: ActorInitMessage, context: &ActorContext) -> Result> { + fn handle( + &mut self, + _msg: ActorInitMessage, + context: &ActorContext, + ) -> Result> { let router = self.router.clone(); let is_stopping = self.is_stopping.clone(); let is_stopped = self.is_stopped.clone(); let mut net_configs = self.net_configs.clone(); let mut last_udp_message_received = Instant::now(); - let on_stop_udp_timeout = self.on_stop_udp_timeout.clone(); + let on_stop_udp_timeout = self.on_stop_udp_timeout.clone(); let context = context.clone(); thread::spawn(move || { - - let mut tcp_listeners :HashMap = HashMap::new(); - let mut udp_sockets:HashMap> = HashMap::new(); + let mut tcp_listeners: HashMap = HashMap::new(); + let mut udp_sockets: HashMap> = HashMap::new(); let poll = Poll::new(); if poll.is_err() { error!("Can't start Poll Port: {:?}", poll.err()); @@ -205,7 +279,9 @@ impl Handler for NetManager let token = Token(i); i += 1; - let address = format!("{}:{}", net_config.host, net_config.port).parse().unwrap(); + let address = format!("{}:{}", net_config.host, net_config.port) + .parse() + .unwrap(); match net_config.protocol { NetProtocol::TCP => { @@ -217,7 +293,9 @@ impl Handler for NetManager return; } let mut listener = listener.unwrap(); - let res = poll.registry().register(&mut listener, token, Interest::READABLE); + let res = + poll.registry() + .register(&mut listener, token, Interest::READABLE); if res.is_err() { error!("Can't register TCP listener: {:?}", res.err()); is_stopped.store(true, Ordering::Relaxed); @@ -225,7 +303,7 @@ impl Handler for NetManager return; } tcp_listeners.insert(token, listener); - }, + } NetProtocol::UDP => { let socket = UdpSocket::bind(address); if socket.is_err() { @@ -235,7 +313,9 @@ impl Handler for NetManager return; } let mut socket = socket.unwrap(); - let res = poll.registry().register(&mut socket, token, Interest::READABLE); + let res = poll + .registry() + .register(&mut socket, token, Interest::READABLE); if res.is_err() { error!("Can't register UDP Socket: {:?}", res.err()); is_stopped.store(true, Ordering::Relaxed); @@ -251,13 +331,11 @@ impl Handler for NetManager let num_tcp_listeners = tcp_listeners.len(); let num_total_listeners = net_configs.len(); - let mut events = Events::with_capacity(1024); - let mut streams = HashMap::new(); + let mut streams = HashMap::new(); let mut buf = [0; 65535]; loop { - if is_stopped.load(Ordering::Relaxed) { return; } @@ -265,12 +343,15 @@ impl Handler for NetManager let res = poll.poll(&mut events, None); if res.is_err() { debug!("Can't poll Network Events"); - continue + continue; } for event in events.iter() { let stopping = is_stopping.load(Ordering::Relaxed); - if stopping && streams.len() == 0 && last_udp_message_received.elapsed() > on_stop_udp_timeout { + if stopping + && streams.len() == 0 + && last_udp_message_received.elapsed() > on_stop_udp_timeout + { is_stopped.store(true, Ordering::Relaxed); break; } @@ -290,7 +371,11 @@ impl Handler for NetManager let _ = socket.shutdown(Shutdown::Both); continue; } - let res = socket.register(poll.registry(), Token(i), Interest::READABLE); + let res = socket.register( + poll.registry(), + Token(i), + Interest::READABLE, + ); if res.is_err() { error!("Could not register TcpStream. {:?}", res.err()); } @@ -312,8 +397,7 @@ impl Handler for NetManager } } } - } - else if token.0 < num_total_listeners { + } else if token.0 < num_total_listeners { //UDP handling let socket = udp_sockets.get(&token); if socket.is_none() { @@ -329,19 +413,20 @@ impl Handler for NetManager continue; } panic!("recv() failed: {:?}", e); - }, + } }; let request = String::from_utf8_lossy(&buf[..len]); - let _ = router.send(ReceiveUdpMessage::new(token.0, from, request.into_owned())); + let _ = router.send(ReceiveUdpMessage::new( + token.0, + from, + request.into_owned(), + )); last_udp_message_received = Instant::now(); - } - else { + } else { if event.is_read_closed() || event.is_write_closed() { let _ = streams.remove(&token.0); let _ = router.send(RemoveTcpConnection::new(token.0)); - - } - else if event.is_readable() { + } else if event.is_readable() { let stream = streams.get(&token.0); if stream.is_none() { let _ = streams.remove(&token.0); @@ -352,15 +437,13 @@ impl Handler for NetManager let buf_reader = BufReader::new(stream.clone()); let request: Vec = buf_reader .lines() - .map(|result| { - match result { - Ok(res) => { - return res; - } - Err(err) => { - warn!("Could not read from stream: {:?}", err); - return String::from(""); - } + .map(|result| match result { + Ok(res) => { + return res; + } + Err(err) => { + warn!("Could not read from stream: {:?}", err); + return String::from(""); } }) .take_while(|line| !line.is_empty()) @@ -368,7 +451,6 @@ impl Handler for NetManager if !request.is_empty() { let _ = router.send(ReceiveTcpMessage::new(token.0, request)); } - } } } @@ -376,4 +458,4 @@ impl Handler for NetManager }); return Ok(ActorResult::Ok); } -} \ No newline at end of file +} diff --git a/src/net/net_messages.rs b/src/net/net_messages.rs index d22b9f4..9bb4f09 100644 --- a/src/net/net_messages.rs +++ b/src/net/net_messages.rs @@ -1,9 +1,9 @@ -use std::hash::{Hash, Hasher}; -use std::net::SocketAddr; +use crate::prelude::ActorMessage; use io_arc::IoArc; use mio::net::{TcpStream, UdpSocket}; use serde::Serialize; -use crate::prelude::ActorMessage; +use std::hash::{Hash, Hasher}; +use std::net::SocketAddr; #[derive(Serialize)] pub struct AddTcpConnection { @@ -13,8 +13,7 @@ pub struct AddTcpConnection { pub address: SocketAddr, } -impl Hash for AddTcpConnection -{ +impl Hash for AddTcpConnection { fn hash(&self, state: &mut H) { self.stream_id.hash(state); } @@ -35,8 +34,7 @@ pub struct RemoveTcpConnection { pub stream_id: usize, } -impl Hash for RemoveTcpConnection -{ +impl Hash for RemoveTcpConnection { fn hash(&self, state: &mut H) { self.stream_id.hash(state); } @@ -46,9 +44,7 @@ impl ActorMessage for AddTcpConnection {} impl RemoveTcpConnection { pub fn new(stream_id: usize) -> Self { - return Self { - stream_id, - }; + return Self { stream_id }; } } @@ -60,8 +56,7 @@ pub struct ReceiveTcpMessage { pub request: Vec, } -impl Hash for ReceiveTcpMessage -{ +impl Hash for ReceiveTcpMessage { fn hash(&self, state: &mut H) { self.stream_id.hash(state); } @@ -69,10 +64,7 @@ impl Hash for ReceiveTcpMessage impl ReceiveTcpMessage { pub fn new(stream_id: usize, request: Vec) -> Self { - return Self { - stream_id, - request, - }; + return Self { stream_id, request }; } } @@ -85,8 +77,7 @@ pub struct AddUdpSocket { pub socket: IoArc, } -impl Hash for AddUdpSocket -{ +impl Hash for AddUdpSocket { fn hash(&self, state: &mut H) { self.socket_id.hash(state); } @@ -94,10 +85,7 @@ impl Hash for AddUdpSocket impl AddUdpSocket { pub fn new(socket_id: usize, socket: IoArc) -> Self { - return Self { - socket_id, - socket, - }; + return Self { socket_id, socket }; } } @@ -111,8 +99,7 @@ pub struct ReceiveUdpMessage { pub request: String, } -impl Hash for ReceiveUdpMessage -{ +impl Hash for ReceiveUdpMessage { fn hash(&self, state: &mut H) { self.socket_id.hash(state); } @@ -128,4 +115,4 @@ impl ReceiveUdpMessage { } } -impl ActorMessage for ReceiveUdpMessage {} \ No newline at end of file +impl ActorMessage for ReceiveUdpMessage {} diff --git a/src/net/net_worker.rs b/src/net/net_worker.rs index e1474cf..f5ee122 100644 --- a/src/net/net_worker.rs +++ b/src/net/net_worker.rs @@ -1,12 +1,14 @@ +use crate::net::net_messages::{ + AddTcpConnection, AddUdpSocket, ReceiveTcpMessage, ReceiveUdpMessage, RemoveTcpConnection, +}; +use crate::prelude::{Actor, ActorContext, ActorFactory, ActorResult, Handler}; +use io_arc::IoArc; +use log::{debug, warn}; +use mio::net::{TcpStream, UdpSocket}; use std::collections::HashMap; use std::error::Error; use std::io::Write; use std::net::{Shutdown, SocketAddr}; -use io_arc::IoArc; -use log::{debug, warn}; -use mio::net::{TcpStream, UdpSocket}; -use crate::net::net_messages::{AddTcpConnection, AddUdpSocket, ReceiveTcpMessage, ReceiveUdpMessage, RemoveTcpConnection}; -use crate::prelude::{Actor, ActorContext, ActorFactory, ActorResult, Handler}; #[derive(Clone)] pub struct NetWorker { @@ -23,7 +25,10 @@ impl NetWorker { } } impl Actor for NetWorker { - fn on_system_stop(&mut self, _context: &ActorContext) -> Result> { + fn on_system_stop( + &mut self, + _context: &ActorContext, + ) -> Result> { //we intentionally ignore if the actor system is stopped //we only react if the actor is explicitly stopped by the manager, because there might still be open connections that we don't want to drop Ok(ActorResult::Ok) @@ -33,21 +38,26 @@ impl Actor for NetWorker { #[derive(Clone)] pub struct NetWorkerFactory {} impl ActorFactory for NetWorkerFactory { - fn new_actor(&mut self, _context: ActorContext) -> Result> { + fn new_actor( + &mut self, + _context: ActorContext, + ) -> Result> { return Ok(NetWorker::new()); } } impl NetWorkerFactory { pub fn new() -> Self { - return Self { - - }; + return Self {}; } } impl Handler for NetWorker { - fn handle(&mut self, msg: ReceiveTcpMessage, _context: &ActorContext) -> Result> { + fn handle( + &mut self, + msg: ReceiveTcpMessage, + _context: &ActorContext, + ) -> Result> { let stream = self.streams.get_mut(&msg.stream_id); if stream.is_none() { // temporary implementation for our instant http response, later on we won't have to care here if the stream is active, we'll just forward the message @@ -75,7 +85,11 @@ impl Handler for NetWorker { } impl Handler for NetWorker { - fn handle(&mut self, msg: AddTcpConnection, _context: &ActorContext) -> Result> { + fn handle( + &mut self, + msg: AddTcpConnection, + _context: &ActorContext, + ) -> Result> { let key_already_exists = self.streams.remove(&msg.stream_id); if key_already_exists.is_some() { warn!("Stream ID already exists, dropping old one in favor of the new connection."); @@ -83,20 +97,30 @@ impl Handler for NetWorker { let _ = stream.as_ref().shutdown(Shutdown::Both); } - let _ = self.streams.insert(msg.stream_id, (msg.stream, msg.address)); + let _ = self + .streams + .insert(msg.stream_id, (msg.stream, msg.address)); return Ok(ActorResult::Ok); } } impl Handler for NetWorker { - fn handle(&mut self, msg: RemoveTcpConnection, _context: &ActorContext) -> Result> { + fn handle( + &mut self, + msg: RemoveTcpConnection, + _context: &ActorContext, + ) -> Result> { let _ = self.streams.remove(&msg.stream_id); return Ok(ActorResult::Ok); } } impl Handler for NetWorker { - fn handle(&mut self, msg: ReceiveUdpMessage, _context: &ActorContext) -> Result> { + fn handle( + &mut self, + msg: ReceiveUdpMessage, + _context: &ActorContext, + ) -> Result> { let socket = self.sockets.get_mut(&msg.socket_id); if socket.is_none() { // temporary implementation for our instant http response, later on we won't have to care here if the stream is active, we'll just forward the message @@ -111,7 +135,11 @@ impl Handler for NetWorker { } impl Handler for NetWorker { - fn handle(&mut self, msg: AddUdpSocket, _context: &ActorContext) -> Result> { + fn handle( + &mut self, + msg: AddUdpSocket, + _context: &ActorContext, + ) -> Result> { let key_already_exists = self.sockets.remove(&msg.socket_id); if key_already_exists.is_some() { warn!("Socket ID already exists, dropping old one in favor of the new."); @@ -120,4 +148,4 @@ impl Handler for NetWorker { let _ = self.sockets.insert(msg.socket_id, msg.socket); return Ok(ActorResult::Ok); } -} \ No newline at end of file +} diff --git a/src/routers/add_actor_message.rs b/src/routers/add_actor_message.rs index e965e4d..87e90b6 100644 --- a/src/routers/add_actor_message.rs +++ b/src/routers/add_actor_message.rs @@ -1,5 +1,5 @@ +use serde::Serialize; use std::hash::{Hash, Hasher}; -use serde::{Serialize}; use crate::message::actor_message::BaseActorMessage; use crate::prelude::{Actor, ActorWrapper}; @@ -19,9 +19,7 @@ where A: Actor, { pub fn new(actor: ActorWrapper) -> Self { - return Self { - actor - }; + return Self { actor }; } } @@ -29,6 +27,7 @@ impl BaseActorMessage for AddActorMessage where A: Actor {} impl Hash for AddActorMessage where - A: Actor { + A: Actor, +{ fn hash(&self, _state: &mut H) {} -} \ No newline at end of file +} diff --git a/src/routers/bulk_router_message.rs b/src/routers/bulk_router_message.rs index 8c98161..3d534cc 100644 --- a/src/routers/bulk_router_message.rs +++ b/src/routers/bulk_router_message.rs @@ -1,6 +1,6 @@ -use std::hash::{Hash, Hasher}; -use serde::Serialize; use crate::message::actor_message::BaseActorMessage; +use serde::Serialize; +use std::hash::{Hash, Hasher}; /// Wraps multiple [ActorMessage](../prelude/trait.ActorMessage.html) to be sent to a Router #[derive(Serialize)] @@ -24,9 +24,9 @@ where impl Hash for BulkRouterMessage where - M: BaseActorMessage + 'static + M: BaseActorMessage + 'static, { fn hash(&self, state: &mut H) { self.data.hash(state); } -} \ No newline at end of file +} diff --git a/src/routers/least_message_router.rs b/src/routers/least_message_router.rs index 75539f3..990c411 100644 --- a/src/routers/least_message_router.rs +++ b/src/routers/least_message_router.rs @@ -2,13 +2,13 @@ use crate::actor::actor_factory::ActorFactory; use crate::actor::context::ActorContext; use crate::actor::handler::Handler; +use crate::message::actor_message::BaseActorMessage; use crate::prelude::{Actor, ActorMessage, ActorResult, ActorWrapper}; +use crate::router::SendToAllTargetsMessage; use crate::routers::add_actor_message::AddActorMessage; use crate::routers::remove_actor_message::RemoveActorMessage; use log::{debug, error}; use std::error::Error; -use crate::message::actor_message::BaseActorMessage; -use crate::router::SendToAllTargetsMessage; pub struct LeastMessageRouter where @@ -91,7 +91,11 @@ pub struct LeastMessageRouterFactory { } impl LeastMessageRouterFactory { - pub fn new(min_mailbox_size: usize, stop_on_system_stop: bool, stop_on_empty_targets: bool,) -> Self { + pub fn new( + min_mailbox_size: usize, + stop_on_system_stop: bool, + stop_on_empty_targets: bool, + ) -> Self { Self { min_mailbox_size, stop_on_system_stop, @@ -108,7 +112,11 @@ where &mut self, _context: ActorContext>, ) -> Result, Box> { - return Ok(LeastMessageRouter::new(self.min_mailbox_size, self.stop_on_system_stop, self.stop_on_empty_targets)); + return Ok(LeastMessageRouter::new( + self.min_mailbox_size, + self.stop_on_system_stop, + self.stop_on_empty_targets, + )); } } @@ -116,7 +124,11 @@ impl LeastMessageRouter where A: Actor, { - pub fn new(min_mailbox_size: usize, stop_on_system_stop: bool, stop_on_empty_targets: bool,) -> Self { + pub fn new( + min_mailbox_size: usize, + stop_on_system_stop: bool, + stop_on_empty_targets: bool, + ) -> Self { Self { next_route_index: 0, min_mailbox_size, @@ -128,12 +140,18 @@ where } } -impl Actor for LeastMessageRouter where A: Actor { - fn on_system_stop(&mut self, context: &ActorContext) -> Result> { +impl Actor for LeastMessageRouter +where + A: Actor, +{ + fn on_system_stop( + &mut self, + context: &ActorContext, + ) -> Result> { if self.stop_on_system_stop { - let result = context.actor_ref.stop(); - if result.is_err() { - error!( + let result = context.actor_ref.stop(); + if result.is_err() { + error!( "Could not forward message ActorStopMessage to target {}", context.actor_ref.get_address().actor ); @@ -141,7 +159,6 @@ impl Actor for LeastMessageRouter where A: Actor { } } return Ok(ActorResult::Ok); - } } @@ -211,10 +228,9 @@ where self.route_to.remove(route_index); if self.route_to.len() == 0 && self.stop_on_empty_targets { debug!("Stopping router, because all targets have been removed"); - return Ok(ActorResult::Stop) + return Ok(ActorResult::Stop); } - } - else { + } else { break; } } @@ -250,9 +266,9 @@ where } impl Handler> for LeastMessageRouter - where - A: Actor + Handler + 'static, - M: BaseActorMessage + Clone + 'static, +where + A: Actor + Handler + 'static, + M: BaseActorMessage + Clone + 'static, { fn handle( &mut self, @@ -276,18 +292,17 @@ impl Handler> for LeastMessageRouter self.route_to.remove(route_index); if self.route_to.len() == 0 && self.stop_on_empty_targets { debug!("Stopping router, because all targets have been removed"); - return Ok(ActorResult::Stop) + return Ok(ActorResult::Stop); } - } - else { + } else { break; } } for target in &self.route_to { - let _ = target.send(msg.msg.clone()); + let _ = target.send(msg.msg.clone()); } return Ok(ActorResult::Ok); } -} \ No newline at end of file +} diff --git a/src/routers/mod.rs b/src/routers/mod.rs index 983bff0..4e44794 100644 --- a/src/routers/mod.rs +++ b/src/routers/mod.rs @@ -1,21 +1,20 @@ mod add_actor_message; mod bulk_router_message; +mod least_message_router; mod remove_actor_message; mod round_robin_router; -mod sharded_router; -mod least_message_router; mod send_to_all_targets_message; +mod sharded_router; pub mod prelude { pub use crate::routers::add_actor_message::AddActorMessage; - pub use crate::routers::send_to_all_targets_message::SendToAllTargetsMessage; pub use crate::routers::bulk_router_message::BulkRouterMessage; + pub use crate::routers::least_message_router::LeastMessageRouter; + pub use crate::routers::least_message_router::LeastMessageRouterFactory; pub use crate::routers::remove_actor_message::RemoveActorMessage; pub use crate::routers::round_robin_router::RoundRobinRouter; pub use crate::routers::round_robin_router::RoundRobinRouterFactory; + pub use crate::routers::send_to_all_targets_message::SendToAllTargetsMessage; pub use crate::routers::sharded_router::ShardedRouter; pub use crate::routers::sharded_router::ShardedRouterFactory; - pub use crate::routers::least_message_router::LeastMessageRouter; - pub use crate::routers::least_message_router::LeastMessageRouterFactory; - } diff --git a/src/routers/remove_actor_message.rs b/src/routers/remove_actor_message.rs index afb3198..3e2b874 100644 --- a/src/routers/remove_actor_message.rs +++ b/src/routers/remove_actor_message.rs @@ -1,5 +1,5 @@ -use std::hash::{Hash, Hasher}; use serde::Serialize; +use std::hash::{Hash, Hasher}; use crate::message::actor_message::BaseActorMessage; use crate::prelude::{Actor, ActorWrapper}; @@ -26,7 +26,8 @@ where impl BaseActorMessage for RemoveActorMessage where A: Actor {} impl Hash for RemoveActorMessage - where - A: Actor { +where + A: Actor, +{ fn hash(&self, _state: &mut H) {} -} \ No newline at end of file +} diff --git a/src/routers/round_robin_router.rs b/src/routers/round_robin_router.rs index 47433cb..d6dfa7a 100644 --- a/src/routers/round_robin_router.rs +++ b/src/routers/round_robin_router.rs @@ -3,13 +3,13 @@ use crate::actor::actor_factory::ActorFactory; use crate::actor::context::ActorContext; use crate::actor::handler::Handler; use crate::message::actor_message::{ActorMessage, BaseActorMessage}; -use crate::prelude::{Actor, ActorResult, BulkActorMessage, ActorWrapper}; +use crate::prelude::{Actor, ActorResult, ActorWrapper, BulkActorMessage}; +use crate::router::SendToAllTargetsMessage; use crate::routers::add_actor_message::AddActorMessage; use crate::routers::bulk_router_message::BulkRouterMessage; use crate::routers::remove_actor_message::RemoveActorMessage; use log::{debug, error}; use std::error::Error; -use crate::router::SendToAllTargetsMessage; pub struct RoundRobinRouter where @@ -128,8 +128,14 @@ where } } -impl Actor for RoundRobinRouter where A: Actor { - fn on_system_stop(&mut self, context: &ActorContext) -> Result> { +impl Actor for RoundRobinRouter +where + A: Actor, +{ + fn on_system_stop( + &mut self, + context: &ActorContext, + ) -> Result> { if self.stop_on_system_stop { let result = context.actor_ref.stop(); if result.is_err() { @@ -141,7 +147,6 @@ impl Actor for RoundRobinRouter where A: Actor { } } return Ok(ActorResult::Ok); - } } @@ -197,7 +202,6 @@ where return Ok(ActorResult::Ok); } - // skip/remove stopped actors loop { let route_index = self.route_index; @@ -211,10 +215,9 @@ where self.route_to.remove(route_index); if self.route_to.len() == 0 && self.stop_on_empty_targets { debug!("Stopping router, because all targets have been removed"); - return Ok(ActorResult::Stop) + return Ok(ActorResult::Stop); } - } - else { + } else { break; } } @@ -263,10 +266,9 @@ where self.route_to.remove(route_index); if self.route_to.len() == 0 && self.stop_on_empty_targets { debug!("Stopping router, because all targets have been removed"); - return Ok(ActorResult::Stop) + return Ok(ActorResult::Stop); } - } - else { + } else { break; } } @@ -296,9 +298,9 @@ where } impl Handler> for RoundRobinRouter - where - A: Actor + Handler + 'static, - M: BaseActorMessage + Clone + 'static, +where + A: Actor + Handler + 'static, + M: BaseActorMessage + Clone + 'static, { fn handle( &mut self, @@ -322,18 +324,17 @@ impl Handler> for RoundRobinRouter self.route_to.remove(route_index); if self.route_to.len() == 0 && self.stop_on_empty_targets { debug!("Stopping router, because all targets have been removed"); - return Ok(ActorResult::Stop) + return Ok(ActorResult::Stop); } - } - else { + } else { break; } } for target in &self.route_to { - let _ = target.send(msg.msg.clone()); + let _ = target.send(msg.msg.clone()); } return Ok(ActorResult::Ok); } -} \ No newline at end of file +} diff --git a/src/routers/send_to_all_targets_message.rs b/src/routers/send_to_all_targets_message.rs index 3108f6d..5cb3a00 100644 --- a/src/routers/send_to_all_targets_message.rs +++ b/src/routers/send_to_all_targets_message.rs @@ -1,12 +1,12 @@ -use std::hash::{Hash, Hasher}; -use serde::Serialize; use crate::message::actor_message::BaseActorMessage; +use serde::Serialize; +use std::hash::{Hash, Hasher}; /// Wraps multiple [ActorMessage](../prelude/trait.ActorMessage.html) to be sent to a Router #[derive(Serialize)] pub struct SendToAllTargetsMessage - where - M: BaseActorMessage + 'static, +where + M: BaseActorMessage + 'static, { pub msg: M, } @@ -14,8 +14,8 @@ pub struct SendToAllTargetsMessage impl BaseActorMessage for SendToAllTargetsMessage where M: BaseActorMessage + 'static {} impl SendToAllTargetsMessage - where - M: BaseActorMessage + 'static, +where + M: BaseActorMessage + 'static, { pub fn new(msg: M) -> Self { Self { msg } @@ -23,10 +23,10 @@ impl SendToAllTargetsMessage } impl Hash for SendToAllTargetsMessage - where - M: BaseActorMessage + 'static +where + M: BaseActorMessage + 'static, { fn hash(&self, state: &mut H) { self.msg.hash(state); } -} \ No newline at end of file +} diff --git a/src/routers/sharded_router.rs b/src/routers/sharded_router.rs index 9b82f2c..a712bb3 100644 --- a/src/routers/sharded_router.rs +++ b/src/routers/sharded_router.rs @@ -3,15 +3,15 @@ use crate::actor::actor_factory::ActorFactory; use crate::actor::context::ActorContext; use crate::actor::handler::Handler; use crate::message::actor_message::BaseActorMessage; -use crate::prelude::{Actor, ActorMessage, ActorResult, BulkActorMessage, ActorWrapper}; +use crate::prelude::{Actor, ActorMessage, ActorResult, ActorWrapper, BulkActorMessage}; +use crate::router::SendToAllTargetsMessage; use crate::routers::add_actor_message::AddActorMessage; use crate::routers::bulk_router_message::BulkRouterMessage; use crate::routers::remove_actor_message::RemoveActorMessage; +use hashring::HashRing; use log::{debug, error, info, warn}; use std::collections::HashMap; use std::error::Error; -use hashring::HashRing; -use crate::router::SendToAllTargetsMessage; pub struct ShardedRouter where @@ -134,8 +134,14 @@ where } } -impl Actor for ShardedRouter where A: Actor { - fn on_system_stop(&mut self, context: &ActorContext) -> Result> { +impl Actor for ShardedRouter +where + A: Actor, +{ + fn on_system_stop( + &mut self, + context: &ActorContext, + ) -> Result> { if self.stop_on_system_stop { let result = context.actor_ref.stop(); if result.is_err() { @@ -147,7 +153,6 @@ impl Actor for ShardedRouter where A: Actor { } } return Ok(ActorResult::Ok); - } } @@ -232,7 +237,7 @@ where if self.route_to.len() == 0 { if self.stop_on_empty_targets { debug!("Stopping router, because all targets have been stopped"); - return Ok(ActorResult::Stop) + return Ok(ActorResult::Stop); } self.can_route = false; info!("Router has no valid targets to route to. Dropping message."); @@ -291,9 +296,9 @@ where } impl Handler> for ShardedRouter - where - A: Actor + Handler + 'static, - M: BaseActorMessage + Clone + 'static, +where + A: Actor + Handler + 'static, + M: BaseActorMessage + Clone + 'static, { fn handle( &mut self, @@ -312,9 +317,9 @@ impl Handler> for ShardedRouter } for target in &self.route_to { - let _ = target.send(msg.msg.clone()); + let _ = target.send(msg.msg.clone()); } return Ok(ActorResult::Ok); } -} \ No newline at end of file +} diff --git a/src/system/actor_system.rs b/src/system/actor_system.rs index 4ee9c8a..8466b81 100644 --- a/src/system/actor_system.rs +++ b/src/system/actor_system.rs @@ -9,8 +9,8 @@ use crate::system::system_state::SystemState; use crate::system::thread_pool_manager::ThreadPoolManager; use crate::system::wakeup_manager::WakeupManager; use dashmap::DashMap; -use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; use std::thread::sleep; use std::time::Duration; @@ -83,7 +83,8 @@ impl ActorSystem { let sys = system.clone(); ctrlc::set_handler(move || { sys.sigint_handler(Duration::from_secs(300)); - }).unwrap(); + }) + .unwrap(); } system.internal_actor_manager.init(system.clone()); diff --git a/src/system/delay_actor.rs b/src/system/delay_actor.rs index 2b04948..f6f3991 100644 --- a/src/system/delay_actor.rs +++ b/src/system/delay_actor.rs @@ -1,10 +1,10 @@ +use crate::message::actor_message::BaseActorMessage; use crate::message::delayed_message::DelayedMessage; use crate::prelude::{Actor, ActorContext, ActorFactory, ActorResult, Handler}; use log::error; use std::error::Error; use std::thread::sleep; use std::time::Duration; -use crate::message::actor_message::BaseActorMessage; pub struct DelayActor {} impl Actor for DelayActor {} diff --git a/src/system/internal_actor_manager.rs b/src/system/internal_actor_manager.rs index 23aa460..ecfb966 100644 --- a/src/system/internal_actor_manager.rs +++ b/src/system/internal_actor_manager.rs @@ -1,10 +1,10 @@ +use crate::message::actor_message::BaseActorMessage; use crate::message::delayed_message::DelayedMessage; use crate::prelude::{ActorSystem, ActorWrapper, Handler}; use crate::router::{AddActorMessage, RoundRobinRouter, RoundRobinRouterFactory}; use crate::system::delay_actor::{DelayActor, DelayActorFactory}; use log::error; use std::time::Duration; -use crate::message::actor_message::BaseActorMessage; #[derive(Clone)] pub struct InternalActorManager { @@ -44,15 +44,11 @@ impl InternalActorManager { M: BaseActorMessage + 'static, A: Handler + 'static, { - let result = - self.delay_router - .as_ref() - .unwrap() - .send(DelayedMessage::new( - msg, - destination, - duration, - )); + let result = self + .delay_router + .as_ref() + .unwrap() + .send(DelayedMessage::new(msg, destination, duration)); if result.is_err() { error!("Could not send message to delay router"); } diff --git a/src/system/system_state.rs b/src/system/system_state.rs index 5fede8a..d99bf64 100644 --- a/src/system/system_state.rs +++ b/src/system/system_state.rs @@ -62,7 +62,6 @@ impl SystemState { let now = Instant::now(); while self.get_actor_count() != 0 { - if now.elapsed() >= timeout || self.is_force_stopped.load(Ordering::Relaxed) { self.is_force_stopped.store(true, Ordering::Relaxed); self.mailboxes.clear(); @@ -144,7 +143,6 @@ impl SystemState { v.fetch_sub(1, Ordering::Relaxed); }); self.total_actor_count.fetch_sub(1, Ordering::Relaxed); - } pub fn remove_mailbox(&self, address: &ActorAddress) { @@ -152,11 +150,7 @@ impl SystemState { self.mailboxes.remove(address); } - pub fn add_mailbox( - &self, - address: ActorAddress, - mailbox: Mailbox, - ) + pub fn add_mailbox(&self, address: ActorAddress, mailbox: Mailbox) where A: Handler + 'static, { diff --git a/src/wrapper/actor_wrapper.rs b/src/wrapper/actor_wrapper.rs index ac95574..050fdc7 100644 --- a/src/wrapper/actor_wrapper.rs +++ b/src/wrapper/actor_wrapper.rs @@ -1,35 +1,34 @@ -use std::fmt::{Debug, Formatter}; -use std::hash::{Hash, Hasher}; -use std::panic::UnwindSafe; -use std::time::Duration; -use serde::{Deserialize, Serialize}; +use crate::actor::actor::Actor; use crate::actor::actor_address::ActorAddress; -use crate::actor::mailbox::Mailbox; -use crate::message::actor_message::BaseActorMessage; use crate::actor::actor_send_error::ActorSendError; -use crate::actor::actor::Actor; use crate::actor::handler::Handler; +use crate::actor::mailbox::Mailbox; +use crate::message::actor_message::BaseActorMessage; use crate::system::internal_actor_manager::InternalActorManager; use crate::system::system_state::SystemState; use crate::system::wakeup_manager::WakeupManager; use crate::wrapper::local_actor_wrapper::LocalActorWrapper; use crate::wrapper::remote_actor_wrapper::RemoteActorWrapper; +use serde::{Deserialize, Serialize}; +use std::fmt::{Debug, Formatter}; +use std::hash::{Hash, Hasher}; +use std::panic::UnwindSafe; +use std::time::Duration; #[derive(Serialize, Deserialize)] pub struct ActorWrapper - where - A: Actor, +where + A: Actor, { address: ActorAddress, remote: RemoteActorWrapper, #[serde(skip)] local: Option>, - } impl Debug for ActorWrapper - where - A: Actor, +where + A: Actor, { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "") @@ -39,8 +38,8 @@ impl Debug for ActorWrapper impl UnwindSafe for ActorWrapper where A: Actor {} impl ActorWrapper - where - A: Actor + UnwindSafe, +where + A: Actor + UnwindSafe, { /// Automatically called by the [ActorBuilder.spawn](../prelude/struct.ActorBuilder.html#method.spawn) pub fn new( @@ -50,7 +49,11 @@ impl ActorWrapper internal_actor_manager: InternalActorManager, system_state: SystemState, ) -> Self { - let local = Some(LocalActorWrapper::new(mailbox, wakeup_manager, internal_actor_manager)); + let local = Some(LocalActorWrapper::new( + mailbox, + wakeup_manager, + internal_actor_manager, + )); let remote = RemoteActorWrapper::new(system_state); Self { address, @@ -63,9 +66,9 @@ impl ActorWrapper /// Blocks until message has been sent, or fails if the target has been stopped /// It is NOT recommended to use this to send messages to Actors with a limited mailbox. Use send_timeout() or send_after() for these cases pub fn send(&self, msg: M) -> Result<(), ActorSendError> - where - A: Handler, - M: BaseActorMessage + 'static, + where + A: Handler, + M: BaseActorMessage + 'static, { if self.local.is_some() { return self.local.as_ref().unwrap().send(msg, self.address.clone()); @@ -75,24 +78,32 @@ impl ActorWrapper /// Same as send, but with a user defined timeout pub fn send_timeout(&self, msg: M, timeout: Duration) -> Result<(), ActorSendError> - where - A: Handler, - M: BaseActorMessage + 'static, + where + A: Handler, + M: BaseActorMessage + 'static, { if self.local.is_some() { - return self.local.as_ref().unwrap().send_timeout(msg, timeout, self.address.clone()); + return self + .local + .as_ref() + .unwrap() + .send_timeout(msg, timeout, self.address.clone()); } return self.remote.send_timeout(msg, timeout, self.address.clone()); } /// Sends a message to the actor after a specified delay pub fn send_after(&self, msg: M, delay: Duration) -> Result<(), ActorSendError> - where - A: Handler + 'static, - M: BaseActorMessage + 'static, + where + A: Handler + 'static, + M: BaseActorMessage + 'static, { if self.local.is_some() { - return self.local.as_ref().unwrap().send_after(msg, delay, self.clone()); + return self + .local + .as_ref() + .unwrap() + .send_after(msg, delay, self.clone()); } return self.remote.send_after(msg, delay, self.address.clone()); } @@ -108,7 +119,11 @@ impl ActorWrapper /// Tells the actor to sleep for the specified duration pub fn sleep(&self, duration: Duration) -> Result<(), ActorSendError> { if self.local.is_some() { - return self.local.as_ref().unwrap().sleep(duration, self.address.clone()); + return self + .local + .as_ref() + .unwrap() + .sleep(duration, self.address.clone()); } return self.remote.sleep(duration, self.address.clone()); } @@ -146,15 +161,19 @@ impl ActorWrapper /// Blocks until the actor has been stopped pub fn wait_for_stop(&self) { if self.local.is_some() { - return self.local.as_ref().unwrap().wait_for_stop(self.address.clone()); + return self + .local + .as_ref() + .unwrap() + .wait_for_stop(self.address.clone()); } return self.remote.wait_for_stop(); } } impl Clone for ActorWrapper - where - A: Actor + UnwindSafe, +where + A: Actor + UnwindSafe, { fn clone(&self) -> Self { Self { @@ -166,10 +185,10 @@ impl Clone for ActorWrapper } impl Hash for ActorWrapper - where - A: Actor + UnwindSafe, +where + A: Actor + UnwindSafe, { fn hash(&self, state: &mut H) { self.address.hash(state); } -} \ No newline at end of file +} diff --git a/src/wrapper/local_actor_wrapper.rs b/src/wrapper/local_actor_wrapper.rs index 8e27f9d..ab59e93 100644 --- a/src/wrapper/local_actor_wrapper.rs +++ b/src/wrapper/local_actor_wrapper.rs @@ -1,31 +1,30 @@ -use std::panic::UnwindSafe; -use std::thread::sleep; -use std::time::Duration; +use crate::actor::actor::Actor; use crate::actor::actor_address::ActorAddress; +use crate::actor::actor_send_error::ActorSendError; +use crate::actor::handler::Handler; use crate::actor::mailbox::Mailbox; use crate::message::actor_message::BaseActorMessage; use crate::message::actor_stop_message::ActorStopMessage; use crate::message::sleep_message::SleepMessage; -use crate::actor::actor_send_error::ActorSendError; -use crate::actor::actor::Actor; -use crate::actor::handler::Handler; use crate::prelude::ActorWrapper; use crate::system::internal_actor_manager::InternalActorManager; use crate::system::wakeup_manager::WakeupManager; +use std::panic::UnwindSafe; +use std::thread::sleep; +use std::time::Duration; pub struct LocalActorWrapper - where - A: Actor, +where + A: Actor, { mailbox: Mailbox, wakeup_manager: WakeupManager, internal_actor_manager: Box, } - impl LocalActorWrapper - where - A: Actor + UnwindSafe, +where + A: Actor + UnwindSafe, { pub fn new( mailbox: Mailbox, @@ -40,9 +39,9 @@ impl LocalActorWrapper } pub fn send(&self, msg: M, address: ActorAddress) -> Result<(), ActorSendError> - where - A: Handler, - M: BaseActorMessage + 'static, + where + A: Handler, + M: BaseActorMessage + 'static, { if self.mailbox.is_stopped() { return Err(ActorSendError::AlreadyStoppedError); @@ -61,10 +60,15 @@ impl LocalActorWrapper return Ok(()); } - pub fn send_timeout(&self, msg: M, timeout: Duration, address: ActorAddress) -> Result<(), ActorSendError> - where - A: Handler, - M: BaseActorMessage + 'static, + pub fn send_timeout( + &self, + msg: M, + timeout: Duration, + address: ActorAddress, + ) -> Result<(), ActorSendError> + where + A: Handler, + M: BaseActorMessage + 'static, { if self.mailbox.is_stopped() { return Err(ActorSendError::AlreadyStoppedError); @@ -83,10 +87,15 @@ impl LocalActorWrapper return Ok(()); } - pub fn send_after(&self, msg: M, delay: Duration, destination: ActorWrapper) -> Result<(), ActorSendError> - where - A: Handler + 'static, - M: BaseActorMessage + 'static, + pub fn send_after( + &self, + msg: M, + delay: Duration, + destination: ActorWrapper, + ) -> Result<(), ActorSendError> + where + A: Handler + 'static, + M: BaseActorMessage + 'static, { if self.mailbox.is_stopped() { return Err(ActorSendError::AlreadyStoppedError); @@ -111,11 +120,11 @@ impl LocalActorWrapper } pub fn is_mailbox_stopped(&self) -> bool { - return self.mailbox.is_stopped() + return self.mailbox.is_stopped(); } pub fn is_stopped(&self) -> bool { - return self.get_mailbox_size() == 0 && self.mailbox.is_stopped() + return self.get_mailbox_size() == 0 && self.mailbox.is_stopped(); } pub fn wait_for_stop(&self, address: ActorAddress) { @@ -127,8 +136,8 @@ impl LocalActorWrapper } impl Clone for LocalActorWrapper - where - A: Actor + UnwindSafe, +where + A: Actor + UnwindSafe, { fn clone(&self) -> Self { Self { @@ -137,4 +146,4 @@ impl Clone for LocalActorWrapper internal_actor_manager: self.internal_actor_manager.clone(), } } -} \ No newline at end of file +} diff --git a/src/wrapper/mod.rs b/src/wrapper/mod.rs index d0cb689..a18e63f 100644 --- a/src/wrapper/mod.rs +++ b/src/wrapper/mod.rs @@ -1,7 +1,7 @@ +pub mod actor_wrapper; mod local_actor_wrapper; mod remote_actor_wrapper; -pub mod actor_wrapper; pub mod prelude { pub use crate::wrapper::actor_wrapper::ActorWrapper; -} \ No newline at end of file +} diff --git a/src/wrapper/remote_actor_wrapper.rs b/src/wrapper/remote_actor_wrapper.rs index 9ad026d..88dea18 100644 --- a/src/wrapper/remote_actor_wrapper.rs +++ b/src/wrapper/remote_actor_wrapper.rs @@ -1,11 +1,11 @@ -use std::hash::{Hash, Hasher}; -use std::time::Duration; -use serde::{Deserialize, Serialize}; use crate::actor::actor_address::ActorAddress; -use crate::message::actor_message::BaseActorMessage; use crate::actor::actor_send_error::ActorSendError; +use crate::message::actor_message::BaseActorMessage; use crate::prelude::SerializedMessage; use crate::system::system_state::SystemState; +use serde::{Deserialize, Serialize}; +use std::hash::{Hash, Hasher}; +use std::time::Duration; #[derive(Serialize, Deserialize, Clone)] pub struct RemoteActorWrapper { @@ -13,39 +13,47 @@ pub struct RemoteActorWrapper { system_state: Option, } -impl RemoteActorWrapper -{ - pub fn new( - system_state: SystemState, - ) -> Self { +impl RemoteActorWrapper { + pub fn new(system_state: SystemState) -> Self { let system_state = Some(system_state); - return Self { - system_state - }; + return Self { system_state }; } pub fn send(&self, msg: M, address: &ActorAddress) -> Result<(), ActorSendError> - where - M: BaseActorMessage + 'static, + where + M: BaseActorMessage + 'static, { //todo this needs to forward to the NetWorker //the Networker should then forward this to the remote actor system //the remote actor system should then forward the message using system_state let serialized = bincode::serialize(&msg).unwrap(); - self.system_state.as_ref().unwrap().send_to_address(address, SerializedMessage::new(serialized)); + self.system_state + .as_ref() + .unwrap() + .send_to_address(address, SerializedMessage::new(serialized)); return Ok(()); } - pub fn send_timeout(&self, _msg: M, _timeout: Duration, _address: ActorAddress) -> Result<(), ActorSendError> - where - M: BaseActorMessage + 'static, + pub fn send_timeout( + &self, + _msg: M, + _timeout: Duration, + _address: ActorAddress, + ) -> Result<(), ActorSendError> + where + M: BaseActorMessage + 'static, { return Ok(()); } - pub fn send_after(&self, _msg: M, _delay: Duration, _address: ActorAddress) -> Result<(), ActorSendError> - where - M: BaseActorMessage + 'static, + pub fn send_after( + &self, + _msg: M, + _delay: Duration, + _address: ActorAddress, + ) -> Result<(), ActorSendError> + where + M: BaseActorMessage + 'static, { return Ok(()); } @@ -75,9 +83,8 @@ impl RemoteActorWrapper } } -impl Hash for RemoteActorWrapper -{ +impl Hash for RemoteActorWrapper { fn hash(&self, _state: &mut H) { return; } -} \ No newline at end of file +} From a3233406ce24234e624e888993a39b538a8d18c7 Mon Sep 17 00:00:00 2001 From: Bobonium Date: Sun, 7 May 2023 10:49:36 +0200 Subject: [PATCH 26/44] added proper Serialize and Deserialize to AddActorMessage --- src/routers/add_actor_message.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/routers/add_actor_message.rs b/src/routers/add_actor_message.rs index 87e90b6..79d2bb7 100644 --- a/src/routers/add_actor_message.rs +++ b/src/routers/add_actor_message.rs @@ -1,16 +1,19 @@ -use serde::Serialize; +use serde::{Deserialize, Serialize, Serializer}; use std::hash::{Hash, Hasher}; use crate::message::actor_message::BaseActorMessage; use crate::prelude::{Actor, ActorWrapper}; /// Adds an Actor to the Router -#[derive(Serialize)] +#[derive(Serialize, Deserialize)] +#[serde(bound( +serialize = "ActorWrapper::: Serialize", +deserialize = "ActorWrapper::: Deserialize<'de>", +))] pub struct AddActorMessage where A: Actor, { - #[serde(skip)] pub actor: ActorWrapper, } From c344d6565a0fbc8ef0c38b6bb82ea458cf9fe7ae Mon Sep 17 00:00:00 2001 From: Bobonium Date: Sun, 7 May 2023 17:25:16 +0200 Subject: [PATCH 27/44] fix serde setup for ActorWrapper --- src/message/delayed_message.rs | 5 ++++- src/routers/add_actor_message.rs | 6 +++--- src/routers/remove_actor_message.rs | 5 ++++- src/wrapper/actor_wrapper.rs | 4 ++++ 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/message/delayed_message.rs b/src/message/delayed_message.rs index 9980dcd..1cd445a 100644 --- a/src/message/delayed_message.rs +++ b/src/message/delayed_message.rs @@ -6,13 +6,16 @@ use std::time::{Duration, Instant}; /// Wraps an [ActorMessage](../prelude/trait.ActorMessage.html) to be sent at a later time #[derive(Serialize)] +#[serde(bound( +serialize = "A: Actor", +deserialize = "A: Actor", +))] pub struct DelayedMessage where M: BaseActorMessage + 'static, A: Actor, { pub msg: M, - #[serde(skip)] pub destination: ActorWrapper, pub delay: Duration, #[serde(skip)] diff --git a/src/routers/add_actor_message.rs b/src/routers/add_actor_message.rs index 79d2bb7..03285a4 100644 --- a/src/routers/add_actor_message.rs +++ b/src/routers/add_actor_message.rs @@ -1,4 +1,4 @@ -use serde::{Deserialize, Serialize, Serializer}; +use serde::{Deserialize, Serialize}; use std::hash::{Hash, Hasher}; use crate::message::actor_message::BaseActorMessage; @@ -7,8 +7,8 @@ use crate::prelude::{Actor, ActorWrapper}; /// Adds an Actor to the Router #[derive(Serialize, Deserialize)] #[serde(bound( -serialize = "ActorWrapper::: Serialize", -deserialize = "ActorWrapper::: Deserialize<'de>", +serialize = "A: Actor", +deserialize = "A: Actor", ))] pub struct AddActorMessage where diff --git a/src/routers/remove_actor_message.rs b/src/routers/remove_actor_message.rs index 3e2b874..e67f11c 100644 --- a/src/routers/remove_actor_message.rs +++ b/src/routers/remove_actor_message.rs @@ -6,11 +6,14 @@ use crate::prelude::{Actor, ActorWrapper}; /// Removes an Actor from the Router #[derive(Serialize)] +#[serde(bound( +serialize = "A: Actor", +deserialize = "A: Actor", +))] pub struct RemoveActorMessage where A: Actor, { - #[serde(skip)] pub actor: ActorWrapper, } diff --git a/src/wrapper/actor_wrapper.rs b/src/wrapper/actor_wrapper.rs index 050fdc7..1754592 100644 --- a/src/wrapper/actor_wrapper.rs +++ b/src/wrapper/actor_wrapper.rs @@ -16,6 +16,10 @@ use std::panic::UnwindSafe; use std::time::Duration; #[derive(Serialize, Deserialize)] +#[serde(bound( +serialize = "A: Actor", +deserialize = "A: Actor", +))] pub struct ActorWrapper where A: Actor, From f407a7883419aaf872288e7ca27fc30429f54f09 Mon Sep 17 00:00:00 2001 From: Bobonium Date: Sun, 7 May 2023 23:31:20 +0200 Subject: [PATCH 28/44] add ability to re-initialize ActorWrapper after deserializing --- CHANGELOG.md | 1 + examples/serialize.rs | 16 +++---- src/actor/actor.rs | 2 +- src/actor/actor_builder.rs | 68 ++++++++++++++++++++++++++--- src/routers/least_message_router.rs | 8 ++-- src/routers/round_robin_router.rs | 8 ++-- src/routers/sharded_router.rs | 8 ++-- src/system/system_state.rs | 6 +-- src/wrapper/actor_wrapper.rs | 24 +++++++++- 9 files changed, 110 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 076a843..f1b661a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ - this requirement comes from the fact, that all messages need to be serializable in theory to be able to be sent to other actor systems - if a message is really intended to be sent it should obviously also implement `Deserialize` - Added `.is_mailbox_stopped()`, `is_stopped()` and `wait_for_stop()` to `ActorWrapper` + - Added ability to re-initialize `ActorWrapper` after deserializing - Added `general.signal_graceful_timeout_in_seconds` to config - Added `ActorBuilder.spawn_multiple()` - All routers now support a `SendToAllTargetsMessage` that will forward `M` to all active targets diff --git a/examples/serialize.rs b/examples/serialize.rs index 79268b8..22f5edd 100644 --- a/examples/serialize.rs +++ b/examples/serialize.rs @@ -4,14 +4,14 @@ use std::process::exit; use std::time::Duration; use tyra::prelude::*; -#[derive(Serialize, Deserialize, PartialEq, Debug, Clone, Hash)] +#[derive(Serialize, Deserialize, Hash, Clone)] struct TestMsg { content: String, + actor_wrapper: ActorWrapper, } impl ActorMessage for TestMsg {} -#[derive(Clone)] struct RemoteActor {} impl Actor for RemoteActor { @@ -24,10 +24,9 @@ impl Actor for RemoteActor { if result.is_err() { return Ok(ActorResult::Ok); } - let decoded: TestMsg = result.unwrap(); - context - .actor_ref - .send_after(decoded, Duration::from_millis(50))?; + let mut deserialized: TestMsg = result.unwrap(); + deserialized.actor_wrapper.init_after_deserialize(&context.system); + deserialized.actor_wrapper.send_after(deserialized.clone(), Duration::from_millis(50))?; Ok(ActorResult::Ok) } } @@ -60,12 +59,13 @@ fn main() { let actor_system = ActorSystem::new(actor_config); let hw = RemoteActorFactory {}; - let x = actor_system.builder().spawn("hello-world", hw).unwrap(); + let remote_actor = actor_system.builder().spawn("hello-world", hw).unwrap(); let msg = TestMsg { content: String::from("Hello World!"), + actor_wrapper: remote_actor.clone(), }; let serialized = bincode::serialize(&msg).unwrap(); - actor_system.send_to_address(x.get_address(), SerializedMessage::new(serialized)); + actor_system.send_to_address(remote_actor.get_address(), SerializedMessage::new(serialized)); let result = actor_system.await_shutdown(); diff --git a/src/actor/actor.rs b/src/actor/actor.rs index c363eba..2331ee4 100644 --- a/src/actor/actor.rs +++ b/src/actor/actor.rs @@ -91,7 +91,7 @@ use std::panic::UnwindSafe; /// └─◄─┴──────────────────────────────◄─┴──────────────────────◄─┴─────────────────────────────────────────────────────┘ └─────────────────────────────────────────────┘ /// /// ``` -pub trait Actor: Send + Sync + UnwindSafe + Sized { +pub trait Actor: Send + Sync + UnwindSafe + Sized + 'static { /// executed whenever Actor receives a [SerializedMessage](../prelude/struct.SerializedMessage.html) /// panic triggers `self.on_panic()` with `source = ActorPanicSource::Message` fn handle_serialized_message( diff --git a/src/actor/actor_builder.rs b/src/actor/actor_builder.rs index f5c300f..d2dda27 100644 --- a/src/actor/actor_builder.rs +++ b/src/actor/actor_builder.rs @@ -195,7 +195,7 @@ where if self.system_state.is_mailbox_active(&actor_address) { return self .system_state - .get_actor_ref(actor_address, self.internal_actor_manager.clone()); + .get_actor_ref(&actor_address, self.internal_actor_manager.clone()); } let result = self.system_state.increase_pool_actor_count(&actor_address); @@ -315,7 +315,7 @@ where /// let address = ActorAddress::new("local", actor_system.get_name(), pool_name, actor_name); /// /// //this does not work, because the actor does not exist yet - /// let this_is_not_working :Result, ActorError> = actor_system.builder().get_existing(address.clone()); + /// let this_is_not_working :Result, ActorError> = actor_system.builder().get_existing(&address); /// assert!(this_is_not_working.is_err(), "The BrokenActor existed"); /// let err = this_is_not_working.err().unwrap(); /// assert_eq!(err, ActorError::DoesNotExistError, "Error is not correct"); @@ -325,21 +325,21 @@ where /// assert!(this_works.is_ok(), "The actor could not be spawned"); /// /// //this does not work, because the actor type does not match - /// let this_is_not_working :Result, ActorError> = actor_system.builder().get_existing(address.clone()); + /// let this_is_not_working :Result, ActorError> = actor_system.builder().get_existing(&address); /// assert!(this_is_not_working.is_err(), "The BrokenActor existed"); /// let err = this_is_not_working.err().unwrap(); /// assert_eq!(err, ActorError::InvalidActorTypeError, "Error is not correct"); /// /// //this does work, because the actor type matches - /// let this_works :Result, ActorError> = actor_system.builder().get_existing(address.clone()); + /// let this_works :Result, ActorError> = actor_system.builder().get_existing(&address); /// assert!(this_works.is_ok(), "The TestActor did not exist"); /// /// actor_system.stop(Duration::from_millis(3000)); /// std::process::exit(actor_system.await_shutdown()); /// } /// ``` - pub fn get_existing(&self, actor_address: ActorAddress) -> Result, ActorError> { - if self.system_state.is_mailbox_active(&actor_address) { + pub fn get_existing(&self, actor_address: &ActorAddress) -> Result, ActorError> { + if self.system_state.is_mailbox_active(actor_address) { return self .system_state .get_actor_ref(actor_address, self.internal_actor_manager.clone()); @@ -347,6 +347,62 @@ where return Err(ActorError::DoesNotExistError); } + /// Always returns a ActorWrapper, even if it does not exist on the current ActorSystem + /// + /// # Returns + /// + /// `ActorWrapper` + /// + /// # Examples + /// + /// ```rust + /// use tyra::prelude::*; + /// use std::error::Error; + /// use std::time::Duration; + /// + /// struct TestActor {} + /// impl TestActor { + /// pub fn new() -> Self { + /// Self {} + /// } + /// } + /// impl Actor for TestActor {} + /// + /// #[derive(Clone)] + /// struct TestActorFactory {} + /// impl TestActorFactory { + /// pub fn new() -> Self { + /// Self {} + /// } + /// } + /// impl ActorFactory for TestActorFactory { + /// fn new_actor(&mut self, _context: ActorContext) -> Result> { + /// Ok(TestActor::new()) + /// } + /// } + /// + /// #[ntest::timeout(100000)] + /// fn main() { + /// let mut actor_config = TyraConfig::new().unwrap(); + /// actor_config.thread_pool.config.insert(String::from("default"), ThreadPoolConfig::new(2, 1, 1, 1.0)); + /// let actor_system = ActorSystem::new(actor_config); + /// + /// let address = ActorAddress::new("remote", "system", "pool", "actor"); + /// let actor_wrapper :ActorWrapper = actor_system.builder().init_after_deserialize(&address); + /// + /// actor_system.stop(Duration::from_millis(3000)); + /// std::process::exit(actor_system.await_shutdown()); + /// } + /// ``` + pub fn init_after_deserialize(&self, actor_address: &ActorAddress) -> ActorWrapper { + let result = self.get_existing(actor_address); + if result.is_ok() { + let result = result.unwrap(); + return result; + } + return ActorWrapper::from_address(actor_address.clone(), self.system_state.clone()); + } + /// Creates N defined [Actor]s on the [ActorSystem] /// /// Requires [ActorFactory] to implement `Clone` diff --git a/src/routers/least_message_router.rs b/src/routers/least_message_router.rs index 990c411..8bcd68a 100644 --- a/src/routers/least_message_router.rs +++ b/src/routers/least_message_router.rs @@ -122,7 +122,7 @@ where impl LeastMessageRouter where - A: Actor, + A: Actor + 'static, { pub fn new( min_mailbox_size: usize, @@ -142,7 +142,7 @@ where impl Actor for LeastMessageRouter where - A: Actor, + A: Actor + 'static, { fn on_system_stop( &mut self, @@ -164,7 +164,7 @@ where impl Handler> for LeastMessageRouter where - A: Actor, + A: Actor + 'static, { fn handle( &mut self, @@ -179,7 +179,7 @@ where impl Handler> for LeastMessageRouter where - A: Actor, + A: Actor + 'static, { fn handle( &mut self, diff --git a/src/routers/round_robin_router.rs b/src/routers/round_robin_router.rs index d6dfa7a..1d2b60d 100644 --- a/src/routers/round_robin_router.rs +++ b/src/routers/round_robin_router.rs @@ -115,7 +115,7 @@ where impl RoundRobinRouter where - A: Actor, + A: Actor + 'static, { pub fn new(stop_on_system_stop: bool, stop_on_empty_targets: bool) -> Self { Self { @@ -130,7 +130,7 @@ where impl Actor for RoundRobinRouter where - A: Actor, + A: Actor + 'static, { fn on_system_stop( &mut self, @@ -152,7 +152,7 @@ where impl Handler> for RoundRobinRouter where - A: Actor, + A: Actor + 'static, { fn handle( &mut self, @@ -167,7 +167,7 @@ where impl Handler> for RoundRobinRouter where - A: Actor, + A: Actor + 'static, { fn handle( &mut self, diff --git a/src/routers/sharded_router.rs b/src/routers/sharded_router.rs index a712bb3..7c19a9f 100644 --- a/src/routers/sharded_router.rs +++ b/src/routers/sharded_router.rs @@ -119,7 +119,7 @@ where impl ShardedRouter where - A: Actor, + A: Actor + 'static, { pub fn new(stop_on_system_stop: bool, stop_on_empty_targets: bool) -> Self { Self { @@ -136,7 +136,7 @@ where impl Actor for ShardedRouter where - A: Actor, + A: Actor + 'static, { fn on_system_stop( &mut self, @@ -158,7 +158,7 @@ where impl Handler> for ShardedRouter where - A: Actor, + A: Actor + 'static, { fn handle( &mut self, @@ -174,7 +174,7 @@ where impl Handler> for ShardedRouter where - A: Actor, + A: Actor + 'static, { fn handle( &mut self, diff --git a/src/system/system_state.rs b/src/system/system_state.rs index d99bf64..30faace 100644 --- a/src/system/system_state.rs +++ b/src/system/system_state.rs @@ -190,17 +190,17 @@ impl SystemState { pub fn get_actor_ref( &self, - address: ActorAddress, + address: &ActorAddress, internal_actor_manager: InternalActorManager, ) -> Result, ActorError> where A: Handler + 'static, { - let mb = self.mailboxes.get(&address).unwrap().value().clone(); + let mb = self.mailboxes.get(address).unwrap().value().clone(); return match mb.as_any().downcast_ref::>() { Some(m) => Ok(ActorWrapper::new( m.clone(), - address, + address.clone(), self.wakeup_manager.clone(), internal_actor_manager, self.clone(), diff --git a/src/wrapper/actor_wrapper.rs b/src/wrapper/actor_wrapper.rs index 1754592..fe4f122 100644 --- a/src/wrapper/actor_wrapper.rs +++ b/src/wrapper/actor_wrapper.rs @@ -14,6 +14,7 @@ use std::fmt::{Debug, Formatter}; use std::hash::{Hash, Hasher}; use std::panic::UnwindSafe; use std::time::Duration; +use crate::prelude::ActorSystem; #[derive(Serialize, Deserialize)] #[serde(bound( @@ -43,7 +44,7 @@ impl UnwindSafe for ActorWrapper where A: Actor {} impl ActorWrapper where - A: Actor + UnwindSafe, + A: Actor + UnwindSafe + 'static, { /// Automatically called by the [ActorBuilder.spawn](../prelude/struct.ActorBuilder.html#method.spawn) pub fn new( @@ -173,6 +174,27 @@ where } return self.remote.wait_for_stop(); } + + /// This function is required after deserializing any ActorWrapper + /// If the actor is a local actor it will restore the LocalActorWrapper and any message can be sent directly to the actor again + /// If the actor is a remote actor it will add the SystemState to it, so that the messages can be forwarded to the destination + /// It is technically impossible to deserialize a working LocalActor or a RemoteActor directly, which is why this helper is required to feed the unserializable information back into it + pub fn init_after_deserialize(&mut self, system: &ActorSystem) { + let actor_wrapper = system.builder::().init_after_deserialize(self.get_address()); + self.local = actor_wrapper.local; + self.remote = actor_wrapper.remote; + } + + /// Returns an ActorWrapper that will be handled as a remote actor. + /// If the actor exists locally tru `init_after_deserialize` or `ActorBuilder::get_existing()` instead + pub fn from_address(address: ActorAddress, system_state: SystemState) -> Self { + return Self { + address, + remote: RemoteActorWrapper::new(system_state), + local: None, + } + } + } impl Clone for ActorWrapper From 9bfba6dfd363344378ed3e906cec0b5dc8ddc932 Mon Sep 17 00:00:00 2001 From: Bobonium Date: Mon, 8 May 2023 12:49:01 +0200 Subject: [PATCH 29/44] fix router and worker name for netmanager created actors --- src/net/net_manager.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/net/net_manager.rs b/src/net/net_manager.rs index 764765d..4e63e42 100644 --- a/src/net/net_manager.rs +++ b/src/net/net_manager.rs @@ -63,22 +63,24 @@ where { let pool_name = &context.actor_ref.get_address().pool; + let router_name = format!("{}-net-router", context.actor_ref.get_address().actor); let router = context .system .builder() .set_pool_name(pool_name) - .spawn("net-least-message", ShardedRouterFactory::new(false, false)) + .spawn(router_name, ShardedRouterFactory::new(false, false)) .unwrap(); let max_pool_count = context .system .get_available_actor_count_for_pool(pool_name) .unwrap(); let worker_count = min(max_worker_count, max_pool_count); + let worker_name = format!("{}-net-worker", context.actor_ref.get_address().actor); let workers = context .system .builder() .set_pool_name(pool_name) - .spawn_multiple("net-worker", worker_factory.clone(), worker_count) + .spawn_multiple(worker_name, worker_factory.clone(), worker_count) .unwrap(); for worker in &workers { router.send(AddActorMessage::new(worker.clone())).unwrap(); From 4896439e6e60b8268443acd1e5f5aa8f0d3c31b3 Mon Sep 17 00:00:00 2001 From: Bobonium Date: Mon, 8 May 2023 15:55:28 +0200 Subject: [PATCH 30/44] preparation for cluster implementation --- examples/serialize.rs | 2 +- src/actor/actor_send_error.rs | 4 ++++ src/message/serialized_message.rs | 9 +++++++-- src/net/net_worker.rs | 7 ++++++- src/system/actor_system.rs | 11 ++++++++--- src/system/system_state.rs | 21 ++++++++++++++++++--- src/wrapper/actor_wrapper.rs | 1 - src/wrapper/remote_actor_wrapper.rs | 25 +++++++++++++------------ 8 files changed, 57 insertions(+), 23 deletions(-) diff --git a/examples/serialize.rs b/examples/serialize.rs index 22f5edd..9f39680 100644 --- a/examples/serialize.rs +++ b/examples/serialize.rs @@ -65,7 +65,7 @@ fn main() { actor_wrapper: remote_actor.clone(), }; let serialized = bincode::serialize(&msg).unwrap(); - actor_system.send_to_address(remote_actor.get_address(), SerializedMessage::new(serialized)); + actor_system.send_to_address(remote_actor.get_address(), serialized); let result = actor_system.await_shutdown(); diff --git a/src/actor/actor_send_error.rs b/src/actor/actor_send_error.rs index d4627bf..7c44c9e 100644 --- a/src/actor/actor_send_error.rs +++ b/src/actor/actor_send_error.rs @@ -9,4 +9,8 @@ pub enum ActorSendError { /// Triggered by [ActorWrapper.send](../prelude/struct.ActorWrapper.html#method.send) && [ActorWrapper.send_timeout](../prelude/struct.ActorWrapper.html#method.send_timout) when a message is sent to a stopped Actor #[error("Message could not be delivered")] AlreadyStoppedError, + + /// Triggered by [ActorWrapper](../prelude/struct.ActorWrapper.html) if a message can't be send to remote Actors + #[error("Message can't be delivered to remote Actor")] + NotAllowedForRemoteActorError } diff --git a/src/message/serialized_message.rs b/src/message/serialized_message.rs index ac19f88..38d25c0 100644 --- a/src/message/serialized_message.rs +++ b/src/message/serialized_message.rs @@ -1,5 +1,6 @@ use crate::message::actor_message::DefaultActorMessage; use serde::{Deserialize, Serialize}; +use crate::prelude::ActorAddress; /// For Remote message handling /// @@ -11,12 +12,16 @@ use serde::{Deserialize, Serialize}; /// [ActorSystem.send_to_address](../prelude/struct.ActorSystem.html#method.send_to_address) uses this object to send serialized messages to Actors #[derive(Hash, Serialize, Deserialize)] pub struct SerializedMessage { + pub destination_address: ActorAddress, pub content: Vec, } impl SerializedMessage { - pub fn new(content: Vec) -> Self { - Self { content } + pub fn new(destination_address: ActorAddress, content: Vec) -> Self { + Self { + destination_address, + content, + } } } diff --git a/src/net/net_worker.rs b/src/net/net_worker.rs index f5ee122..70b981e 100644 --- a/src/net/net_worker.rs +++ b/src/net/net_worker.rs @@ -1,7 +1,7 @@ use crate::net::net_messages::{ AddTcpConnection, AddUdpSocket, ReceiveTcpMessage, ReceiveUdpMessage, RemoveTcpConnection, }; -use crate::prelude::{Actor, ActorContext, ActorFactory, ActorResult, Handler}; +use crate::prelude::{Actor, ActorContext, ActorFactory, ActorResult, Handler, SerializedMessage}; use io_arc::IoArc; use log::{debug, warn}; use mio::net::{TcpStream, UdpSocket}; @@ -33,6 +33,11 @@ impl Actor for NetWorker { //we only react if the actor is explicitly stopped by the manager, because there might still be open connections that we don't want to drop Ok(ActorResult::Ok) } + + fn handle_serialized_message(&mut self, _msg: SerializedMessage, _context: &ActorContext) -> Result> { + //handle all outgoing messages here + return Ok(ActorResult::Ok); + } } #[derive(Clone)] diff --git a/src/system/actor_system.rs b/src/system/actor_system.rs index 8466b81..34cf0fa 100644 --- a/src/system/actor_system.rs +++ b/src/system/actor_system.rs @@ -55,7 +55,12 @@ impl ActorSystem { thread_pool_manager.add_pool_with_config(key, value.clone()); thread_pool_max_actors.insert(key.clone(), value.actor_limit); } - let state = SystemState::new(wakeup_manager.clone(), Arc::new(thread_pool_max_actors)); + + //start the net_manager and provide the net_worker_lb_address to the systemstate + //also properly fill out remote_name in ActorAddress. hostname of the system is probably a good choice, will in most cases be equal to the system_name but that's okay + let net_worker_lb_address = ActorAddress::new("", "", "", ""); + let remote_name = "todo".into(); + let state = SystemState::new(wakeup_manager.clone(), Arc::new(thread_pool_max_actors), net_worker_lb_address, config.general.name.clone(), remote_name); let s = state.clone(); let t = thread_pool_manager.clone(); @@ -216,9 +221,9 @@ impl ActorSystem { /// let actor_system = ActorSystem::new(actor_config); /// let actor_wrapper = actor_system.builder().spawn("test", TestFactory{}).unwrap(); /// let address = actor_wrapper.get_address(); - /// actor_system.send_to_address(address, SerializedMessage::new(Vec::new())); + /// actor_system.send_to_address(address, Vec::new()); /// ``` - pub fn send_to_address(&self, address: &ActorAddress, msg: SerializedMessage) { + pub fn send_to_address<>(&self, address: &ActorAddress, msg: Vec) { self.state.send_to_address(address, msg); } diff --git a/src/system/system_state.rs b/src/system/system_state.rs index 30faace..075417a 100644 --- a/src/system/system_state.rs +++ b/src/system/system_state.rs @@ -23,12 +23,18 @@ pub struct SystemState { is_force_stopped: Arc, forced_exit_code: Arc, use_forced_exit_code: Arc, + net_worker_lb_address: ActorAddress, + system_name: String, + remote_name: String, } impl SystemState { pub fn new( wakeup_manager: WakeupManager, max_actors_per_pool: Arc>, + net_worker_lb_address: ActorAddress, + system_name: String, + remote_name: String, ) -> Self { Self { mailboxes: Arc::new(DashMap::new()), @@ -41,6 +47,9 @@ impl SystemState { is_force_stopped: Arc::new(AtomicBool::new(false)), forced_exit_code: Arc::new(AtomicI32::new(0)), use_forced_exit_code: Arc::new(AtomicBool::new(false)), + net_worker_lb_address, + system_name, + remote_name, } } @@ -100,11 +109,17 @@ impl SystemState { self.total_actor_count.load(Ordering::Relaxed) } - pub fn send_to_address(&self, address: &ActorAddress, msg: SerializedMessage) { - let target = self.mailboxes.get(address); + pub fn send_to_address(&self, address: &ActorAddress, msg: Vec) { + let target = if address.system == self.system_name && address.remote == self.remote_name + { + self.mailboxes.get(address) + } else { + self.mailboxes.get(&self.net_worker_lb_address) + }; + if target.is_some() { let target = target.unwrap(); - target.send_serialized(msg); + target.send_serialized(SerializedMessage::new(address.clone(), msg)); if target.is_sleeping() { self.wakeup_manager.wakeup(target.key().clone()); } diff --git a/src/wrapper/actor_wrapper.rs b/src/wrapper/actor_wrapper.rs index fe4f122..89ad412 100644 --- a/src/wrapper/actor_wrapper.rs +++ b/src/wrapper/actor_wrapper.rs @@ -133,7 +133,6 @@ where return self.remote.sleep(duration, self.address.clone()); } - /// Returns a reference to the address of the actor /// Returns a reference to the address of the actor pub fn get_address(&self) -> &ActorAddress { &self.address diff --git a/src/wrapper/remote_actor_wrapper.rs b/src/wrapper/remote_actor_wrapper.rs index 88dea18..ec87572 100644 --- a/src/wrapper/remote_actor_wrapper.rs +++ b/src/wrapper/remote_actor_wrapper.rs @@ -1,11 +1,11 @@ use crate::actor::actor_address::ActorAddress; use crate::actor::actor_send_error::ActorSendError; use crate::message::actor_message::BaseActorMessage; -use crate::prelude::SerializedMessage; use crate::system::system_state::SystemState; use serde::{Deserialize, Serialize}; use std::hash::{Hash, Hasher}; use std::time::Duration; +use crate::prelude::ActorSendError::NotAllowedForRemoteActorError; #[derive(Serialize, Deserialize, Clone)] pub struct RemoteActorWrapper { @@ -23,27 +23,26 @@ impl RemoteActorWrapper { where M: BaseActorMessage + 'static, { - //todo this needs to forward to the NetWorker - //the Networker should then forward this to the remote actor system - //the remote actor system should then forward the message using system_state let serialized = bincode::serialize(&msg).unwrap(); self.system_state .as_ref() .unwrap() - .send_to_address(address, SerializedMessage::new(serialized)); + .send_to_address(address, serialized); return Ok(()); } pub fn send_timeout( &self, - _msg: M, + msg: M, _timeout: Duration, - _address: ActorAddress, + address: ActorAddress, ) -> Result<(), ActorSendError> where M: BaseActorMessage + 'static, { - return Ok(()); + // since we don't work with the mailbox directly for remote actors, we can't send messages with timeout + // instead of giving an error, we'll simply send the message and ignore the timeout + return self.send(msg, &address); } pub fn send_after( @@ -55,15 +54,17 @@ impl RemoteActorWrapper { where M: BaseActorMessage + 'static, { + //send serialized version of DelayedMessage to system_state + //destination address in SerializedMessage needs to be the delay-router on the remote system return Ok(()); } pub fn stop(&self) -> Result<(), ActorSendError> { - return Ok(()); + return Err(NotAllowedForRemoteActorError); } pub fn sleep(&self, _duration: Duration, _address: ActorAddress) -> Result<(), ActorSendError> { - return Ok(()); + return Err(NotAllowedForRemoteActorError); } pub fn get_mailbox_size(&self) -> usize { @@ -71,11 +72,11 @@ impl RemoteActorWrapper { } pub fn is_mailbox_stopped(&self) -> bool { - return true; + return false; } pub fn is_stopped(&self) -> bool { - return true; + return false; } pub fn wait_for_stop(&self) { From e6e9aa247a7c4bc8b50c6f384b57debe26e465a0 Mon Sep 17 00:00:00 2001 From: Bobonium Date: Wed, 10 May 2023 21:11:13 +0200 Subject: [PATCH 31/44] fix incorrectly hardcoded remote string --- src/system/actor_system.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/system/actor_system.rs b/src/system/actor_system.rs index 34cf0fa..5256e3d 100644 --- a/src/system/actor_system.rs +++ b/src/system/actor_system.rs @@ -59,7 +59,7 @@ impl ActorSystem { //start the net_manager and provide the net_worker_lb_address to the systemstate //also properly fill out remote_name in ActorAddress. hostname of the system is probably a good choice, will in most cases be equal to the system_name but that's okay let net_worker_lb_address = ActorAddress::new("", "", "", ""); - let remote_name = "todo".into(); + let remote_name = "local".into(); let state = SystemState::new(wakeup_manager.clone(), Arc::new(thread_pool_max_actors), net_worker_lb_address, config.general.name.clone(), remote_name); let s = state.clone(); From dcf8f4baaddb5bd059e7649f00e218c4697ee108 Mon Sep 17 00:00:00 2001 From: Bobonium Date: Wed, 10 May 2023 21:41:56 +0200 Subject: [PATCH 32/44] add new hostname config and rename ActorAddress.remote to ActorAddress.hostname --- CHANGELOG.md | 3 +++ src/actor/actor_address.rs | 6 +++--- src/actor/actor_builder.rs | 2 +- src/config/default.toml | 5 ++++- src/config/global_config.rs | 1 + src/config/tyra_config.rs | 7 +++++-- src/system/actor_system.rs | 26 +++++++++++++++++++++++--- src/system/system_state.rs | 8 ++++---- 8 files changed, 44 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f1b661a..a85af1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,9 @@ - All routers now support a `SendToAllTargetsMessage` that will forward `M` to all active targets - Users can now use `ActorBuilder.get_existing(address: ActorAddress)` to get an `ActorWrapper` for an already existing `Actor` - `ActorBuilder.spawn()` will continue to return the `ActorWrapper` for already existing actors + - renamed config `global.name` to `global.hostname` + - also renamed `ActorAddress.remote` to `ActorAddress.hostname` + - added new config `global.name` that defaults to the cargo package name or `tyra` if the application is not built using cargo # 1.0.0 diff --git a/src/actor/actor_address.rs b/src/actor/actor_address.rs index 7fb3055..b8defef 100644 --- a/src/actor/actor_address.rs +++ b/src/actor/actor_address.rs @@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize}; #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Serialize, Deserialize)] pub struct ActorAddress { - pub remote: String, + pub hostname: String, pub system: String, pub pool: String, pub actor: String, @@ -10,13 +10,13 @@ pub struct ActorAddress { impl ActorAddress { pub fn new( - remote: impl Into, + hostname: impl Into, system: impl Into, pool: impl Into, actor: impl Into, ) -> Self { return Self { - remote: remote.into(), + hostname: hostname.into(), system: system.into(), pool: pool.into(), actor: actor.into(), diff --git a/src/actor/actor_builder.rs b/src/actor/actor_builder.rs index d2dda27..cf7ef06 100644 --- a/src/actor/actor_builder.rs +++ b/src/actor/actor_builder.rs @@ -186,7 +186,7 @@ where P: ActorFactory + 'static, { let actor_address = ActorAddress::new( - "local", + self.system.get_hostname(), self.system.get_name(), self.actor_config.pool_name.clone(), name, diff --git a/src/config/default.toml b/src/config/default.toml index 4c8ad45..e6cf73f 100644 --- a/src/config/default.toml +++ b/src/config/default.toml @@ -2,8 +2,11 @@ # general actor settings [general] # name of the actor system +# magic default: name of the cargo package; defaults to "tyra" if not built with cargo +name = "$CARGO_PKG_NAME" +# hostname of the actor system # magic default: system hostname -name = "$HOSTNAME" +hostname = "$HOSTNAME" # default mailbox size for every actor if no explicit size is set # 0 is treated as unlimited default_mailbox_size = 0 diff --git a/src/config/global_config.rs b/src/config/global_config.rs index 195f3c6..51f7f38 100644 --- a/src/config/global_config.rs +++ b/src/config/global_config.rs @@ -3,6 +3,7 @@ use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize, Deserialize, Clone)] pub struct GeneralConfig { pub name: String, + pub hostname: String, pub default_mailbox_size: usize, pub default_message_throughput: usize, pub override_panic_hook: bool, diff --git a/src/config/tyra_config.rs b/src/config/tyra_config.rs index cf34275..4da050a 100644 --- a/src/config/tyra_config.rs +++ b/src/config/tyra_config.rs @@ -46,8 +46,11 @@ impl TyraConfig { let conf = config.build().expect("Could not fetch Config"); let mut parsed: TyraConfig = conf.try_deserialize().expect("Could not parse Config"); - if parsed.general.name == "$HOSTNAME" { - parsed.general.name = String::from(hostname::get().unwrap().to_str().unwrap()); + if parsed.general.hostname == "$HOSTNAME" { + parsed.general.hostname = String::from(hostname::get().unwrap().to_str().unwrap()); + } + if parsed.general.name == "$CARGO_PKG_NAME" { + parsed.general.name = option_env!("CARGO_PKG_NAME").unwrap_or("tyra").into(); } Ok(parsed) diff --git a/src/system/actor_system.rs b/src/system/actor_system.rs index 5256e3d..2f47411 100644 --- a/src/system/actor_system.rs +++ b/src/system/actor_system.rs @@ -21,6 +21,7 @@ pub struct ActorSystem { thread_pool_manager: ThreadPoolManager, wakeup_manager: WakeupManager, name: String, + hostname: String, config: Arc, internal_actor_manager: InternalActorManager, sigint_received: Arc, @@ -58,9 +59,8 @@ impl ActorSystem { //start the net_manager and provide the net_worker_lb_address to the systemstate //also properly fill out remote_name in ActorAddress. hostname of the system is probably a good choice, will in most cases be equal to the system_name but that's okay - let net_worker_lb_address = ActorAddress::new("", "", "", ""); - let remote_name = "local".into(); - let state = SystemState::new(wakeup_manager.clone(), Arc::new(thread_pool_max_actors), net_worker_lb_address, config.general.name.clone(), remote_name); + let net_worker_lb_address = ActorAddress::new(config.general.hostname.clone(), config.general.name.clone(), "", ""); + let state = SystemState::new(wakeup_manager.clone(), Arc::new(thread_pool_max_actors), net_worker_lb_address, config.general.name.clone(), config.general.hostname.clone()); let s = state.clone(); let t = thread_pool_manager.clone(); @@ -79,6 +79,7 @@ impl ActorSystem { thread_pool_manager, wakeup_manager, name: config.general.name.clone(), + hostname: config.general.hostname.clone(), config: Arc::new(config.clone()), internal_actor_manager: InternalActorManager::new(), sigint_received: Arc::new(AtomicBool::new(false)), @@ -379,4 +380,23 @@ impl ActorSystem { pub fn get_name(&self) -> &str { &self.name } + + /// Returns the configured hostname of the system + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rust + /// use tyra::prelude::{TyraConfig, ActorSystem, ThreadPoolConfig}; + /// use std::time::Duration; + /// use std::process::exit; + /// + /// let actor_config = TyraConfig::new().unwrap(); + /// let actor_system = ActorSystem::new(actor_config); + /// let name = actor_system.get_hostname(); + /// ``` + pub fn get_hostname(&self) -> &str { + &self.hostname + } } diff --git a/src/system/system_state.rs b/src/system/system_state.rs index 075417a..2407e4f 100644 --- a/src/system/system_state.rs +++ b/src/system/system_state.rs @@ -25,7 +25,7 @@ pub struct SystemState { use_forced_exit_code: Arc, net_worker_lb_address: ActorAddress, system_name: String, - remote_name: String, + hostname: String, } impl SystemState { @@ -34,7 +34,7 @@ impl SystemState { max_actors_per_pool: Arc>, net_worker_lb_address: ActorAddress, system_name: String, - remote_name: String, + hostname: String, ) -> Self { Self { mailboxes: Arc::new(DashMap::new()), @@ -49,7 +49,7 @@ impl SystemState { use_forced_exit_code: Arc::new(AtomicBool::new(false)), net_worker_lb_address, system_name, - remote_name, + hostname, } } @@ -110,7 +110,7 @@ impl SystemState { } pub fn send_to_address(&self, address: &ActorAddress, msg: Vec) { - let target = if address.system == self.system_name && address.remote == self.remote_name + let target = if address.system == self.system_name && address.hostname == self.hostname { self.mailboxes.get(address) } else { From f7e16c4d3858e8f845a22321730eee540f446c5d Mon Sep 17 00:00:00 2001 From: Bobonium Date: Fri, 19 May 2023 20:38:45 +0200 Subject: [PATCH 33/44] allow dynamic router injection into net_manager --- examples/net.rs | 3 +++ src/net/net_manager.rs | 48 +++++++++++++++++++++++++---------- src/routers/mod.rs | 1 + src/routers/sharded_router.rs | 7 +++++ 4 files changed, 46 insertions(+), 13 deletions(-) diff --git a/examples/net.rs b/examples/net.rs index 35373ec..57c0dbb 100644 --- a/examples/net.rs +++ b/examples/net.rs @@ -3,6 +3,7 @@ use tyra::prelude::{ ActorSystem, NetConfig, NetManagerFactory, NetProtocol, NetWorkerFactory, ThreadPoolConfig, TyraConfig, }; +use tyra::router::ShardedRouterFactory; fn main() { // generate config @@ -20,6 +21,7 @@ fn main() { net_configs.push(NetConfig::new(NetProtocol::TCP, "0.0.0.0", 2022)); let worker_factory = NetWorkerFactory::new(); + let router_factory = ShardedRouterFactory::new(false, false); let _actor = actor_system .builder() .set_pool_name("mio") @@ -30,6 +32,7 @@ fn main() { Duration::from_secs(10), Duration::from_secs(3), worker_factory, + router_factory, 3, ), ) diff --git a/src/net/net_manager.rs b/src/net/net_manager.rs index 4e63e42..d977f7d 100644 --- a/src/net/net_manager.rs +++ b/src/net/net_manager.rs @@ -5,7 +5,7 @@ use crate::prelude::{ Actor, ActorContext, ActorFactory, ActorInitMessage, ActorResult, ActorWrapper, Handler, NetConfig, NetProtocol, }; -use crate::router::{AddActorMessage, ShardedRouter, ShardedRouterFactory}; +use crate::router::{AddActorMessage, Router}; use io_arc::IoArc; use log::{debug, error, warn}; use mio::event::Source; @@ -23,7 +23,7 @@ use std::thread; use std::thread::sleep; use std::time::{Duration, Instant}; -pub struct NetManager +pub struct NetManager where T: Handler + Handler @@ -31,17 +31,18 @@ where + Handler + Handler + 'static, + R: Router + Handler + Handler + Handler + Handler + Handler, { graceful_shutdown_time_in_seconds: Duration, on_stop_udp_timeout: Duration, - router: ActorWrapper>, + router: ActorWrapper, workers: Vec>, net_configs: Vec, is_stopping: Arc, is_stopped: Arc, } -impl NetManager +impl NetManager where T: Handler + Handler @@ -49,17 +50,21 @@ where + Handler + Handler + 'static, + R: Router + Handler + Handler + Handler + Handler + Handler, { - pub fn new( + pub fn new( context: ActorContext, net_configs: Vec, graceful_shutdown_time_in_seconds: Duration, on_stop_udp_timeout: Duration, worker_factory: F, + router_factory: FF, max_worker_count: usize, ) -> Self where F: ActorFactory + Clone + 'static, + FF: ActorFactory + Clone + 'static, + R: Router + Handler + Handler + Handler + Handler + Handler, { let pool_name = &context.actor_ref.get_address().pool; @@ -68,7 +73,7 @@ where .system .builder() .set_pool_name(pool_name) - .spawn(router_name, ShardedRouterFactory::new(false, false)) + .spawn(router_name, router_factory) .unwrap(); let max_pool_count = context .system @@ -100,7 +105,7 @@ where }; } } -impl Actor for NetManager +impl Actor for NetManager where T: Handler + Handler @@ -108,6 +113,8 @@ where + Handler + Handler + 'static, + R: Router + Handler + Handler + Handler + Handler + Handler, + { fn pre_stop(&mut self, _context: &ActorContext) { let iterations = 10; @@ -171,8 +178,9 @@ where } } -pub struct NetManagerFactory +pub struct NetManagerFactory where + F: ActorFactory + Clone + 'static, T: Handler + Handler @@ -180,16 +188,20 @@ where + Handler + Handler + 'static, + FF: ActorFactory + Clone + 'static, + R: Router + Handler + Handler + Handler + Handler + Handler, { net_configs: Vec, graceful_shutdown_time_in_seconds: Duration, on_stop_udp_timeout: Duration, worker_factory: F, + router_factory: FF, max_worker_count: usize, phantom: PhantomData, + phantom_two: PhantomData } -impl NetManagerFactory +impl NetManagerFactory where F: ActorFactory + Clone + 'static, T: Handler @@ -198,12 +210,15 @@ where + Handler + Handler + 'static, + FF: ActorFactory + Clone + 'static, + R: Router + Handler + Handler + Handler + Handler + Handler, { pub fn new( net_configs: Vec, graceful_shutdown_time_in_seconds: Duration, on_stop_udp_timeout: Duration, worker_factory: F, + router_factory: FF, max_worker_count: usize, ) -> Self { return Self { @@ -211,12 +226,14 @@ where graceful_shutdown_time_in_seconds, on_stop_udp_timeout, worker_factory, + router_factory, max_worker_count, phantom: PhantomData, + phantom_two: PhantomData }; } } -impl ActorFactory> for NetManagerFactory +impl ActorFactory> for NetManagerFactory where F: ActorFactory + Clone + 'static, T: Handler @@ -225,11 +242,13 @@ where + Handler + Handler + 'static, + FF: ActorFactory + Clone + 'static, + R: Router + Handler + Handler + Handler + Handler + Handler, { fn new_actor( &mut self, - context: ActorContext>, - ) -> Result, Box> { + context: ActorContext>, + ) -> Result, Box> { context.actor_ref.send(ActorInitMessage::new()).unwrap(); return Ok(NetManager::new( context, @@ -237,12 +256,13 @@ where self.graceful_shutdown_time_in_seconds, self.on_stop_udp_timeout, self.worker_factory.clone(), + self.router_factory.clone(), self.max_worker_count, )); } } -impl Handler for NetManager +impl Handler for NetManager where T: Handler + Handler @@ -250,6 +270,8 @@ where + Handler + Handler + 'static, + R: Router + Handler + Handler + Handler + Handler + Handler, + { fn handle( &mut self, diff --git a/src/routers/mod.rs b/src/routers/mod.rs index 4e44794..95c0b9e 100644 --- a/src/routers/mod.rs +++ b/src/routers/mod.rs @@ -17,4 +17,5 @@ pub mod prelude { pub use crate::routers::send_to_all_targets_message::SendToAllTargetsMessage; pub use crate::routers::sharded_router::ShardedRouter; pub use crate::routers::sharded_router::ShardedRouterFactory; + pub use crate::routers::sharded_router::Router; } diff --git a/src/routers/sharded_router.rs b/src/routers/sharded_router.rs index 7c19a9f..2a60274 100644 --- a/src/routers/sharded_router.rs +++ b/src/routers/sharded_router.rs @@ -13,6 +13,12 @@ use log::{debug, error, info, warn}; use std::collections::HashMap; use std::error::Error; +pub trait Router: Actor + Handler>{} + +impl Router for ShardedRouter + where + A: Actor {} + pub struct ShardedRouter where A: Actor, @@ -85,6 +91,7 @@ where /// router.send(AddActorMessage::new(actor.clone())).unwrap(); /// router.send(FooBar{}).unwrap(); /// ``` +#[derive(Clone)] pub struct ShardedRouterFactory { /// defines if the actor should automatically be stopped when the system is stopped. If set to false it's up to the user to setup their own shutdown process if they want a quick and clean exit stop_on_system_stop: bool, From e1cf8465155a9a1d47cf0fb3c1a53fd79e5f42a9 Mon Sep 17 00:00:00 2001 From: Bobonium Date: Fri, 19 May 2023 20:59:42 +0200 Subject: [PATCH 34/44] remove redundant '<>' --- src/system/actor_system.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/system/actor_system.rs b/src/system/actor_system.rs index 2f47411..138aabe 100644 --- a/src/system/actor_system.rs +++ b/src/system/actor_system.rs @@ -224,7 +224,7 @@ impl ActorSystem { /// let address = actor_wrapper.get_address(); /// actor_system.send_to_address(address, Vec::new()); /// ``` - pub fn send_to_address<>(&self, address: &ActorAddress, msg: Vec) { + pub fn send_to_address(&self, address: &ActorAddress, msg: Vec) { self.state.send_to_address(address, msg); } From 2898687bca9224fb2982d18f85a79086417598f1 Mon Sep 17 00:00:00 2001 From: Bobonium Date: Sun, 21 May 2023 22:02:27 +0200 Subject: [PATCH 35/44] WIP: start cluster integration into actor_system --- Cargo.toml | 1 + examples/net.rs | 29 +----------- src/actor/actor_builder.rs | 2 +- src/config/cluster_config.rs | 7 +++ src/config/default.toml | 25 ++++++++-- src/config/mod.rs | 3 ++ src/config/tyra_config.rs | 4 ++ src/message/serialized_message.rs | 9 +++- src/net/net_manager.rs | 77 +++++++----------------------- src/system/actor_system.rs | 78 ++++++++++++++++++++++++++++--- 10 files changed, 136 insertions(+), 99 deletions(-) create mode 100644 src/config/cluster_config.rs diff --git a/Cargo.toml b/Cargo.toml index bb0cde2..8bb5dba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,7 @@ quiche = "0.17.1" io-arc = "1.0.0" hashring = "0.3.0" bincode = "1.3.3" +regex = "1.8.1" [dev-dependencies] ntest = "0.9.0" \ No newline at end of file diff --git a/examples/net.rs b/examples/net.rs index 57c0dbb..a68ffe5 100644 --- a/examples/net.rs +++ b/examples/net.rs @@ -1,13 +1,9 @@ -use std::time::Duration; -use tyra::prelude::{ - ActorSystem, NetConfig, NetManagerFactory, NetProtocol, NetWorkerFactory, ThreadPoolConfig, - TyraConfig, -}; -use tyra::router::ShardedRouterFactory; +use tyra::prelude::*; fn main() { // generate config let mut actor_config = TyraConfig::new().unwrap(); + actor_config.cluster.enabled = true; let cluster = ThreadPoolConfig::new(22, 4, 4, 1.00); actor_config .thread_pool @@ -15,28 +11,7 @@ fn main() { .insert(String::from("mio"), cluster); // start system with config let actor_system = ActorSystem::new(actor_config); - // create actor on the system - let mut net_configs = Vec::new(); - net_configs.push(NetConfig::new(NetProtocol::UDP, "0.0.0.0", 2023)); - net_configs.push(NetConfig::new(NetProtocol::TCP, "0.0.0.0", 2022)); - let worker_factory = NetWorkerFactory::new(); - let router_factory = ShardedRouterFactory::new(false, false); - let _actor = actor_system - .builder() - .set_pool_name("mio") - .spawn( - "test", - NetManagerFactory::new( - net_configs, - Duration::from_secs(10), - Duration::from_secs(3), - worker_factory, - router_factory, - 3, - ), - ) - .unwrap(); std::process::exit(actor_system.await_shutdown()); } diff --git a/src/actor/actor_builder.rs b/src/actor/actor_builder.rs index cf7ef06..7686293 100644 --- a/src/actor/actor_builder.rs +++ b/src/actor/actor_builder.rs @@ -312,7 +312,7 @@ where /// /// let pool_name = "default"; /// let actor_name = "test"; - /// let address = ActorAddress::new("local", actor_system.get_name(), pool_name, actor_name); + /// let address = ActorAddress::new(actor_system.get_hostname(), actor_system.get_name(), pool_name, actor_name); /// /// //this does not work, because the actor does not exist yet /// let this_is_not_working :Result, ActorError> = actor_system.builder().get_existing(&address); diff --git a/src/config/cluster_config.rs b/src/config/cluster_config.rs new file mode 100644 index 0000000..7c9209c --- /dev/null +++ b/src/config/cluster_config.rs @@ -0,0 +1,7 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct ClusterConfig { + pub enabled: bool, + pub hosts: Vec, +} diff --git a/src/config/default.toml b/src/config/default.toml index e6cf73f..6f0d17d 100644 --- a/src/config/default.toml +++ b/src/config/default.toml @@ -41,6 +41,27 @@ threads_max = 3 # num_cpu * factor = amount of threads to spawn for this pool threads_factor = 1 +# cluster pool settings +# only enabled if `cluster.enabled` is set to `true` +[thread_pool.config.cluster] +# amount of actors that this thread_pool can handle +# 0 is treated as unlimited +actor_limit = 5 +# minimum amount of threads to spawn for this pool +threads_min = 2 +# maximum amount of threads to spawn for this pool +threads_max = 5 +# num_cpu * factor = amount of threads to spawn for this pool +threads_factor = 1 + +# cluster configuration +[cluster] +# defines if cluster functionality should be enabled +enabled = false +## addresses and port used by the cluster. +## if port is omitted, the default of 2022 is used +## if port is set to 0, a port will automatically be assigned +hosts = ["tcp://0.0.0.0:2022", "udp://0.0.0.0:2023"] ##WIP: How cluster config could look like ##### @@ -48,9 +69,7 @@ threads_factor = 1 # ## default cluster settings #[cluster] -## addresses and port used by the cluster. -## if port is omitted, the default of 2022 is used -## if port is set to 0, a port will automatically be assigned + #hosts = ["tcp://127.0.0.1:2022", "tcp://192.168.0.1:2022", "udp://0.0.0.0:2023"] # ## cluster groups the node should be part of. Valid values are "server", "client" and "proxy" diff --git a/src/config/mod.rs b/src/config/mod.rs index d1b8c07..8429384 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1,8 +1,11 @@ pub mod global_config; pub mod pool_config; pub mod tyra_config; +pub mod cluster_config; pub mod prelude { + pub use crate::config::global_config::GeneralConfig; pub use crate::config::pool_config::ThreadPoolConfig; pub use crate::config::tyra_config::TyraConfig; + pub use crate::config::cluster_config::ClusterConfig; } diff --git a/src/config/tyra_config.rs b/src/config/tyra_config.rs index 4da050a..7d2cf2e 100644 --- a/src/config/tyra_config.rs +++ b/src/config/tyra_config.rs @@ -4,14 +4,18 @@ use std::path::Path; use config::{Config, ConfigError, File, FileFormat}; use serde::{Deserialize, Serialize}; +use crate::prelude::ClusterConfig; pub const DEFAULT_POOL: &str = "default"; +pub const NET_CLUSTER_POOL: &str = "cluster"; +pub const NET_CLUSTER_LB: &str = "cluster-router"; /// See [default.toml](https://github.com/sers-dev/tyra/blob/master/src/config/default.toml) for documentation of all configurations & their defaults #[derive(Debug, Serialize, Deserialize, Clone)] pub struct TyraConfig { pub general: GeneralConfig, pub thread_pool: PoolConfig, + pub cluster: ClusterConfig, } impl TyraConfig { diff --git a/src/message/serialized_message.rs b/src/message/serialized_message.rs index 38d25c0..e2ef896 100644 --- a/src/message/serialized_message.rs +++ b/src/message/serialized_message.rs @@ -1,3 +1,4 @@ +use std::hash::{Hash, Hasher}; use crate::message::actor_message::DefaultActorMessage; use serde::{Deserialize, Serialize}; use crate::prelude::ActorAddress; @@ -10,12 +11,18 @@ use crate::prelude::ActorAddress; /// and it may also include some additional fields to make deserialization easier for end users /// /// [ActorSystem.send_to_address](../prelude/struct.ActorSystem.html#method.send_to_address) uses this object to send serialized messages to Actors -#[derive(Hash, Serialize, Deserialize)] +#[derive(Serialize, Deserialize)] pub struct SerializedMessage { pub destination_address: ActorAddress, pub content: Vec, } +impl Hash for SerializedMessage { + fn hash(&self, state: &mut H) { + self.destination_address.hash(state); + } +} + impl SerializedMessage { pub fn new(destination_address: ActorAddress, content: Vec) -> Self { Self { diff --git a/src/net/net_manager.rs b/src/net/net_manager.rs index d977f7d..793b2d9 100644 --- a/src/net/net_manager.rs +++ b/src/net/net_manager.rs @@ -5,13 +5,12 @@ use crate::prelude::{ Actor, ActorContext, ActorFactory, ActorInitMessage, ActorResult, ActorWrapper, Handler, NetConfig, NetProtocol, }; -use crate::router::{AddActorMessage, Router}; +use crate::router::Router; use io_arc::IoArc; use log::{debug, error, warn}; use mio::event::Source; use mio::net::{TcpListener, TcpStream, UdpSocket}; use mio::{Events, Interest, Poll, Token}; -use std::cmp::min; use std::collections::HashMap; use std::error::Error; use std::io::{BufRead, BufReader}; @@ -52,45 +51,14 @@ where + 'static, R: Router + Handler + Handler + Handler + Handler + Handler, { - pub fn new( - context: ActorContext, + pub fn new( net_configs: Vec, graceful_shutdown_time_in_seconds: Duration, on_stop_udp_timeout: Duration, - worker_factory: F, - router_factory: FF, - max_worker_count: usize, + workers: Vec>, + router: ActorWrapper, ) -> Self - where - F: ActorFactory + Clone + 'static, - FF: ActorFactory + Clone + 'static, - R: Router + Handler + Handler + Handler + Handler + Handler, { - let pool_name = &context.actor_ref.get_address().pool; - - let router_name = format!("{}-net-router", context.actor_ref.get_address().actor); - let router = context - .system - .builder() - .set_pool_name(pool_name) - .spawn(router_name, router_factory) - .unwrap(); - let max_pool_count = context - .system - .get_available_actor_count_for_pool(pool_name) - .unwrap(); - let worker_count = min(max_worker_count, max_pool_count); - let worker_name = format!("{}-net-worker", context.actor_ref.get_address().actor); - let workers = context - .system - .builder() - .set_pool_name(pool_name) - .spawn_multiple(worker_name, worker_factory.clone(), worker_count) - .unwrap(); - for worker in &workers { - router.send(AddActorMessage::new(worker.clone())).unwrap(); - } - let is_stopping = Arc::new(AtomicBool::new(false)); let is_stopped = Arc::new(AtomicBool::new(false)); @@ -178,71 +146,60 @@ where } } -pub struct NetManagerFactory +pub struct NetManagerFactory where - F: ActorFactory + Clone + 'static, T: Handler + Handler + Handler + Handler + Handler + 'static, - FF: ActorFactory + Clone + 'static, R: Router + Handler + Handler + Handler + Handler + Handler, { net_configs: Vec, graceful_shutdown_time_in_seconds: Duration, on_stop_udp_timeout: Duration, - worker_factory: F, - router_factory: FF, - max_worker_count: usize, - phantom: PhantomData, - phantom_two: PhantomData + workers: Vec>, + router: ActorWrapper, + phantom: PhantomData } -impl NetManagerFactory +impl NetManagerFactory where - F: ActorFactory + Clone + 'static, T: Handler + Handler + Handler + Handler + Handler + 'static, - FF: ActorFactory + Clone + 'static, R: Router + Handler + Handler + Handler + Handler + Handler, { pub fn new( net_configs: Vec, graceful_shutdown_time_in_seconds: Duration, on_stop_udp_timeout: Duration, - worker_factory: F, - router_factory: FF, - max_worker_count: usize, + workers: Vec>, + router: ActorWrapper, ) -> Self { return Self { net_configs, graceful_shutdown_time_in_seconds, on_stop_udp_timeout, - worker_factory, - router_factory, - max_worker_count, + workers, + router, phantom: PhantomData, - phantom_two: PhantomData }; } } -impl ActorFactory> for NetManagerFactory +impl ActorFactory> for NetManagerFactory where - F: ActorFactory + Clone + 'static, T: Handler + Handler + Handler + Handler + Handler + 'static, - FF: ActorFactory + Clone + 'static, R: Router + Handler + Handler + Handler + Handler + Handler, { fn new_actor( @@ -251,13 +208,11 @@ where ) -> Result, Box> { context.actor_ref.send(ActorInitMessage::new()).unwrap(); return Ok(NetManager::new( - context, self.net_configs.clone(), self.graceful_shutdown_time_in_seconds, self.on_stop_udp_timeout, - self.worker_factory.clone(), - self.router_factory.clone(), - self.max_worker_count, + self.workers.clone(), + self.router.clone(), )); } } diff --git a/src/system/actor_system.rs b/src/system/actor_system.rs index 138aabe..1f82643 100644 --- a/src/system/actor_system.rs +++ b/src/system/actor_system.rs @@ -1,9 +1,9 @@ use crate::actor::actor_address::ActorAddress; use crate::actor::actor_builder::ActorBuilder; use crate::config::pool_config::ThreadPoolConfig; -use crate::config::tyra_config::{TyraConfig, DEFAULT_POOL}; +use crate::config::tyra_config::{TyraConfig, DEFAULT_POOL, NET_CLUSTER_POOL, NET_CLUSTER_LB}; use crate::message::serialized_message::SerializedMessage; -use crate::prelude::{Actor, ActorError, Handler}; +use crate::prelude::{Actor, ActorError, Handler, NetConfig, NetManagerFactory, NetProtocol, NetWorkerFactory}; use crate::system::internal_actor_manager::InternalActorManager; use crate::system::system_state::SystemState; use crate::system::thread_pool_manager::ThreadPoolManager; @@ -13,6 +13,8 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use std::thread::sleep; use std::time::Duration; +use regex::Regex; +use crate::router::{AddActorMessage, ShardedRouterFactory}; /// Manages thread pools and actors #[derive(Clone)] @@ -45,7 +47,10 @@ impl ActorSystem { std::panic::set_hook(Box::new(|_| {})); } - let thread_pool_config = config.thread_pool.clone(); + let mut thread_pool_config = config.thread_pool.clone(); + if !config.cluster.enabled { + thread_pool_config.config.remove("cluster"); + } let thread_pool_manager = ThreadPoolManager::new(); let wakeup_manager = WakeupManager::new(); @@ -57,9 +62,8 @@ impl ActorSystem { thread_pool_max_actors.insert(key.clone(), value.actor_limit); } - //start the net_manager and provide the net_worker_lb_address to the systemstate - //also properly fill out remote_name in ActorAddress. hostname of the system is probably a good choice, will in most cases be equal to the system_name but that's okay - let net_worker_lb_address = ActorAddress::new(config.general.hostname.clone(), config.general.name.clone(), "", ""); + let net_worker_lb_address = ActorAddress::new(config.general.hostname.clone(), config.general.name.clone(), NET_CLUSTER_POOL, NET_CLUSTER_LB); + let state = SystemState::new(wakeup_manager.clone(), Arc::new(thread_pool_max_actors), net_worker_lb_address, config.general.name.clone(), config.general.hostname.clone()); let s = state.clone(); @@ -94,10 +98,72 @@ impl ActorSystem { } system.internal_actor_manager.init(system.clone()); + if config.cluster.enabled { + system.init_cluster(); + } system } + fn init_cluster(&self) { + let mut net_configs = Vec::new(); + let regex = Regex::new("(tcp|udp):\\/\\/(.*):(.*)").unwrap(); + for host in &self.config.cluster.hosts { + let captures = regex.captures(host); + if captures.is_none() { + return; + } + let captures = captures.unwrap(); + if captures.len() < 3 { + return; + } + let protocol = if &captures[1] == "tcp" { + NetProtocol::TCP + } else { + NetProtocol::UDP + }; + + let port = if captures.len() == 4 { + captures[3].parse::().unwrap() + } else { + 0 as usize + }; + + net_configs.push(NetConfig::new(protocol, &captures[2], port)); + + } + + let worker_factory = NetWorkerFactory::new(); + let router_factory = ShardedRouterFactory::new(false, false); + let router = self.builder().set_pool_name(NET_CLUSTER_POOL).spawn(NET_CLUSTER_LB, router_factory).unwrap(); + + let worker_count = self + .get_available_actor_count_for_pool(NET_CLUSTER_POOL) + .unwrap() - 1; + let workers = self + .builder() + .set_pool_name(NET_CLUSTER_POOL) + .spawn_multiple("cluster-worker", worker_factory.clone(), worker_count) + .unwrap(); + for worker in &workers { + router.send(AddActorMessage::new(worker.clone())).unwrap(); + } + let _actor = self + .builder() + .set_pool_name(NET_CLUSTER_POOL) + .spawn( + "cluster-manager", + NetManagerFactory::new( + net_configs, + Duration::from_secs(10), + Duration::from_secs(3), + workers, + router, + ), + ) + .unwrap(); + } + /// Adds a new named pool using the [default pool configuration](https://github.com/sers-dev/tyra/blob/master/src/config/default.toml) /// /// # Examples From 3c4673905f6d11b6ccd110331d1ecd3806ec1e0b Mon Sep 17 00:00:00 2001 From: Bobonium Date: Mon, 22 May 2023 18:37:45 +0200 Subject: [PATCH 36/44] WIP: implement clustering --- Cargo.toml | 1 + src/config/cluster_config.rs | 1 + src/config/default.toml | 8 +++ src/net/mod.rs | 1 + src/net/net_config.rs | 14 ++++- src/net/net_manager.rs | 9 +-- src/system/actor_system.rs | 66 +-------------------- src/system/cluster.rs | 110 +++++++++++++++++++++++++++++++++++ src/system/mod.rs | 1 + 9 files changed, 141 insertions(+), 70 deletions(-) create mode 100644 src/system/cluster.rs diff --git a/Cargo.toml b/Cargo.toml index 8bb5dba..62a09f7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,7 @@ io-arc = "1.0.0" hashring = "0.3.0" bincode = "1.3.3" regex = "1.8.1" +trust-dns-resolver = "0.22.0" [dev-dependencies] ntest = "0.9.0" \ No newline at end of file diff --git a/src/config/cluster_config.rs b/src/config/cluster_config.rs index 7c9209c..7964f88 100644 --- a/src/config/cluster_config.rs +++ b/src/config/cluster_config.rs @@ -4,4 +4,5 @@ use serde::{Deserialize, Serialize}; pub struct ClusterConfig { pub enabled: bool, pub hosts: Vec, + pub members: Vec, } diff --git a/src/config/default.toml b/src/config/default.toml index 6f0d17d..7db96da 100644 --- a/src/config/default.toml +++ b/src/config/default.toml @@ -62,6 +62,14 @@ enabled = false ## if port is omitted, the default of 2022 is used ## if port is set to 0, a port will automatically be assigned hosts = ["tcp://0.0.0.0:2022", "udp://0.0.0.0:2023"] +## list of cluster members, entries can include IPs and DNS records, multi A records are supported as well +## if the same node is listed multiple times with different ip addresses, only the first working occurence will be used as a connection +## if port is omitted, the default of 2022 is used +members = [ + "tcp://registry.git.sers.dev", + "tcp://git.sers.dev", + "tcp://abc.sers.dev:1234", +] ##WIP: How cluster config could look like ##### diff --git a/src/net/mod.rs b/src/net/mod.rs index 60bed6f..b324f44 100644 --- a/src/net/mod.rs +++ b/src/net/mod.rs @@ -6,6 +6,7 @@ mod net_worker; pub mod prelude { pub use crate::net::net_config::NetConfig; pub use crate::net::net_config::NetProtocol; + pub use crate::net::net_config::NetConnectionType; pub use crate::net::net_manager::NetManager; pub use crate::net::net_manager::NetManagerFactory; pub use crate::net::net_worker::NetWorker; diff --git a/src/net/net_config.rs b/src/net/net_config.rs index a60263f..1876084 100644 --- a/src/net/net_config.rs +++ b/src/net/net_config.rs @@ -1,20 +1,28 @@ -#[derive(Clone, Ord, Eq, PartialOrd, PartialEq, Copy)] +#[derive(Clone, Ord, Eq, PartialOrd, PartialEq, Copy, Debug)] pub enum NetProtocol { TCP, UDP, } -#[derive(Clone)] +#[derive(Clone, Ord, Eq, PartialOrd, PartialEq, Copy, Debug)] +pub enum NetConnectionType { + CLIENT, + SERVER +} + +#[derive(Clone, Ord, Eq, PartialOrd, PartialEq, Debug)] pub struct NetConfig { pub protocol: NetProtocol, + pub connection_type: NetConnectionType, pub host: String, pub port: usize, } impl NetConfig { - pub fn new(protocol: NetProtocol, host: impl Into, port: usize) -> Self { + pub fn new(protocol: NetProtocol, connection_type: NetConnectionType, host: impl Into, port: usize) -> Self { Self { protocol, + connection_type, host: host.into(), port, } diff --git a/src/net/net_manager.rs b/src/net/net_manager.rs index 793b2d9..004c147 100644 --- a/src/net/net_manager.rs +++ b/src/net/net_manager.rs @@ -1,10 +1,7 @@ use crate::net::net_messages::{ AddTcpConnection, AddUdpSocket, ReceiveTcpMessage, ReceiveUdpMessage, RemoveTcpConnection, }; -use crate::prelude::{ - Actor, ActorContext, ActorFactory, ActorInitMessage, ActorResult, ActorWrapper, Handler, - NetConfig, NetProtocol, -}; +use crate::prelude::{Actor, ActorContext, ActorFactory, ActorInitMessage, ActorResult, ActorWrapper, Handler, NetConfig, NetConnectionType, NetProtocol}; use crate::router::Router; use io_arc::IoArc; use log::{debug, error, warn}; @@ -255,6 +252,10 @@ where let mut i = 0; net_configs.sort_by_key(|c| c.protocol); for net_config in &net_configs { + if net_config.connection_type == NetConnectionType::CLIENT { + println!("TEST: {:?}", net_config); + continue; + } let token = Token(i); i += 1; diff --git a/src/system/actor_system.rs b/src/system/actor_system.rs index 1f82643..65ddca2 100644 --- a/src/system/actor_system.rs +++ b/src/system/actor_system.rs @@ -3,7 +3,7 @@ use crate::actor::actor_builder::ActorBuilder; use crate::config::pool_config::ThreadPoolConfig; use crate::config::tyra_config::{TyraConfig, DEFAULT_POOL, NET_CLUSTER_POOL, NET_CLUSTER_LB}; use crate::message::serialized_message::SerializedMessage; -use crate::prelude::{Actor, ActorError, Handler, NetConfig, NetManagerFactory, NetProtocol, NetWorkerFactory}; +use crate::prelude::{Actor, ActorError, Handler}; use crate::system::internal_actor_manager::InternalActorManager; use crate::system::system_state::SystemState; use crate::system::thread_pool_manager::ThreadPoolManager; @@ -13,8 +13,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use std::thread::sleep; use std::time::Duration; -use regex::Regex; -use crate::router::{AddActorMessage, ShardedRouterFactory}; +use crate::system::cluster::Cluster; /// Manages thread pools and actors #[derive(Clone)] @@ -99,71 +98,12 @@ impl ActorSystem { system.internal_actor_manager.init(system.clone()); if config.cluster.enabled { - system.init_cluster(); + Cluster::init(&system, &config.cluster); } system } - fn init_cluster(&self) { - let mut net_configs = Vec::new(); - let regex = Regex::new("(tcp|udp):\\/\\/(.*):(.*)").unwrap(); - for host in &self.config.cluster.hosts { - let captures = regex.captures(host); - if captures.is_none() { - return; - } - let captures = captures.unwrap(); - if captures.len() < 3 { - return; - } - let protocol = if &captures[1] == "tcp" { - NetProtocol::TCP - } else { - NetProtocol::UDP - }; - - let port = if captures.len() == 4 { - captures[3].parse::().unwrap() - } else { - 0 as usize - }; - - net_configs.push(NetConfig::new(protocol, &captures[2], port)); - - } - - let worker_factory = NetWorkerFactory::new(); - let router_factory = ShardedRouterFactory::new(false, false); - let router = self.builder().set_pool_name(NET_CLUSTER_POOL).spawn(NET_CLUSTER_LB, router_factory).unwrap(); - - let worker_count = self - .get_available_actor_count_for_pool(NET_CLUSTER_POOL) - .unwrap() - 1; - let workers = self - .builder() - .set_pool_name(NET_CLUSTER_POOL) - .spawn_multiple("cluster-worker", worker_factory.clone(), worker_count) - .unwrap(); - for worker in &workers { - router.send(AddActorMessage::new(worker.clone())).unwrap(); - } - let _actor = self - .builder() - .set_pool_name(NET_CLUSTER_POOL) - .spawn( - "cluster-manager", - NetManagerFactory::new( - net_configs, - Duration::from_secs(10), - Duration::from_secs(3), - workers, - router, - ), - ) - .unwrap(); - } - /// Adds a new named pool using the [default pool configuration](https://github.com/sers-dev/tyra/blob/master/src/config/default.toml) /// /// # Examples diff --git a/src/system/cluster.rs b/src/system/cluster.rs new file mode 100644 index 0000000..85ce0c1 --- /dev/null +++ b/src/system/cluster.rs @@ -0,0 +1,110 @@ +use std::time::Duration; +use log::warn; +use regex::Regex; +use trust_dns_resolver::config::{ResolverConfig, ResolverOpts}; +use trust_dns_resolver::error::ResolveResult; +use trust_dns_resolver::Resolver; +use crate::config::tyra_config::{NET_CLUSTER_LB, NET_CLUSTER_POOL}; +use crate::prelude::{ActorSystem, ClusterConfig, NetConfig, NetConnectionType, NetManagerFactory, NetProtocol, NetWorkerFactory}; +use crate::router::{AddActorMessage, ShardedRouterFactory}; + +pub struct Cluster { + +} + +impl Cluster { + + fn generate_net_config(from: &Vec, connection_type: NetConnectionType) -> Vec { + let regex = Regex::new("(tcp|udp):\\/\\/(.*):(.*)").unwrap(); + let mut net_configs = Vec::new(); + + for host in from { + let captures = regex.captures(host); + if captures.is_none() { + continue; + } + let captures = captures.unwrap(); + if captures.len() < 3 { + continue; + } + let protocol = if &captures[1] == "tcp" { + NetProtocol::TCP + } else { + NetProtocol::UDP + }; + + let port = if captures.len() == 4 { + captures[3].parse::().unwrap() + } else { + 2022 as usize + }; + + net_configs.push(NetConfig::new(protocol, connection_type, &captures[2], port)); + + } + return net_configs; + } + + fn resolve_dns(from: &Vec) -> Vec { + let mut to_return = Vec::new(); + for member in from { + let resolver = Resolver::new(ResolverConfig::default(), ResolverOpts::default()).unwrap(); + + let response = resolver.lookup_ip(&member.host); + match response { + ResolveResult::Ok(addresses) => { + for address in addresses.iter() { + let mut res = member.clone(); + res.host = format!("{}", address); + to_return.push(res); + } + } + _ => { + warn!("Can't find DNS records for '{}'", &member.host) + } + } + } + + to_return.sort(); + to_return.dedup(); + return to_return; + + } + + pub fn init(system: &ActorSystem, cluster_config: &ClusterConfig) { + + let server_configs = Self::generate_net_config(&cluster_config.hosts, NetConnectionType::SERVER); + let client_configs = Self::generate_net_config(&cluster_config.members, NetConnectionType::CLIENT); + let client_configs = Self::resolve_dns(&client_configs); + + let worker_factory = NetWorkerFactory::new(); + let router_factory = ShardedRouterFactory::new(false, false); + let router = system.builder().set_pool_name(NET_CLUSTER_POOL).spawn(NET_CLUSTER_LB, router_factory).unwrap(); + + let worker_count = system + .get_available_actor_count_for_pool(NET_CLUSTER_POOL) + .unwrap() - 1; + let workers = system + .builder() + .set_pool_name(NET_CLUSTER_POOL) + .spawn_multiple("cluster-worker", worker_factory.clone(), worker_count) + .unwrap(); + for worker in &workers { + router.send(AddActorMessage::new(worker.clone())).unwrap(); + } + let _actor = system + .builder() + .set_pool_name(NET_CLUSTER_POOL) + .spawn( + "cluster-manager", + NetManagerFactory::new( + server_configs, + Duration::from_secs(10), + Duration::from_secs(3), + workers, + router, + ), + ) + .unwrap(); + } +} \ No newline at end of file diff --git a/src/system/mod.rs b/src/system/mod.rs index 2ba3a18..657c97b 100644 --- a/src/system/mod.rs +++ b/src/system/mod.rs @@ -5,6 +5,7 @@ pub mod internal_actor_manager; pub mod system_state; mod thread_pool_manager; pub mod wakeup_manager; +mod cluster; pub mod prelude { pub use crate::system::actor_error::ActorError; From 72ed4f4c2d7996e576d5f6196ca5e298c40f80ff Mon Sep 17 00:00:00 2001 From: Bobonium Date: Mon, 22 May 2023 23:50:32 +0200 Subject: [PATCH 37/44] WIP: implement clustering --- src/system/cluster.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/system/cluster.rs b/src/system/cluster.rs index 85ce0c1..5aa1351 100644 --- a/src/system/cluster.rs +++ b/src/system/cluster.rs @@ -71,12 +71,7 @@ impl Cluster { } - pub fn init(system: &ActorSystem, cluster_config: &ClusterConfig) { - - let server_configs = Self::generate_net_config(&cluster_config.hosts, NetConnectionType::SERVER); - let client_configs = Self::generate_net_config(&cluster_config.members, NetConnectionType::CLIENT); - let client_configs = Self::resolve_dns(&client_configs); - + fn setup_actors(system: &ActorSystem, server_configs: Vec, client_configs: Vec) { let worker_factory = NetWorkerFactory::new(); let router_factory = ShardedRouterFactory::new(false, false); let router = system.builder().set_pool_name(NET_CLUSTER_POOL).spawn(NET_CLUSTER_LB, router_factory).unwrap(); @@ -107,4 +102,15 @@ impl Cluster { ) .unwrap(); } + + pub fn init(system: &ActorSystem, cluster_config: &ClusterConfig) { + + let server_configs = Self::generate_net_config(&cluster_config.hosts, NetConnectionType::SERVER); + let client_configs = Self::generate_net_config(&cluster_config.members, NetConnectionType::CLIENT); + let client_configs = Self::resolve_dns(&client_configs); + + Self::setup_actors(system, server_configs, client_configs); + + + } } \ No newline at end of file From 0a2f6ee8623e9349dae082b58717a49d17816345 Mon Sep 17 00:00:00 2001 From: Bobonium Date: Tue, 23 May 2023 19:23:27 +0200 Subject: [PATCH 38/44] provide client_configs to net_manager --- src/config/tyra_config.rs | 2 -- src/net/net_manager.rs | 36 +++++++++++++++++++++++------------- src/net/net_worker.rs | 1 + src/system/actor_system.rs | 6 +++--- src/system/cluster.rs | 13 ++++++++----- 5 files changed, 35 insertions(+), 23 deletions(-) diff --git a/src/config/tyra_config.rs b/src/config/tyra_config.rs index 7d2cf2e..36e25ec 100644 --- a/src/config/tyra_config.rs +++ b/src/config/tyra_config.rs @@ -7,8 +7,6 @@ use serde::{Deserialize, Serialize}; use crate::prelude::ClusterConfig; pub const DEFAULT_POOL: &str = "default"; -pub const NET_CLUSTER_POOL: &str = "cluster"; -pub const NET_CLUSTER_LB: &str = "cluster-router"; /// See [default.toml](https://github.com/sers-dev/tyra/blob/master/src/config/default.toml) for documentation of all configurations & their defaults #[derive(Debug, Serialize, Deserialize, Clone)] diff --git a/src/net/net_manager.rs b/src/net/net_manager.rs index 004c147..8f7585f 100644 --- a/src/net/net_manager.rs +++ b/src/net/net_manager.rs @@ -33,7 +33,8 @@ where on_stop_udp_timeout: Duration, router: ActorWrapper, workers: Vec>, - net_configs: Vec, + server_configs: Vec, + client_configs: Vec, is_stopping: Arc, is_stopped: Arc, } @@ -49,7 +50,8 @@ where R: Router + Handler + Handler + Handler + Handler + Handler, { pub fn new( - net_configs: Vec, + server_configs: Vec, + client_configs: Vec, graceful_shutdown_time_in_seconds: Duration, on_stop_udp_timeout: Duration, workers: Vec>, @@ -64,7 +66,8 @@ where on_stop_udp_timeout, router, workers, - net_configs, + server_configs, + client_configs, is_stopping, is_stopped, }; @@ -90,7 +93,7 @@ where self.is_stopping.store(true, Ordering::Relaxed); for _ in 0..iterations { - for net_config in &self.net_configs { + for net_config in &self.server_configs { let address = format!("{}:{}", net_config.host, net_config.port); match net_config.protocol { NetProtocol::TCP => { @@ -123,7 +126,7 @@ where } self.is_stopped.store(true, Ordering::Relaxed); - for net_config in &self.net_configs { + for net_config in &self.server_configs { let address = format!("{}:{}", net_config.host, net_config.port); match net_config.protocol { NetProtocol::TCP => { @@ -154,7 +157,8 @@ where + 'static, R: Router + Handler + Handler + Handler + Handler + Handler, { - net_configs: Vec, + server_configs: Vec, + client_configs: Vec, graceful_shutdown_time_in_seconds: Duration, on_stop_udp_timeout: Duration, workers: Vec>, @@ -173,14 +177,16 @@ where R: Router + Handler + Handler + Handler + Handler + Handler, { pub fn new( - net_configs: Vec, + server_configs: Vec, + client_configs: Vec, graceful_shutdown_time_in_seconds: Duration, on_stop_udp_timeout: Duration, workers: Vec>, router: ActorWrapper, ) -> Self { return Self { - net_configs, + server_configs, + client_configs, graceful_shutdown_time_in_seconds, on_stop_udp_timeout, workers, @@ -205,7 +211,8 @@ where ) -> Result, Box> { context.actor_ref.send(ActorInitMessage::new()).unwrap(); return Ok(NetManager::new( - self.net_configs.clone(), + self.server_configs.clone(), + self.client_configs.clone(), self.graceful_shutdown_time_in_seconds, self.on_stop_udp_timeout, self.workers.clone(), @@ -233,7 +240,8 @@ where let router = self.router.clone(); let is_stopping = self.is_stopping.clone(); let is_stopped = self.is_stopped.clone(); - let mut net_configs = self.net_configs.clone(); + let mut server_configs = self.server_configs.clone(); + let mut client_configs = self.client_configs.clone(); let mut last_udp_message_received = Instant::now(); let on_stop_udp_timeout = self.on_stop_udp_timeout.clone(); let context = context.clone(); @@ -250,8 +258,10 @@ where let mut poll = poll.unwrap(); let mut i = 0; - net_configs.sort_by_key(|c| c.protocol); - for net_config in &net_configs { + server_configs.sort_by_key(|c| c.protocol); + client_configs.sort_by_key(|c| c.protocol); + server_configs.append(&mut client_configs); + for net_config in &server_configs { if net_config.connection_type == NetConnectionType::CLIENT { println!("TEST: {:?}", net_config); continue; @@ -309,7 +319,7 @@ where } } let num_tcp_listeners = tcp_listeners.len(); - let num_total_listeners = net_configs.len(); + let num_total_listeners = server_configs.len(); let mut events = Events::with_capacity(1024); let mut streams = HashMap::new(); diff --git a/src/net/net_worker.rs b/src/net/net_worker.rs index 70b981e..aa39322 100644 --- a/src/net/net_worker.rs +++ b/src/net/net_worker.rs @@ -64,6 +64,7 @@ impl Handler for NetWorker { _context: &ActorContext, ) -> Result> { let stream = self.streams.get_mut(&msg.stream_id); + if stream.is_none() { // temporary implementation for our instant http response, later on we won't have to care here if the stream is active, we'll just forward the message debug!("Stream ID no longer exists, can't reply to request"); diff --git a/src/system/actor_system.rs b/src/system/actor_system.rs index 65ddca2..8ff614e 100644 --- a/src/system/actor_system.rs +++ b/src/system/actor_system.rs @@ -1,7 +1,7 @@ use crate::actor::actor_address::ActorAddress; use crate::actor::actor_builder::ActorBuilder; use crate::config::pool_config::ThreadPoolConfig; -use crate::config::tyra_config::{TyraConfig, DEFAULT_POOL, NET_CLUSTER_POOL, NET_CLUSTER_LB}; +use crate::config::tyra_config::{TyraConfig, DEFAULT_POOL}; use crate::message::serialized_message::SerializedMessage; use crate::prelude::{Actor, ActorError, Handler}; use crate::system::internal_actor_manager::InternalActorManager; @@ -13,7 +13,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use std::thread::sleep; use std::time::Duration; -use crate::system::cluster::Cluster; +use crate::system::cluster::{Cluster, CLUSTER_LB, CLUSTER_POOL}; /// Manages thread pools and actors #[derive(Clone)] @@ -61,7 +61,7 @@ impl ActorSystem { thread_pool_max_actors.insert(key.clone(), value.actor_limit); } - let net_worker_lb_address = ActorAddress::new(config.general.hostname.clone(), config.general.name.clone(), NET_CLUSTER_POOL, NET_CLUSTER_LB); + let net_worker_lb_address = ActorAddress::new(config.general.hostname.clone(), config.general.name.clone(), CLUSTER_POOL, CLUSTER_LB); let state = SystemState::new(wakeup_manager.clone(), Arc::new(thread_pool_max_actors), net_worker_lb_address, config.general.name.clone(), config.general.hostname.clone()); diff --git a/src/system/cluster.rs b/src/system/cluster.rs index 5aa1351..662d6c8 100644 --- a/src/system/cluster.rs +++ b/src/system/cluster.rs @@ -4,10 +4,12 @@ use regex::Regex; use trust_dns_resolver::config::{ResolverConfig, ResolverOpts}; use trust_dns_resolver::error::ResolveResult; use trust_dns_resolver::Resolver; -use crate::config::tyra_config::{NET_CLUSTER_LB, NET_CLUSTER_POOL}; use crate::prelude::{ActorSystem, ClusterConfig, NetConfig, NetConnectionType, NetManagerFactory, NetProtocol, NetWorkerFactory}; use crate::router::{AddActorMessage, ShardedRouterFactory}; +pub const CLUSTER_POOL: &str = "cluster"; +pub const CLUSTER_LB: &str = "cluster-router"; + pub struct Cluster { } @@ -74,14 +76,14 @@ impl Cluster { fn setup_actors(system: &ActorSystem, server_configs: Vec, client_configs: Vec) { let worker_factory = NetWorkerFactory::new(); let router_factory = ShardedRouterFactory::new(false, false); - let router = system.builder().set_pool_name(NET_CLUSTER_POOL).spawn(NET_CLUSTER_LB, router_factory).unwrap(); + let router = system.builder().set_pool_name(CLUSTER_POOL).spawn(CLUSTER_LB, router_factory).unwrap(); let worker_count = system - .get_available_actor_count_for_pool(NET_CLUSTER_POOL) + .get_available_actor_count_for_pool(CLUSTER_POOL) .unwrap() - 1; let workers = system .builder() - .set_pool_name(NET_CLUSTER_POOL) + .set_pool_name(CLUSTER_POOL) .spawn_multiple("cluster-worker", worker_factory.clone(), worker_count) .unwrap(); for worker in &workers { @@ -89,11 +91,12 @@ impl Cluster { } let _actor = system .builder() - .set_pool_name(NET_CLUSTER_POOL) + .set_pool_name(CLUSTER_POOL) .spawn( "cluster-manager", NetManagerFactory::new( server_configs, + client_configs, Duration::from_secs(10), Duration::from_secs(3), workers, From 9bf8e43ecaf2ae47c023c6f6ce09c58fcb1b1282 Mon Sep 17 00:00:00 2001 From: Bobonium Date: Mon, 29 May 2023 17:13:28 +0200 Subject: [PATCH 39/44] add simple_log dependency --- Cargo.toml | 3 ++- examples/net.rs | 3 +++ src/net/net_manager.rs | 10 +++++----- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 62a09f7..a546d3b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,4 +39,5 @@ regex = "1.8.1" trust-dns-resolver = "0.22.0" [dev-dependencies] -ntest = "0.9.0" \ No newline at end of file +ntest = "0.9.0" +simple_logger = "4.1.0" \ No newline at end of file diff --git a/examples/net.rs b/examples/net.rs index a68ffe5..d4805b7 100644 --- a/examples/net.rs +++ b/examples/net.rs @@ -1,6 +1,9 @@ +use simple_logger::SimpleLogger; use tyra::prelude::*; fn main() { + SimpleLogger::new().init().unwrap(); + // generate config let mut actor_config = TyraConfig::new().unwrap(); actor_config.cluster.enabled = true; diff --git a/src/net/net_manager.rs b/src/net/net_manager.rs index 8f7585f..b9e78af 100644 --- a/src/net/net_manager.rs +++ b/src/net/net_manager.rs @@ -262,13 +262,14 @@ where client_configs.sort_by_key(|c| c.protocol); server_configs.append(&mut client_configs); for net_config in &server_configs { + + let token = Token(i); + i += 1; + if net_config.connection_type == NetConnectionType::CLIENT { println!("TEST: {:?}", net_config); continue; } - let token = Token(i); - i += 1; - let address = format!("{}:{}", net_config.host, net_config.port) .parse() .unwrap(); @@ -330,7 +331,7 @@ where return; } - let res = poll.poll(&mut events, None); + let res = poll.poll(&mut events, Some(Duration::from_secs(10))); if res.is_err() { debug!("Can't poll Network Events"); continue; @@ -432,7 +433,6 @@ where return res; } Err(err) => { - warn!("Could not read from stream: {:?}", err); return String::from(""); } }) From 48c165a0482a0f6f6e7b664bc0bdbb6d23e3a275 Mon Sep 17 00:00:00 2001 From: Bobonium Date: Mon, 26 Jun 2023 17:37:08 +0200 Subject: [PATCH 40/44] wip --- src/net/net_manager.rs | 22 ++++++++++++++++++++-- src/net/net_worker.rs | 5 ++++- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/net/net_manager.rs b/src/net/net_manager.rs index b9e78af..0332eb8 100644 --- a/src/net/net_manager.rs +++ b/src/net/net_manager.rs @@ -10,7 +10,7 @@ use mio::net::{TcpListener, TcpStream, UdpSocket}; use mio::{Events, Interest, Poll, Token}; use std::collections::HashMap; use std::error::Error; -use std::io::{BufRead, BufReader}; +use std::io::{BufRead, BufReader, Write}; use std::marker::PhantomData; use std::net::Shutdown; use std::sync::atomic::{AtomicBool, Ordering}; @@ -267,7 +267,23 @@ where i += 1; if net_config.connection_type == NetConnectionType::CLIENT { - println!("TEST: {:?}", net_config); + let client = TcpStream::connect(format!("{}:{}", net_config.host, net_config.port).parse().unwrap()); + if client.is_err() { + warn!("Can't connect to client: {:?}", net_config); + continue; + } + let mut client = client.unwrap(); + let res = + poll.registry() + .register(&mut client, token, Interest::READABLE); + if res.is_err() { + error!("Can't register TCP Stream: {:?}", res.err()); + is_stopped.store(true, Ordering::Relaxed); + let _ = context.actor_ref.stop(); + return; + } + //tcp_listeners.insert(token, client); + println!("!!!!!!!"); continue; } let address = format!("{}:{}", net_config.host, net_config.port) @@ -348,6 +364,8 @@ where } let token = &event.token(); if token.0 < num_tcp_listeners { + println!("!!!!!!!"); + let listener = tcp_listeners.get(token); if listener.is_none() { warn!("Can't find TcpListener for {:?}", token); diff --git a/src/net/net_worker.rs b/src/net/net_worker.rs index aa39322..793b7c8 100644 --- a/src/net/net_worker.rs +++ b/src/net/net_worker.rs @@ -3,7 +3,7 @@ use crate::net::net_messages::{ }; use crate::prelude::{Actor, ActorContext, ActorFactory, ActorResult, Handler, SerializedMessage}; use io_arc::IoArc; -use log::{debug, warn}; +use log::{debug, trace, warn}; use mio::net::{TcpStream, UdpSocket}; use std::collections::HashMap; use std::error::Error; @@ -96,6 +96,7 @@ impl Handler for NetWorker { msg: AddTcpConnection, _context: &ActorContext, ) -> Result> { + trace!("Add TCP Connection: {:?}", msg.address); let key_already_exists = self.streams.remove(&msg.stream_id); if key_already_exists.is_some() { warn!("Stream ID already exists, dropping old one in favor of the new connection."); @@ -116,6 +117,8 @@ impl Handler for NetWorker { msg: RemoveTcpConnection, _context: &ActorContext, ) -> Result> { + trace!("Remove TCP Connection: {:?}", msg.stream_id); + let _ = self.streams.remove(&msg.stream_id); return Ok(ActorResult::Ok); } From 7ba3d8b36c149f2cd907a543134360c7157f5b44 Mon Sep 17 00:00:00 2001 From: Bobonium Date: Tue, 26 Mar 2024 23:42:34 +0100 Subject: [PATCH 41/44] upgrade dependencies; resolve warnings --- Cargo.toml | 32 ++++++++++++++++---------------- src/net/net_manager.rs | 4 ++-- src/system/actor_system.rs | 2 +- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a546d3b..6fcd791 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,26 +18,26 @@ name = "tyra" path = "src/lib.rs" [dependencies] -config = "0.13.3" +config = "0.14.0" hostname = "0.3.1" -num_cpus = "1.15.0" +num_cpus = "1.16.0" threadpool = "1.8.1" -crossbeam-channel = "0.5.7" -flume = "0.10.14" -dashmap = "5.4.0" -serde = { version = "1.0", features = ["derive"] } +crossbeam-channel = "0.5.12" +flume = "0.11.0" +dashmap = "5.5.3" +serde = { version = "1.0.197", features = ["derive"] } serde-encrypt = "0.7.0" -thiserror = "1.0" -log = "0.4" -ctrlc = { version = "3.2.5", features = ["termination"] } -mio = {version = "0.8.6", features = ["os-poll", "os-ext", "net"]} -quiche = "0.17.1" +thiserror = "1.0.58" +log = "0.4.21" +ctrlc = { version = "3.4.4", features = ["termination"] } +mio = {version = "0.8.11", features = ["os-poll", "os-ext", "net"]} +quiche = "0.20.1" io-arc = "1.0.0" -hashring = "0.3.0" +hashring = "0.3.3" bincode = "1.3.3" -regex = "1.8.1" -trust-dns-resolver = "0.22.0" +regex = "1.10.4" +trust-dns-resolver = "0.23.2" [dev-dependencies] -ntest = "0.9.0" -simple_logger = "4.1.0" \ No newline at end of file +ntest = "0.9.2" +simple_logger = "4.3.3" \ No newline at end of file diff --git a/src/net/net_manager.rs b/src/net/net_manager.rs index 0332eb8..e91ee18 100644 --- a/src/net/net_manager.rs +++ b/src/net/net_manager.rs @@ -10,7 +10,7 @@ use mio::net::{TcpListener, TcpStream, UdpSocket}; use mio::{Events, Interest, Poll, Token}; use std::collections::HashMap; use std::error::Error; -use std::io::{BufRead, BufReader, Write}; +use std::io::{BufRead, BufReader}; use std::marker::PhantomData; use std::net::Shutdown; use std::sync::atomic::{AtomicBool, Ordering}; @@ -450,7 +450,7 @@ where Ok(res) => { return res; } - Err(err) => { + Err(_err) => { return String::from(""); } }) diff --git a/src/system/actor_system.rs b/src/system/actor_system.rs index 8ff614e..8ede72e 100644 --- a/src/system/actor_system.rs +++ b/src/system/actor_system.rs @@ -167,7 +167,7 @@ impl ActorSystem { /// ``` pub fn add_pool_with_config(&self, name: &str, thread_pool_config: ThreadPoolConfig) { self.state - .add_pool_actor_limit(String::from(name.clone()), thread_pool_config.actor_limit); + .add_pool_actor_limit(String::from(name), thread_pool_config.actor_limit); self.thread_pool_manager .add_pool_with_config(name, thread_pool_config); } From 01176f0f95ac818e8f10782ead047a075f4f04ed Mon Sep 17 00:00:00 2001 From: Bobonium Date: Thu, 24 Oct 2024 17:07:51 +0200 Subject: [PATCH 42/44] upgrade dependencies --- Cargo.toml | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6fcd791..c51cc03 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,25 +19,25 @@ path = "src/lib.rs" [dependencies] config = "0.14.0" -hostname = "0.3.1" +hostname = "0.4.0" num_cpus = "1.16.0" threadpool = "1.8.1" -crossbeam-channel = "0.5.12" -flume = "0.11.0" -dashmap = "5.5.3" -serde = { version = "1.0.197", features = ["derive"] } +crossbeam-channel = "0.5.13" +flume = "0.11.1" +dashmap = "6.1.0" +serde = { version = "1.0.213", features = ["derive"] } serde-encrypt = "0.7.0" -thiserror = "1.0.58" -log = "0.4.21" -ctrlc = { version = "3.4.4", features = ["termination"] } -mio = {version = "0.8.11", features = ["os-poll", "os-ext", "net"]} -quiche = "0.20.1" +thiserror = "1.0.65" +log = "0.4.22" +ctrlc = { version = "3.4.5", features = ["termination"] } +mio = {version = "1.0.2", features = ["os-poll", "os-ext", "net"]} +quiche = "0.22.0" io-arc = "1.0.0" -hashring = "0.3.3" +hashring = "0.3.6" bincode = "1.3.3" -regex = "1.10.4" +regex = "1.11.0" trust-dns-resolver = "0.23.2" [dev-dependencies] -ntest = "0.9.2" -simple_logger = "4.3.3" \ No newline at end of file +ntest = "0.9.3" +simple_logger = "5.0.0" \ No newline at end of file From 6f5726cd0f7a987d187e3dbe4db694f8dbba66c3 Mon Sep 17 00:00:00 2001 From: Bobonium Date: Tue, 29 Oct 2024 22:58:47 +0100 Subject: [PATCH 43/44] rework graceful timeout to be a globally configured value used for stop(), network management and signal handling; also allow endless waiting through 0 --- CHANGELOG.md | 5 ++- README.md | 2 +- examples/benchmark_bulk_router.rs | 2 +- examples/benchmark_router_round_robin.rs | 4 +- examples/benchmark_single_actor.rs | 4 +- ...nchmark_single_actor_process_after_send.rs | 2 +- ..._actor_process_after_send_single_thread.rs | 2 +- .../benchmark_single_actor_single_thread.rs | 4 +- examples/quickstart.rs | 3 +- examples/serialize.rs | 2 +- src/actor/actor.rs | 14 +++---- src/actor/actor_builder.rs | 8 ++-- src/config/default.toml | 7 +++- src/config/global_config.rs | 3 +- src/lib.rs | 2 +- src/net/net_manager.rs | 26 +++++++++--- src/system/actor_system.rs | 40 ++++++++++++++----- src/system/cluster.rs | 8 ++-- src/system/system_state.rs | 2 +- 19 files changed, 91 insertions(+), 49 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a85af1d..fb6ecf6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,10 @@ - if a message is really intended to be sent it should obviously also implement `Deserialize` - Added `.is_mailbox_stopped()`, `is_stopped()` and `wait_for_stop()` to `ActorWrapper` - Added ability to re-initialize `ActorWrapper` after deserializing - - Added `general.signal_graceful_timeout_in_seconds` to config + - Added `general.graceful_timeout_in_seconds` to config + - reworked `system.stop()` to make use of configured value + - added `system.stop_override_graceful_termination_timeout(Duration)` to allow override of configured default + - value is also used for signal handling graceful timeout and timeout for network manager - Added `ActorBuilder.spawn_multiple()` - All routers now support a `SendToAllTargetsMessage` that will forward `M` to all active targets - Users can now use `ActorBuilder.get_existing(address: ActorAddress)` to get an `ActorWrapper` for an already existing `Actor` diff --git a/README.md b/README.md index 040bfbb..aeef77c 100644 --- a/README.md +++ b/README.md @@ -99,7 +99,7 @@ impl ActorFactory for TestActorFactory { impl Handler for TestActor { fn handle(&mut self, _msg: TestMessage, context: &ActorContext) -> Result> { println!("HELLO WORLD!"); - context.system.stop(Duration::from_millis(1000)); + context.system.stop(); Ok(ActorResult::Ok) } } diff --git a/examples/benchmark_bulk_router.rs b/examples/benchmark_bulk_router.rs index 7af2413..20c0b9a 100644 --- a/examples/benchmark_bulk_router.rs +++ b/examples/benchmark_bulk_router.rs @@ -143,7 +143,7 @@ impl Handler for Aggregator { "{} It took {:?} to finish {} actors", self.name, duration, self.total_actors ); - self.ctx.system.stop(Duration::from_secs(60)); + self.ctx.system.stop(); } Ok(ActorResult::Ok) } diff --git a/examples/benchmark_router_round_robin.rs b/examples/benchmark_router_round_robin.rs index 20f09ed..2efc787 100644 --- a/examples/benchmark_router_round_robin.rs +++ b/examples/benchmark_router_round_robin.rs @@ -1,7 +1,7 @@ use serde::Serialize; use std::error::Error; use std::process::exit; -use std::time::{Duration, Instant}; +use std::time::Instant; use tyra::prelude::*; use tyra::router::{AddActorMessage, RoundRobinRouterFactory}; @@ -138,7 +138,7 @@ impl Handler for Aggregator { "{} It took {:?} to finish {} actors", self.name, duration, self.total_actors ); - self.ctx.system.stop(Duration::from_secs(60)); + self.ctx.system.stop(); } Ok(ActorResult::Ok) } diff --git a/examples/benchmark_single_actor.rs b/examples/benchmark_single_actor.rs index 7244b37..5d37f9a 100644 --- a/examples/benchmark_single_actor.rs +++ b/examples/benchmark_single_actor.rs @@ -1,7 +1,7 @@ use serde::Serialize; use std::error::Error; use std::process::exit; -use std::time::{Duration, Instant}; +use std::time::Instant; use tyra::prelude::*; #[derive(Hash, Serialize)] @@ -63,7 +63,7 @@ impl Handler for Benchmark { ); } if self.count == self.total_msgs { - context.system.stop(Duration::from_secs(60)); + context.system.stop(); } Ok(ActorResult::Ok) } diff --git a/examples/benchmark_single_actor_process_after_send.rs b/examples/benchmark_single_actor_process_after_send.rs index daed727..5ef51d0 100644 --- a/examples/benchmark_single_actor_process_after_send.rs +++ b/examples/benchmark_single_actor_process_after_send.rs @@ -63,7 +63,7 @@ impl Handler for Benchmark { ); } if self.count == self.total_msgs { - context.system.stop(Duration::from_secs(60)); + context.system.stop(); } Ok(ActorResult::Ok) } diff --git a/examples/benchmark_single_actor_process_after_send_single_thread.rs b/examples/benchmark_single_actor_process_after_send_single_thread.rs index 406e6a2..9de72fd 100644 --- a/examples/benchmark_single_actor_process_after_send_single_thread.rs +++ b/examples/benchmark_single_actor_process_after_send_single_thread.rs @@ -63,7 +63,7 @@ impl Handler for Benchmark { ); } if self.count == self.total_msgs { - context.system.stop(Duration::from_secs(60)); + context.system.stop(); } Ok(ActorResult::Ok) } diff --git a/examples/benchmark_single_actor_single_thread.rs b/examples/benchmark_single_actor_single_thread.rs index 0ee64e9..6e8fd5e 100644 --- a/examples/benchmark_single_actor_single_thread.rs +++ b/examples/benchmark_single_actor_single_thread.rs @@ -1,7 +1,7 @@ use serde::Serialize; use std::error::Error; use std::process::exit; -use std::time::{Duration, Instant}; +use std::time::Instant; use tyra::prelude::*; #[derive(Hash, Serialize)] @@ -63,7 +63,7 @@ impl Handler for Benchmark { ); } if self.count == self.total_msgs { - context.system.stop(Duration::from_secs(60)); + context.system.stop(); } Ok(ActorResult::Ok) } diff --git a/examples/quickstart.rs b/examples/quickstart.rs index 5dd4dbc..e499f47 100644 --- a/examples/quickstart.rs +++ b/examples/quickstart.rs @@ -1,6 +1,5 @@ use serde::Serialize; use std::error::Error; -use std::time::Duration; use tyra::prelude::*; // define an `ActorMessage` that can be sent to `Actors` that implement the corresponding `Handler` @@ -47,7 +46,7 @@ impl Handler for TestActor { context: &ActorContext, ) -> Result> { println!("HELLO WORLD!"); - context.system.stop(Duration::from_millis(1000)); + context.system.stop(); Ok(ActorResult::Ok) } } diff --git a/examples/serialize.rs b/examples/serialize.rs index 9f39680..da8a1d3 100644 --- a/examples/serialize.rs +++ b/examples/serialize.rs @@ -38,7 +38,7 @@ impl Handler for RemoteActor { context: &ActorContext, ) -> Result> { println!("{}", msg.content); - context.system.stop(Duration::from_secs(10)); + context.system.stop(); Ok(ActorResult::Stop) } } diff --git a/src/actor/actor.rs b/src/actor/actor.rs index 2331ee4..ad55544 100644 --- a/src/actor/actor.rs +++ b/src/actor/actor.rs @@ -123,7 +123,7 @@ pub trait Actor: Send + Sync + UnwindSafe + Sized + 'static { /// } /// impl Actor for TestActor { /// fn on_panic(&mut self, context: &ActorContext, source: ActorPanicSource) -> Result> { - /// context.system.stop(Duration::from_millis(5000)); + /// context.system.stop(); /// return Ok(ActorResult::Kill); /// } /// } @@ -188,7 +188,7 @@ pub trait Actor: Send + Sync + UnwindSafe + Sized + 'static { /// } /// impl Actor for TestActor { /// fn on_error(&mut self, context: &ActorContext, err: Box) -> ActorResult { - /// context.system.stop(Duration::from_millis(5000)); + /// context.system.stop(); /// return ActorResult::Kill; /// } /// } @@ -246,7 +246,7 @@ pub trait Actor: Send + Sync + UnwindSafe + Sized + 'static { /// } /// impl Actor for TestActor { /// fn pre_start(&mut self, context: &ActorContext) -> Result> { - /// context.system.stop(Duration::from_millis(5000)); + /// context.system.stop(); /// return Ok(ActorResult::Kill); /// } /// } @@ -301,7 +301,7 @@ pub trait Actor: Send + Sync + UnwindSafe + Sized + 'static { /// } /// impl Actor for TestActor { /// fn pre_restart(&mut self, context: &ActorContext) { - /// context.system.stop(Duration::from_millis(5000)); + /// context.system.stop(); /// } /// } /// @@ -351,7 +351,7 @@ pub trait Actor: Send + Sync + UnwindSafe + Sized + 'static { /// } /// impl Actor for TestActor { /// fn pre_stop(&mut self, context: &ActorContext) { - /// context.system.stop(Duration::from_millis(5000)); + /// context.system.stop(); /// } /// } /// @@ -401,7 +401,7 @@ pub trait Actor: Send + Sync + UnwindSafe + Sized + 'static { /// } /// impl Actor for TestActor { /// fn post_stop(&mut self, context: &ActorContext) { - /// context.system.stop(Duration::from_millis(5000)); + /// context.system.stop(); /// } /// } /// @@ -473,7 +473,7 @@ pub trait Actor: Send + Sync + UnwindSafe + Sized + 'static { /// /// impl Handler for TestActor { /// fn handle(&mut self, _msg: ActorInitMessage, context: &ActorContext) -> Result> { - /// context.system.stop(Duration::from_millis(5000)); + /// context.system.stop(); /// return Ok(ActorResult::Ok); /// } /// } diff --git a/src/actor/actor_builder.rs b/src/actor/actor_builder.rs index 7686293..0efd8df 100644 --- a/src/actor/actor_builder.rs +++ b/src/actor/actor_builder.rs @@ -177,7 +177,7 @@ where /// let err = this_is_not_working_either.err().unwrap(); /// assert_eq!(err, ActorError::InvalidActorTypeError, "Error is not correct"); /// - /// actor_system.stop(Duration::from_millis(3000)); + /// actor_system.stop(); /// std::process::exit(actor_system.await_shutdown()); /// } /// ``` @@ -334,7 +334,7 @@ where /// let this_works :Result, ActorError> = actor_system.builder().get_existing(&address); /// assert!(this_works.is_ok(), "The TestActor did not exist"); /// - /// actor_system.stop(Duration::from_millis(3000)); + /// actor_system.stop(); /// std::process::exit(actor_system.await_shutdown()); /// } /// ``` @@ -390,7 +390,7 @@ where /// let address = ActorAddress::new("remote", "system", "pool", "actor"); /// let actor_wrapper :ActorWrapper = actor_system.builder().init_after_deserialize(&address); /// - /// actor_system.stop(Duration::from_millis(3000)); + /// actor_system.stop(); /// std::process::exit(actor_system.await_shutdown()); /// } /// ``` @@ -465,7 +465,7 @@ where /// let err = pool_full.err().unwrap(); /// assert_eq!(err, ActorError::ThreadPoolHasTooManyActorsError, "Error is not correct"); /// - /// actor_system.stop(Duration::from_millis(3000)); + /// actor_system.stop(); /// std::process::exit(actor_system.await_shutdown()); /// } /// ``` diff --git a/src/config/default.toml b/src/config/default.toml index 7db96da..dc09940 100644 --- a/src/config/default.toml +++ b/src/config/default.toml @@ -14,8 +14,11 @@ default_mailbox_size = 0 default_message_throughput = 15 # defines if the rust panic hook should be overwritten by the actor system on startup override_panic_hook = true -# will start a graceful shutdown on first SIGINT, SIGTERM or SIGHUP, or force a shutdown if multiple signals are received. Can be disabled by setting to 0 -signal_graceful_timeout_in_seconds = 300 +# will start a graceful shutdown on first SIGINT, SIGTERM or SIGHUP, or force a shutdown if multiple signals are received. +enable_signal_handling = true +# maximum graceful shutdown time before system is hard killed 0 disables graceful shutdown +# waning: setting to 0 can result in endlessly running applications +graceful_timeout_in_seconds = 300 # default pool settings [thread_pool.config.default] diff --git a/src/config/global_config.rs b/src/config/global_config.rs index 51f7f38..f3eacad 100644 --- a/src/config/global_config.rs +++ b/src/config/global_config.rs @@ -7,5 +7,6 @@ pub struct GeneralConfig { pub default_mailbox_size: usize, pub default_message_throughput: usize, pub override_panic_hook: bool, - pub signal_graceful_timeout_in_seconds: usize, + pub enable_signal_handling: bool, + pub graceful_timeout_in_seconds: u64, } diff --git a/src/lib.rs b/src/lib.rs index a778961..85a8f61 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -59,7 +59,7 @@ //! //! // cleanup //! actor.stop().unwrap(); -//! actor_system.stop(Duration::from_millis(5000)); +//! actor_system.stop(); //! exit(actor_system.await_shutdown()); //! } //! ``` diff --git a/src/net/net_manager.rs b/src/net/net_manager.rs index e91ee18..2f607e2 100644 --- a/src/net/net_manager.rs +++ b/src/net/net_manager.rs @@ -85,14 +85,28 @@ where { fn pre_stop(&mut self, _context: &ActorContext) { - let iterations = 10; - let iterate_graceful_stop = self.graceful_shutdown_time_in_seconds / iterations; + let iterate_graceful_stop_sleep_duration_in_seconds = 1; + let sleep_duration = Duration::from_secs(iterate_graceful_stop_sleep_duration_in_seconds); - sleep(iterate_graceful_stop); + let iterations = if self.graceful_shutdown_time_in_seconds.as_secs() > 0 { + self.graceful_shutdown_time_in_seconds.as_secs() / iterate_graceful_stop_sleep_duration_in_seconds + } else { + 0 + }; + + sleep(sleep_duration); self.is_stopping.store(true, Ordering::Relaxed); - for _ in 0..iterations { + let mut i = 0; + loop { + if self.graceful_shutdown_time_in_seconds.as_secs() > 0 { + if i > iterations { + return; + } + i += 1; + } + for net_config in &self.server_configs { let address = format!("{}:{}", net_config.host, net_config.port); match net_config.protocol { @@ -113,7 +127,7 @@ where if self.is_stopped.load(Ordering::Relaxed) { return; } - sleep(iterate_graceful_stop); + sleep(sleep_duration); } } fn post_stop(&mut self, _context: &ActorContext) { @@ -283,7 +297,7 @@ where return; } //tcp_listeners.insert(token, client); - println!("!!!!!!!"); + println!("!!!!!!?"); continue; } let address = format!("{}:{}", net_config.host, net_config.port) diff --git a/src/system/actor_system.rs b/src/system/actor_system.rs index 8ede72e..bd31bba 100644 --- a/src/system/actor_system.rs +++ b/src/system/actor_system.rs @@ -88,17 +88,18 @@ impl ActorSystem { sigint_received: Arc::new(AtomicBool::new(false)), }; - if config.general.signal_graceful_timeout_in_seconds > 0 { + if config.general.enable_signal_handling { let sys = system.clone(); + let graceful_timeout = config.general.graceful_timeout_in_seconds.clone(); ctrlc::set_handler(move || { - sys.sigint_handler(Duration::from_secs(300)); + sys.sigint_handler(Duration::from_secs(graceful_timeout)); }) .unwrap(); } system.internal_actor_manager.init(system.clone()); if config.cluster.enabled { - Cluster::init(&system, &config.cluster); + Cluster::init(&system, &config.cluster, Duration::from_secs(config.general.graceful_timeout_in_seconds)); } system @@ -116,7 +117,7 @@ impl ActorSystem { /// /// let mut actor_config = TyraConfig::new().unwrap(); /// //disable automatic setup of sigint handling, so that we can set it manually - /// actor_config.general.signal_graceful_timeout_in_seconds = 0; + /// actor_config.general.enable_signal_handling = false; /// let actor_system = ActorSystem::new(actor_config); /// ctrlc::set_handler(move || {actor_system.sigint_handler(Duration::from_secs(60));}).unwrap(); /// ``` @@ -125,7 +126,7 @@ impl ActorSystem { self.force_stop(); } self.sigint_received.store(true, Ordering::Relaxed); - self.stop(graceful_termination_timeout); + self.stop_override_graceful_termination_timeout(graceful_termination_timeout); } /// Adds a new named pool using the [default pool configuration](https://github.com/sers-dev/tyra/blob/master/src/config/default.toml) /// @@ -289,12 +290,33 @@ impl ActorSystem { /// /// let actor_config = TyraConfig::new().unwrap(); /// let actor_system = ActorSystem::new(actor_config); - /// actor_system.stop(Duration::from_secs(1)); + /// actor_system.stop_override_graceful_termination_timeout(Duration::from_secs(1)); /// ``` - pub fn stop(&self, graceful_termination_timeout: Duration) { + pub fn stop_override_graceful_termination_timeout(&self, graceful_termination_timeout: Duration) { self.state.stop(graceful_termination_timeout); } + /// Sends a SystemStopMessage to all running Actors, and wakes them up if necessary. + /// Users can implement their own clean system stop behavior, by implementing [Actor.on_system_stop](../prelude/trait.Actor.html#method.on_system_stop) and [Actor.on_actor_stop](../prelude/trait.Actor.html#method.on_actor_stop) + /// + /// System will stop after all actors have been stopped or after `general.graceful_timeout_in_seconds` + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rust + /// use tyra::prelude::{TyraConfig, ActorSystem, ThreadPoolConfig}; + /// use std::time::Duration; + /// + /// let actor_config = TyraConfig::new().unwrap(); + /// let actor_system = ActorSystem::new(actor_config); + /// actor_system.stop(); + /// ``` + pub fn stop(&self) { + self.stop_override_graceful_termination_timeout(Duration::from_secs(self.config.general.graceful_timeout_in_seconds)); + } + pub fn force_stop(&self) { self.state.force_stop(); } @@ -315,7 +337,7 @@ impl ActorSystem { /// ``` pub fn stop_with_code(&self, graceful_termination_timeout: Duration, code: i32) { self.state.use_forced_exit_code(code); - self.stop(graceful_termination_timeout); + self.stop_override_graceful_termination_timeout(graceful_termination_timeout); } /// Waits for the system to stop @@ -339,7 +361,7 @@ impl ActorSystem { /// /// let actor_config = TyraConfig::new().unwrap(); /// let actor_system = ActorSystem::new(actor_config); - /// actor_system.stop(Duration::from_secs(3)); + /// actor_system.stop(); /// exit(actor_system.await_shutdown()); /// ``` pub fn await_shutdown(&self) -> i32 { diff --git a/src/system/cluster.rs b/src/system/cluster.rs index 662d6c8..2386b68 100644 --- a/src/system/cluster.rs +++ b/src/system/cluster.rs @@ -73,7 +73,7 @@ impl Cluster { } - fn setup_actors(system: &ActorSystem, server_configs: Vec, client_configs: Vec) { + fn setup_actors(system: &ActorSystem, server_configs: Vec, client_configs: Vec, graceful_timeout_in_seconds: Duration) { let worker_factory = NetWorkerFactory::new(); let router_factory = ShardedRouterFactory::new(false, false); let router = system.builder().set_pool_name(CLUSTER_POOL).spawn(CLUSTER_LB, router_factory).unwrap(); @@ -97,7 +97,7 @@ impl Cluster { NetManagerFactory::new( server_configs, client_configs, - Duration::from_secs(10), + graceful_timeout_in_seconds, Duration::from_secs(3), workers, router, @@ -106,13 +106,13 @@ impl Cluster { .unwrap(); } - pub fn init(system: &ActorSystem, cluster_config: &ClusterConfig) { + pub fn init(system: &ActorSystem, cluster_config: &ClusterConfig, graceful_timeout_in_seconds: Duration) { let server_configs = Self::generate_net_config(&cluster_config.hosts, NetConnectionType::SERVER); let client_configs = Self::generate_net_config(&cluster_config.members, NetConnectionType::CLIENT); let client_configs = Self::resolve_dns(&client_configs); - Self::setup_actors(system, server_configs, client_configs); + Self::setup_actors(system, server_configs, client_configs, graceful_timeout_in_seconds); } diff --git a/src/system/system_state.rs b/src/system/system_state.rs index 2407e4f..a6ef81c 100644 --- a/src/system/system_state.rs +++ b/src/system/system_state.rs @@ -71,7 +71,7 @@ impl SystemState { let now = Instant::now(); while self.get_actor_count() != 0 { - if now.elapsed() >= timeout || self.is_force_stopped.load(Ordering::Relaxed) { + if (timeout.as_secs() > 0 && now.elapsed() >= timeout) || self.is_force_stopped.load(Ordering::Relaxed) { self.is_force_stopped.store(true, Ordering::Relaxed); self.mailboxes.clear(); break; From a0364a221e267f626b6f1fb6538e4b7194635daa Mon Sep 17 00:00:00 2001 From: Bobonium Date: Fri, 10 Oct 2025 00:19:36 +0200 Subject: [PATCH 44/44] upgrade dependencies --- Cargo.toml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index caddabc..7796878 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,17 +25,17 @@ threadpool = "1.8.1" crossbeam-channel = "0.5.15" flume = "0.11.1" dashmap = "6.1.0" -serde = { version = "1.0.213", features = ["derive"] } +serde = { version = "1.0.228", features = ["derive"] } serde-encrypt = "0.7.0" -thiserror = "1.0.65" -log = "0.4.22" -ctrlc = { version = "3.4.5", features = ["termination"] } -mio = {version = "1.0.2", features = ["os-poll", "os-ext", "net"]} -quiche = "0.22.0" +thiserror = "2.0.17" +log = "0.4.28" +ctrlc = { version = "3.5.0", features = ["termination"] } +mio = {version = "1.0.4", features = ["os-poll", "os-ext", "net"]} +quiche = "0.24.6" io-arc = "1.0.0" hashring = "0.3.6" bincode = "1.3.3" -regex = "1.11.0" +regex = "1.11.3" trust-dns-resolver = "0.23.2"