swactor/src/actor.rs

177 lines
5.3 KiB
Rust
Raw Normal View History

use std::any::Any;
use std::sync::Arc;
use crate::Error;
/// 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;
fn handle(&mut self, ctx: &Ctx, msg: Self::Incoming);
}
/// 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)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ActorAddress(pub [u8; 32]);
impl std::fmt::Display for ActorAddress {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for b in &self.0[..8] {
write!(f, "{:02x}", b)?;
}
write!(f, "\u{2026}")
}
}
impl ActorAddress {
pub fn new_random() -> Self {
let mut bytes = [0u8; 32];
crate::get_random(&mut bytes);
Self(bytes)
}
}
/// The actor process as represented in the Runtime — thin wrapper around user state.
pub struct Actor<A: ActorInterface> {
inner: A,
/// Factory for creating fresh instances on restart. None = not restartable.
restart_factory: Option<Arc<dyn Fn() -> A + Send + Sync>>,
max_restarts: u32,
restart_count: u32,
}
impl<A: ActorInterface> Actor<A> {
pub fn new(inner: A) -> Self {
Self {
inner,
restart_factory: None,
max_restarts: 0,
restart_count: 0,
}
}
pub fn new_restartable(
inner: A,
factory: Arc<dyn Fn() -> A + Send + Sync>,
max_restarts: u32,
) -> Self {
Self {
inner,
restart_factory: Some(factory),
max_restarts,
restart_count: 0,
}
}
}
/// Trait for type-erased actors — single-message handler.
///
/// Returns `Some(type_name)` if handled, `None` on type mismatch.
pub trait AnyActor: Send {
fn handle_any(&mut self, ctx: &Ctx, msg: Box<dyn Any + Send>) -> Option<&'static str>;
/// Attempt to create a fresh instance for restart after panic.
/// Returns `None` if restart is not supported or restart limit exceeded.
fn try_restart(&self) -> Option<Box<dyn AnyActor>> {
None
}
}
impl<A> AnyActor for Actor<A>
where
A: ActorInterface,
{
fn handle_any(&mut self, ctx: &Ctx, msg: Box<dyn Any + Send>) -> Option<&'static str> {
if let Ok(typed) = msg.downcast::<A::Incoming>() {
self.inner.handle(ctx, *typed);
Some(std::any::type_name::<A::Incoming>())
} else {
None
}
}
fn try_restart(&self) -> Option<Box<dyn AnyActor>> {
let factory = self.restart_factory.as_ref()?;
if self.restart_count >= self.max_restarts {
return None;
}
let fresh = factory();
Some(Box::new(Actor {
inner: fresh,
restart_factory: Some(factory.clone()),
max_restarts: self.max_restarts,
restart_count: self.restart_count + 1,
}))
}
}
/// Object-safe inner trait for sending type-erased messages.
pub trait ContextInner {
fn send_any(&self, addr: ActorAddress, msg: Box<dyn Any + Send>) -> Result<(), Error>;
fn spawn_any(&self, addr: ActorAddress, actor: Box<dyn AnyActor>);
}
/// Actor syscall interface — passed to `ActorInterface::handle()`.
///
/// Wraps a `&dyn ContextInner` to solve the object-safety problem while
/// providing a typed public API.
pub struct Ctx<'a> {
inner: &'a dyn ContextInner,
self_addr: ActorAddress,
}
impl<'a> Ctx<'a> {
pub(crate) fn new(inner: &'a dyn ContextInner, self_addr: ActorAddress) -> Self {
Self { inner, self_addr }
}
pub fn raw_inner(&self) -> &dyn ContextInner {
self.inner
}
/// Returns the address of the actor currently being ticked.
pub fn self_addr(&self) -> ActorAddress {
self.self_addr
}
/// Send a typed message to an actor address.
pub fn send<M: Message>(&self, addr: ActorAddress, msg: M) -> Result<(), Error> {
self.inner.send_any(addr, Box::new(msg))
}
/// Spawn a new actor, returning its address.
pub fn spawn<A: ActorInterface>(&self, actor: A) -> Result<ActorAddress, Error> {
let addr = ActorAddress::new_random();
let boxed: Box<dyn AnyActor> = Box::new(Actor::new(actor));
self.inner.spawn_any(addr, boxed);
Ok(addr)
}
/// Spawn a restartable actor. On panic, recreated via `factory` up to
/// `max_restarts` times before permanent poisoning.
pub fn spawn_restartable<A, F>(
&self,
actor: A,
factory: F,
max_restarts: u32,
) -> Result<ActorAddress, Error>
where
A: ActorInterface,
F: Fn() -> A + Send + Sync + 'static,
{
let addr = ActorAddress::new_random();
let boxed: Box<dyn AnyActor> = Box::new(Actor::new_restartable(
actor,
Arc::new(factory),
max_restarts,
));
self.inner.spawn_any(addr, boxed);
Ok(addr)
}
}