Skip to main content

ractor/
lib.rs

1// Copyright (c) Sean Lawlor
2//
3// This source code is licensed under both the MIT license found in the
4// LICENSE-MIT file in the root directory of this source tree.
5
6//! `ractor`: A pure-Rust actor framework. Inspired from [Erlang's `gen_server`](https://www.erlang.org/doc/man/gen_server.html),
7//! with the speed + performance of Rust!
8//!
9//! ## Installation
10//!
11//! Install `ractor` by adding the following to your Cargo.toml dependencies
12//!
13//! ```toml
14//! [dependencies]
15//! ractor = "0.15"
16//! ```
17//!
18//! The minimum supported Rust version (MSRV) is 1.64. However if you disable the `async-trait` feature, then you need Rust >= 1.75 due to the native
19//! use of `async fn` in traits. See the [Rust blog](https://blog.rust-lang.org/2023/12/21/async-fn-rpit-in-traits.html).
20//!
21//! ## Getting started
22//!
23//! An example "ping-pong" actor might be the following
24//!
25//! ```rust
26//! use ractor::Actor;
27//! use ractor::ActorProcessingErr;
28//! use ractor::ActorRef;
29//!
30//! /// [PingPong] is a basic actor that will print
31//! /// ping..pong.. repeatedly until some exit
32//! /// condition is met (a counter hits 10). Then
33//! /// it will exit
34//! pub struct PingPong;
35//!
36//! /// This is the types of message [PingPong] supports
37//! #[derive(Debug, Clone)]
38//! pub enum Message {
39//!     Ping,
40//!     Pong,
41//! }
42//! #[cfg(feature = "cluster")]
43//! impl ractor::Message for Message {}
44//!
45//! impl Message {
46//!     // retrieve the next message in the sequence
47//!     fn next(&self) -> Self {
48//!         match self {
49//!             Self::Ping => Self::Pong,
50//!             Self::Pong => Self::Ping,
51//!         }
52//!     }
53//!     // print out this message
54//!     fn print(&self) {
55//!         match self {
56//!             Self::Ping => print!("ping.."),
57//!             Self::Pong => print!("pong.."),
58//!         }
59//!     }
60//! }
61//!
62//! // the implementation of our actor's "logic"
63//! #[cfg_attr(feature = "async-trait", ractor::async_trait)]
64//! impl Actor for PingPong {
65//!     // An actor has a message type
66//!     type Msg = Message;
67//!     // and (optionally) internal state
68//!     type State = u8;
69//!     // Startup arguments for actor initialization
70//!     type Arguments = ();
71//!
72//!     // Initially we need to create our state, and potentially
73//!     // start some internal processing (by posting a message for
74//!     // example)
75//!     async fn pre_start(
76//!         &self,
77//!         myself: ActorRef<Self::Msg>,
78//!         _: (),
79//!     ) -> Result<Self::State, ActorProcessingErr> {
80//!         // startup the event processing
81//!         myself.send_message(Message::Ping).unwrap();
82//!         Ok(0u8)
83//!     }
84//!
85//!     // This is our main message handler
86//!     async fn handle(
87//!         &self,
88//!         myself: ActorRef<Self::Msg>,
89//!         message: Self::Msg,
90//!         state: &mut Self::State,
91//!     ) -> Result<(), ActorProcessingErr> {
92//!         if *state < 10u8 {
93//!             message.print();
94//!             myself.send_message(message.next()).unwrap();
95//!             *state += 1;
96//!         } else {
97//!             myself.stop(None);
98//!             // don't send another message, rather stop the agent after 10 iterations
99//!         }
100//!         Ok(())
101//!     }
102//! }
103//!
104//! async fn run() {
105//!     let (_, actor_handle) = Actor::spawn(None, PingPong, ())
106//!         .await
107//!         .expect("Failed to start actor");
108//!     actor_handle.await.expect("Actor failed to exit cleanly");
109//! }
110//! ```
111//!
112//! which will output
113//!
114//! ```bash
115//! $ cargo run
116//! ping..pong..ping..pong..ping..pong..ping..pong..ping..pong..
117//! $
118//! ```
119//!
120//! ## Supervision
121//!
122//! Actors in `ractor` also support supervision. This is done by "linking" actors together in a supervisor-child relationship.
123//! A supervisor is responsible for the life cycle of the child actor, and as such is notified when the actor starts,
124//! stops, and fails (panics). If you set `panic = 'abort'` in your `Cargo.toml`, panics **will** start cause program termination
125//! and not be caught in the supervision flow.
126//!
127//! Supervision is presently left to the implementor to outline handling of supervision events, but you can see a suite of
128//! supervision tests in `crate::actor::tests::supervisor` for examples on the supported functionality.
129//!
130//! NOTE: panic's in `pre_start` of an actor will cause failures to spawn, rather than supervision notified failures as the actor hasn't "linked"
131//! to its supervisor yet. However failures in `post_start`, `handle`, `handle_supervisor_evt`, `post_stop` will notify the supervisor should a failure
132//! occur. See [crate::Actor] documentation for more information
133//!
134//! There is additionally a "monitor" API which gives non-direct-supervision logic style monitoring akin to Erlang's [process monitors](https://www.erlang.org/doc/system/ref_man_processes.html#monitors).
135//! This functionality is opt-in via feature `monitors` on the `ractor` crate.
136//!
137//! ## Messaging actors
138//!
139//! The means of communication between actors is that they pass messages to each other. A developer can define any message type which is `Send + 'static` and it
140//! will be supported by `ractor`. There are 4 concurrent message types, which are listened to in priority. They are
141//!
142//! 1. Signals: Signals are the highest-priority of all and will interrupt the actor wherever processing currently is (this includes terminating async work). There
143//!    is only 1 signal today, which is `Signal::Kill`, and it immediately terminates all work. This includes message processing or supervision event processing.
144//! 2. Stop: There is also a pre-defined stop signal. You can give a "stop reason" if you want, but it's optional. Stop is a graceful exit, meaning currently executing async
145//!    work will complete, and on the next message processing iteration Stop will take priority over future supervision events or regular messages. It will **not** terminate
146//!    currently executing work, regardless of the provided reason.
147//! 3. SupervisionEvent: Supervision events are messages from child actors to their supervisors in the event of their startup, death, and/or unhandled panic. Supervision events
148//!    are how an actor's supervisor(s) are notified of events of their children and can handle lifetime events for them.
149//! 4. Messages: Regular, user-defined, messages are the last channel of communication to actors. They are the lowest priority of the 4 message types and denote general actor work. The first
150//!    3 messages types (signals, stop, supervision) are generally quiet unless it's a lifecycle event for the actor, but this channel is the "work" channel doing what your actor wants to do!
151#![warn(
152    dead_code,
153    missing_debug_implementations,
154    missing_docs,
155    rust_2018_idioms,
156    rustdoc::all,
157    rustdoc::missing_crate_level_docs,
158    unreachable_pub,
159    unused_imports,
160    unused_variables,
161    unused_crate_dependencies,
162    clippy::mod_module_files,
163    unsafe_code
164)]
165
166// ======================== Modules ======================== //
167
168pub mod actor;
169#[cfg(test)]
170pub(crate) mod common_test;
171#[cfg(test)]
172pub use common_test::*;
173pub mod concurrency;
174pub mod errors;
175pub mod factory;
176pub mod macros;
177pub mod message;
178pub mod pg;
179pub mod port;
180pub mod registry;
181pub mod rpc;
182#[cfg(feature = "cluster")]
183pub mod serialization;
184pub mod thread_local;
185pub mod time;
186
187use concurrency::JoinHandle;
188#[cfg(not(feature = "async-trait"))]
189use strum as _;
190// ======================== Test Modules and blind imports ======================== //
191
192#[cfg(test)]
193mod tests;
194// ======================== Re-exports ======================== //
195pub use actor::actor_cell::ActorCell;
196pub use actor::actor_cell::ActorStatus;
197pub use actor::actor_cell::ACTIVE_STATES;
198pub use actor::actor_id::ActorId;
199pub use actor::actor_ref::ActorRef;
200pub use actor::derived_actor::DerivedActorRef;
201pub use actor::messages::Signal;
202pub use actor::messages::SupervisionEvent;
203pub use actor::Actor;
204pub use actor::ActorRuntime;
205#[cfg(feature = "async-trait")]
206pub use async_trait::async_trait;
207#[cfg(test)]
208use criterion as _;
209pub use errors::ActorErr;
210pub use errors::ActorProcessingErr;
211pub use errors::MessagingErr;
212pub use errors::RactorErr;
213pub use errors::SpawnErr;
214pub use message::Message;
215#[cfg(test)]
216use paste as _;
217pub use port::OutputMessage;
218pub use port::OutputPort;
219pub use port::RpcReplyPort;
220#[cfg(test)]
221use rand as _;
222#[cfg(feature = "cluster")]
223pub use serialization::BytesConvertable;
224#[cfg(test)]
225use tracing_glog as _;
226#[cfg(test)]
227use tracing_subscriber as _;
228
229// ======================== Type aliases and Trait definitions ======================== //
230
231/// An actor's name, equivalent to an [Erlang `atom()`](https://www.erlang.org/doc/reference_manual/data_types.html#atom)
232pub type ActorName = String;
233
234/// A process group's name, equivalent to an [Erlang `atom()`](https://www.erlang.org/doc/reference_manual/data_types.html#atom)
235pub type GroupName = String;
236
237/// A scope's name, equivalent to an [Erlang `atom()`](https://www.erlang.org/doc/reference_manual/data_types.html#atom)
238pub type ScopeName = String;
239
240/// Represents the state of an actor. Must be safe
241/// to send between threads (same bounds as a [Message])
242pub trait State: std::any::Any + Send + 'static {}
243impl<T: std::any::Any + Send + 'static> State for T {}
244
245// ======================== Helper Functionality ======================== //
246
247/// Perform a background-spawn of an actor. This is a utility wrapper over [Actor::spawn] which
248/// assumes the actor implementation implements [Default].
249///
250/// * `args` - The arguments to start the actor
251///
252/// Returns [Ok((ActorRef, JoinHandle<()>))] upon successful actor startup, [Err(SpawnErr)] otherwise
253pub async fn spawn<T: Actor + Default>(
254    args: T::Arguments,
255) -> Result<(ActorRef<T::Msg>, JoinHandle<()>), SpawnErr> {
256    T::spawn(None, T::default(), args).await
257}
258
259/// Perform a background-spawn of an thread-local actor. This is a utility wrapper over [thread_local::ThreadLocalActor::spawn]
260/// which assumes the actor implementation implements [Default].
261///
262/// * `args` - The arguments to start the actor
263/// * `spawner` - The thread-local spawner ([thread_local::ThreadLocalActorSpawner]) used to spawn thread-local actors
264///
265/// Returns [Ok((ActorRef, JoinHandle<()>))] upon successful actor startup, [Err(SpawnErr)] otherwise
266pub async fn spawn_local<T: thread_local::ThreadLocalActor>(
267    args: T::Arguments,
268    spawner: thread_local::ThreadLocalActorSpawner,
269) -> Result<(ActorRef<T::Msg>, JoinHandle<()>), SpawnErr> {
270    T::spawn(None, args, spawner).await
271}
272
273/// Perform a background-spawn of an actor with the provided name. This is a utility wrapper
274/// over [Actor::spawn] which assumes the actor implementation implements [Default].
275///
276/// * `name` - The name for the actor to spawn
277/// * `args` - The arguments to start the actor
278///
279/// Returns [Ok((ActorRef, JoinHandle<()>))] upon successful actor startup, [Err(SpawnErr)] otherwise
280pub async fn spawn_named<T: Actor + Default>(
281    name: crate::ActorName,
282    args: T::Arguments,
283) -> Result<(ActorRef<T::Msg>, JoinHandle<()>), SpawnErr> {
284    T::spawn(Some(name), T::default(), args).await
285}