2026-02-06 11:25:37 +00:00
|
|
|
use std::any::Any;
|
|
|
|
|
|
|
|
|
|
use crate::{get_random, runtime::{ContextInner, Ctx}, worker::Mailbox};
|
2026-01-25 13:38:34 +00:00
|
|
|
|
|
|
|
|
/// The primary trait defining data that can be passed to and from actor processes
|
|
|
|
|
pub trait Message: 'static + Sized + Clone + Send + Sync {}
|
|
|
|
|
impl<T: 'static + Sized + Clone + Send + Sync> Message for T {}
|
|
|
|
|
|
|
|
|
|
pub trait ActorInterface: 'static + Send {
|
|
|
|
|
type Incoming: Message;
|
|
|
|
|
type Response: Message;
|
2026-02-06 11:25:37 +00:00
|
|
|
fn handle(&mut self, ctx: &Ctx, msg: Self::Incoming);
|
2026-01-25 13:38:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// A unique address for this actor. 32 bytes is overkill for a small application,
|
|
|
|
|
/// but most systems are powerful, and this allows us to create a global map of
|
|
|
|
|
/// actor processes in the future, without worrying about collision.
|
|
|
|
|
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
|
|
|
|
|
pub struct ActorAddress(pub [u8; 32]);
|
|
|
|
|
impl ActorAddress {
|
|
|
|
|
pub fn new_random() -> Self {
|
|
|
|
|
let mut bytes = [0u8; 32];
|
|
|
|
|
get_random(&mut bytes);
|
|
|
|
|
Self(bytes)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-06 11:25:37 +00:00
|
|
|
/// The actor process as represented in the Runtime, with the actor state stored with its mailbox.
|
2026-01-25 13:38:34 +00:00
|
|
|
pub(crate) struct Actor<A>
|
|
|
|
|
where
|
|
|
|
|
A: ActorInterface,
|
|
|
|
|
{
|
2026-02-06 11:25:37 +00:00
|
|
|
addr: ActorAddress,
|
|
|
|
|
mailbox: Mailbox<A::Incoming>,
|
2026-01-25 13:38:34 +00:00
|
|
|
inner: A,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<A: ActorInterface> Actor<A> {
|
2026-02-06 11:25:37 +00:00
|
|
|
pub(crate) fn new(addr: ActorAddress, mailbox: Mailbox<A::Incoming>, inner: A) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
addr,
|
|
|
|
|
mailbox,
|
|
|
|
|
inner,
|
|
|
|
|
}
|
2026-01-25 13:38:34 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Trait for type-erased actors
|
|
|
|
|
pub(crate) trait AnyActor: Send {
|
2026-02-06 11:25:37 +00:00
|
|
|
/// Tick the actor, processing pending messages. Returns `true` if any work was done.
|
|
|
|
|
fn tick(&mut self, inner: &dyn ContextInner) -> bool;
|
|
|
|
|
/// Deliver a type-erased message into this actor's mailbox.
|
|
|
|
|
/// Returns `true` if the downcast succeeded.
|
|
|
|
|
fn deliver(&mut self, msg: Box<dyn Any + Send>) -> bool;
|
2026-01-25 13:38:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<A> AnyActor for Actor<A>
|
|
|
|
|
where
|
|
|
|
|
A: ActorInterface,
|
|
|
|
|
{
|
2026-02-06 11:25:37 +00:00
|
|
|
fn tick(&mut self, inner: &dyn ContextInner) -> bool {
|
|
|
|
|
let n = self.mailbox.drain_count();
|
|
|
|
|
if n > 0 {
|
|
|
|
|
let ctx = Ctx::new(inner, self.addr);
|
|
|
|
|
for _ in 0..n {
|
|
|
|
|
if let Some(msg) = self.mailbox.pop() {
|
|
|
|
|
self.inner.handle(&ctx, msg);
|
|
|
|
|
}
|
2026-01-25 13:38:34 +00:00
|
|
|
}
|
|
|
|
|
}
|
2026-02-06 11:25:37 +00:00
|
|
|
n > 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn deliver(&mut self, msg: Box<dyn Any + Send>) -> bool {
|
|
|
|
|
if let Ok(typed) = msg.downcast::<A::Incoming>() {
|
|
|
|
|
self.mailbox.push(*typed);
|
|
|
|
|
true
|
|
|
|
|
} else {
|
|
|
|
|
false
|
|
|
|
|
}
|
2026-01-25 13:38:34 +00:00
|
|
|
}
|
|
|
|
|
}
|