feat: actor refactoring default peer
This commit is contained in:
@@ -0,0 +1,211 @@
|
||||
// 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::utils::thread_pool::ThreadPool;
|
||||
use crate::trace;
|
||||
|
||||
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<M: Send> {
|
||||
pub(crate) sender: Sender<ActorMessage<M>>,
|
||||
}
|
||||
|
||||
impl<M: Send> ActorHandle<M> {
|
||||
/// 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<M> {
|
||||
UserMessage(M),
|
||||
Stop,
|
||||
Tick,
|
||||
}
|
||||
|
||||
/// Actor system that manages actor lifecycle
|
||||
pub struct ActorSystem {
|
||||
thread_pool: Arc<ThreadPool>,
|
||||
}
|
||||
|
||||
impl ActorSystem {
|
||||
pub fn new(thread_pool: Arc<ThreadPool>) -> Self {
|
||||
ActorSystem { thread_pool }
|
||||
}
|
||||
|
||||
/// Spawn a new actor and return its handle
|
||||
pub fn spawn<A: Actor>(&self, mut actor: A) -> ActorHandle<A::Message> {
|
||||
let (sender, receiver) = channel::<ActorMessage<A::Message>>();
|
||||
let handle = ActorHandle { sender: sender.clone() };
|
||||
|
||||
let thread_pool = self.thread_pool.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<A: Actor, F>(&self, mut actor: A, init: F) -> ActorHandle<A::Message>
|
||||
where
|
||||
F: FnOnce(&mut A, ActorHandle<A::Message>) + Send + 'static,
|
||||
{
|
||||
let (sender, receiver) = channel::<ActorMessage<A::Message>>();
|
||||
let handle = ActorHandle { sender: sender.clone() };
|
||||
let handle_for_init = handle.clone();
|
||||
|
||||
let thread_pool = self.thread_pool.clone();
|
||||
|
||||
// Start the actor event loop on the thread pool
|
||||
self.thread_pool.execute(move || {
|
||||
trace!("[actor_system] Actor task starting on thread pool");
|
||||
init(&mut actor, handle_for_init);
|
||||
trace!("[actor_system] Actor init callback completed");
|
||||
actor.on_start();
|
||||
trace!("[actor_system] Actor on_start completed, starting event loop");
|
||||
Self::run_actor_loop(&mut actor, receiver);
|
||||
trace!("[actor_system] Actor event loop ended");
|
||||
actor.on_stop();
|
||||
trace!("[actor_system] Actor on_stop completed");
|
||||
});
|
||||
|
||||
handle
|
||||
}
|
||||
|
||||
/// Run the actor's message processing loop
|
||||
fn run_actor_loop<A: Actor>(actor: &mut A, receiver: Receiver<ActorMessage<A::Message>>) {
|
||||
trace!("[actor_system] run_actor_loop STARTED");
|
||||
let tick_interval = Duration::from_millis(100);
|
||||
let mut last_tick = Instant::now();
|
||||
let mut message_count = 0;
|
||||
let mut tick_count = 0;
|
||||
|
||||
loop {
|
||||
// Try to receive messages with timeout for periodic ticks
|
||||
match receiver.recv_timeout(tick_interval) {
|
||||
Ok(ActorMessage::UserMessage(msg)) => {
|
||||
message_count += 1;
|
||||
trace!("[actor_system] Processing message #{}", message_count);
|
||||
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) => {
|
||||
// Periodic tick
|
||||
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");
|
||||
// Channel closed, actor should stop
|
||||
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<AtomicUsize>,
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user