Expand description
Minimal, self-contained actor runtime used in this crate.
This module provides a tiny, opinionated actor pattern built on top of
tokio, with:
- A per-actor mailbox (
mpsc::Receiver<Action<A>>) of boxed async actions. - A
Handle<A>you can clone and send across threads to schedule work on the actor’s single-threaded mutable state. - Ergonomic macros (
act!,act_ok!) to write actor actions inline. - Panic capturing and error propagation to callers via
anyhow::Result.
The pattern encourages a public API type (“Object”) that holds a
Handle<ObjectActor> and a private type (“ObjectActor”) that owns the
mutable state and the mailbox Receiver. The actor implements Actor for a
simple run loop and is spawned with tokio::spawn.
Example: a simple counter
ⓘ
use anyhow::{anyhow, Result};
use iroh_lan::actor::{Actor, Handle, Action};
use tokio::sync::mpsc;
// Public API type (exposed from your module)
pub struct Counter {
api: Handle<CounterActor>,
}
impl Counter {
pub fn new() -> Self {
// 1) Create channel and api handle
let (api, rx) = Handle::channel(128);
// 2) Create the actor with private state and the mailbox
let actor = CounterActor { value: 0, rx };
// 3) Spawn the run loop
tokio::spawn(async move {
let mut actor = actor;
let _ = actor.run().await;
});
Self { api }
}
// Mutating API method
pub async fn inc(&self, by: i32) -> Result<()> {
self.api
// act_ok! wraps the returned value into Ok(..)
.call(act_ok!(actor => async move {
actor.value += by;
}))
.await
}
// Query method returning a value
pub async fn get(&self) -> Result<i32> {
self.api
.call(act_ok!(actor => actor.value))
.await
}
// An example that can fail
pub async fn set_non_negative(&self, v: i32) -> Result<()> {
self.api
.call(act!(actor => async move {
if v < 0 {
Err(anyhow!("negative value"))
} else {
actor.value = v;
Ok(())
}
}))
.await
}
}
// Private actor with its state and mailbox
struct CounterActor {
value: i32,
rx: mpsc::Receiver<Action<CounterActor>>,
}
impl Actor for CounterActor {
async fn run(&mut self) -> Result<()> {
async move {
loop {
tokio::select! {
Some(action) = self.rx.recv() => {
action(self).await;
}
Some(your_bytes) = your_reader.recv() => {
// do other background work if needed
}
// ... other background work ...
}
}
Ok(())
}
}
}
Notes
Handle::callcaptures the call site location and wraps any panic from the actor action into ananyhow::Errordelivered to the caller.- Actions must complete reasonably promptly; the actor processes actions sequentially and a long-running action blocks the mailbox.
- Do not hold references across
.awaitinside actions; prefer moving values or cloning as needed.
Macros§
- act
- Write an actor action that returns
anyhow::Result<T>. - act_ok
- Write an actor action that returns a plain
T(wrapped asOk(T)).
Structs§
- Handle
- A clonable handle to schedule actions onto an actor of type
A.
Traits§
- Actor
- A minimal trait implemented by concrete actor types to run their mailbox.
Functions§
- into_
actor_ fut_ ok - Convert a future yielding
Tinto a boxed future yieldinganyhow::Result<T>. - into_
actor_ fut_ res - Convert a future yielding
anyhow::Result<T>into the standard boxedActorFut. - into_
actor_ fut_ unit_ ok - Convert a unit future (
Future<Output = ()>) into a boxed future yieldinganyhow::Result<()>.
Type Aliases§
- Action
- An action scheduled onto an actor.
- Actor
Fut - The boxed future type returned by actor actions.
- PreBox
Actor Fut - The unboxed future type used for actor actions.