xan_actor/lib.rs
1//! Akka-style actor library for Rust, built on tokio.
2//!
3//! Multiple actor types share a single [`ActorSystem`]; addresses route
4//! messages with compile-time message-type checks (`send::<T>` only accepts
5//! `T::Message`). Each actor runs on its own task with a bounded or
6//! unbounded mailbox, an [`ErrorHandling`] policy, and standard lifecycle
7//! hooks ([`pre_start`], [`pre_restart`], [`post_stop`], [`post_restart`]).
8//!
9//! The optional `multi-node` feature extends the same API across processes
10//! via a [xanq](https://crates.io/crates/xanq) broker — see the
11//! `inter_node` module and the project wiki for setup details.
12//!
13//! See the [project wiki](https://xanthorrhizol.github.io/actor/) for the
14//! full guide.
15//!
16//! [`pre_start`]: actor::Actor::pre_start
17//! [`pre_restart`]: actor::Actor::pre_restart
18//! [`post_stop`]: actor::Actor::post_stop
19//! [`post_restart`]: actor::Actor::post_restart
20
21pub mod actor;
22pub mod actor_system;
23pub mod channel;
24mod error;
25#[cfg(feature = "multi-node")]
26pub mod inter_node;
27pub mod prelude;
28#[cfg(test)]
29mod test;
30pub mod types;
31
32pub use actor::*;
33pub use actor_system::*;
34pub use error::ActorError;
35pub use types::{JobController, JobSpec, Message, RunJobResult};
36pub(crate) use types::{Mailbox, TypedMailbox};
37
38/// Default per-actor mailbox capacity. Used whenever
39/// `Actor::register`'s `channel_size` argument is `None`. Active under
40/// `bounded-channel`; ignored under `unbounded-channel` but kept defined
41/// so the unified API surface compiles.
42pub(crate) const CHANNEL_SIZE: usize = 4096;
43
44#[macro_use]
45extern crate log;
46
47/// Lifecycle state of a registered actor as tracked by `actor_system_loop`.
48///
49/// Drives which lifecycle hook fires next and whether `FindActor` reports
50/// the actor as ready for delivery:
51///
52/// - `Starting`/`Restarting` → mailbox exists but `FindActor` returns
53/// `ready=false`; the system loop reports the actor isn't ready yet.
54/// - `Receiving` → normal operation; sends go through immediately.
55/// - `Stopping`/`Terminated` → terminal phase; the actor task is winding
56/// down or finished. Subsequent sends fail.
57#[derive(Clone, Copy, Debug, PartialEq, Eq)]
58pub enum LifeCycle {
59 /// Initial state right after `Register` is accepted but before
60 /// `pre_start` finishes. Sends are queued (`ready=false`).
61 Starting,
62 /// Active state. `pre_start` (or `post_restart`) has returned and the
63 /// actor is consuming messages from its mailbox.
64 Receiving,
65 /// Transitional state entered when the actor receives a kill or
66 /// restart signal, or its handler returns `Err` under `ErrorHandling::Stop`.
67 /// `post_stop` is about to run.
68 Stopping,
69 /// Final state after `post_stop`; the actor's task has exited.
70 Terminated,
71 /// Transitional state between `Stopping` and the next `Starting`, set
72 /// when the actor is restarting (handler error under `ErrorHandling::Restart`
73 /// or an explicit `restart` call). `pre_restart` runs here.
74 Restarting,
75}
76
77/// Policy applied when an actor's `handle` returns `Err`.
78///
79/// Passed to `Actor::register` and consulted on every handler error.
80#[derive(Clone, Copy, Debug, PartialEq, Eq)]
81pub enum ErrorHandling {
82 /// Log the error and keep the actor running on the same mailbox.
83 /// The failed message is dropped; the next message is processed normally.
84 Resume,
85 /// Tear down the actor's mailbox, run `pre_restart` / `post_restart`,
86 /// and re-enter the receive loop with a fresh mailbox at the same
87 /// address.
88 Restart,
89 /// Run `post_stop` and terminate the actor. Subsequent sends fail.
90 Stop,
91}
92
93/// Choice of tokio task primitive used to host the actor's receive loop.
94///
95/// Picked per actor at `register` time.
96#[derive(Clone, Copy, Debug, PartialEq, Eq)]
97pub enum Blocking {
98 /// Use `tokio::task::spawn_blocking` + `block_on`. Gives the actor a
99 /// dedicated OS thread, so a synchronous-looking handler body that
100 /// blocks for a while won't starve the async runtime.
101 Blocking,
102 /// Use `tokio::spawn`. Cheapest option; the actor cooperates with
103 /// the async runtime and should not block.
104 NonBlocking,
105}
106
107/// Snapshot status for a running job (returned only via `JobController`'s
108/// internal channels — currently exposed for completeness; the public API
109/// uses `abort_job` / `stop_job` / `resume_job` directly).
110#[derive(Clone, Copy, Debug, PartialEq, Eq)]
111pub enum JobStatus {
112 /// The job loop is actively iterating (between sleeps and dispatches).
113 Running,
114 /// The job loop is paused after a `stop_job` and waiting for `resume_job`.
115 Stopped,
116}
117
118/// Bound on `Actor::Message` / `Actor::Result`.
119///
120/// - Without `multi-node`: vacuous (`impl<T: ?Sized> MaybeCodec for T`),
121/// every type satisfies it — single-node users see no extra constraint.
122/// - With `multi-node`: a supertrait alias for `xancode::Codec`, so any
123/// message/result that travels through `send` / `send_and_recv` /
124/// `run_job` must be serializable. Local routing still skips encoding,
125/// but the bound is enforced at compile time regardless.
126///
127/// Implemented automatically via blanket impl; you should never need to
128/// implement it manually.
129#[cfg(not(feature = "multi-node"))]
130pub trait MaybeCodec {}
131#[cfg(not(feature = "multi-node"))]
132impl<T: ?Sized> MaybeCodec for T {}
133
134#[cfg(feature = "multi-node")]
135pub trait MaybeCodec: xancode::Codec {}
136#[cfg(feature = "multi-node")]
137impl<T: xancode::Codec> MaybeCodec for T {}