// src/actor/mod.rs // Lightweight actor system using the existing thread pool use std::sync::mpsc::{channel, Receiver, Sender}; use std::sync::Arc; use std::time::{Duration, Instant}; use crate::trace; use crate::utils::thread_pool::ThreadPool; pub mod peer_actor; pub mod peer_registry; /// Core actor trait - each actor processes messages pub trait Actor: Send + 'static { type Message: Send + Clone + 'static; /// Handle a single message fn handle(&mut self, msg: Self::Message); /// Called when actor starts (optional hook) fn on_start(&mut self) {} /// Called when actor stops (optional hook) fn on_stop(&mut self) {} /// Optional periodic tick for background work fn tick(&mut self) {} } /// Handle to send messages to an actor #[derive(Clone)] pub struct ActorHandle { pub(crate) sender: Sender>, } impl ActorHandle { /// Send a message to the actor (non-blocking) pub fn send(&self, msg: M) -> Result<(), String> { self.sender .send(ActorMessage::UserMessage(msg)) .map_err(|e| format!("Failed to send message: {}", e)) } /// Request actor to stop gracefully pub fn stop(&self) -> Result<(), String> { self.sender .send(ActorMessage::Stop) .map_err(|e| format!("Failed to send stop signal: {}", e)) } } /// Internal actor message wrapper pub(crate) enum ActorMessage { UserMessage(M), Stop, #[allow(dead_code)] Tick, } /// Actor system that manages actor lifecycle pub struct ActorSystem { thread_pool: Arc, } impl ActorSystem { pub fn new(thread_pool: Arc) -> Self { ActorSystem { thread_pool } } /// Spawn a new actor and return its handle pub fn spawn(&self, mut actor: A) -> ActorHandle { let (sender, receiver) = channel::>(); let handle = ActorHandle { sender: sender.clone(), }; // Start the actor event loop on the thread pool self.thread_pool.execute(move || { actor.on_start(); Self::run_actor_loop(&mut actor, receiver); actor.on_stop(); }); handle } /// Spawn a new actor with initialization callback and return its handle /// The callback receives the actor handle before on_start is called pub fn spawn_with_handle( &self, mut actor: A, init: F, ) -> ActorHandle where F: FnOnce(&mut A, ActorHandle) + Send + 'static, { let (sender, receiver) = channel::>(); let handle = ActorHandle { sender: sender.clone(), }; let handle_for_init = handle.clone(); self.thread_pool.execute(move || { init(&mut actor, handle_for_init); actor.on_start(); Self::run_actor_loop(&mut actor, receiver); actor.on_stop(); }); handle } fn run_actor_loop( actor: &mut A, receiver: Receiver>, ) { let tick_interval = Duration::from_millis(100); let mut last_tick = Instant::now(); let mut message_count = 0; let mut tick_count = 0; loop { match receiver.recv_timeout(tick_interval) { Ok(ActorMessage::UserMessage(msg)) => { message_count += 1; actor.handle(msg); } Ok(ActorMessage::Stop) => { trace!( "[actor_system] Received Stop message, breaking loop" ); break; } Ok(ActorMessage::Tick) => { tick_count += 1; trace!( "[actor_system] Received explicit Tick message #{}", tick_count ); actor.tick(); } Err(std::sync::mpsc::RecvTimeoutError::Timeout) => { if last_tick.elapsed() >= tick_interval { tick_count += 1; // if tick_count % 10 == 0 { // trace!("[actor_system] Periodic tick #{} (every 1s log)", tick_count); // } actor.tick(); last_tick = Instant::now(); } } Err(std::sync::mpsc::RecvTimeoutError::Disconnected) => { trace!( "[actor_system] Channel disconnected, breaking loop" ); break; } } } trace!("[actor_system] run_actor_loop ENDED - processed {} messages, {} ticks", message_count, tick_count); } } #[cfg(test)] mod tests { use super::*; use std::sync::atomic::{AtomicUsize, Ordering}; struct CounterActor { count: Arc, } impl Actor for CounterActor { type Message = usize; fn handle(&mut self, msg: Self::Message) { self.count.fetch_add(msg, Ordering::SeqCst); } fn on_start(&mut self) { println!("Counter actor started"); } fn on_stop(&mut self) { println!("Counter actor stopped"); } } #[test] fn test_actor_system() { let thread_pool = Arc::new(ThreadPool::new(4)); let system = ActorSystem::new(thread_pool); let count = Arc::new(AtomicUsize::new(0)); let actor = CounterActor { count: count.clone(), }; let handle = system.spawn(actor); // Send some messages handle.send(1).unwrap(); handle.send(2).unwrap(); handle.send(3).unwrap(); // Give actor time to process std::thread::sleep(Duration::from_millis(100)); assert_eq!(count.load(Ordering::SeqCst), 6); handle.stop().unwrap(); } }