Skip to content

Commit

Permalink
Update panic documentation on abort. Resolves #214 (#215)
Browse files Browse the repository at this point in the history
  • Loading branch information
slawlor authored Mar 16, 2024
1 parent 6fcdd60 commit 02e8338
Show file tree
Hide file tree
Showing 10 changed files with 221 additions and 14 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ will be supported by `ractor`. There are 4 concurrent message types, which are l
1. Signals: Signals are the highest-priority of all and will interrupt the actor wherever processing currently is (this includes terminating async work). There
is only 1 signal today, which is `Signal::Kill`, and it immediately terminates all work. This includes message processing or supervision event processing.
2. Stop: There is also the 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 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 currently executing work, regardless of the provided reason.
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 are how an actor's supervisor(parent) or peer monitors are notified of events of their children/peers and can handle lifetime events for them.
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 are how an actor's supervisor(parent) or peer monitors are notified of events of their children/peers and can handle lifetime events for them. If you set `panic = 'abort'` in your `Cargo.toml`, panics **will** start cause program termination and not be caught in the supervision flow.
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
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!

Expand Down
2 changes: 1 addition & 1 deletion ractor/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "ractor"
version = "0.9.6"
version = "0.9.7"
authors = ["Sean Lawlor", "Evan Au", "Dillon George"]
description = "A actor framework for Rust"
documentation = "https://docs.rs/ractor"
Expand Down
3 changes: 2 additions & 1 deletion ractor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@
//!
//! Actors in `ractor` also support supervision. This is done by "linking" actors together in a supervisor-child relationship.
//! A supervisor is responsible for the life cycle of the child actor, and as such is notified when the actor starts,
//! stops, and fails (panics).
//! stops, and fails (panics). If you set `panic = 'abort'` in your `Cargo.toml`, panics **will** start cause program termination
//! and not be caught in the supervision flow.
//!
//! Supervision is presently left to the implementor to outline handling of supervision events, but you can see a suite of
//! supervision tests in `crate::actor::tests::supervisor` for examples on the supported functionality.
Expand Down
22 changes: 14 additions & 8 deletions ractor/src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
//! Macro helpers for remote procedure calls
/// `cast!` takes an actor and a message and emits a [crate::RactorErr] error
/// which can be pattern matched on in order to derive the output
/// which can be pattern matched on in order to derive the output.
#[macro_export]
macro_rules! cast {
($actor:expr, $msg:expr) => {
Expand All @@ -24,7 +24,7 @@ macro_rules! cast {
///
/// Returns [Ok(_)] with the result on successful RPC or [Err(crate::RactorErr)] on failure
/// Example usage (without the `cluster` feature)
/// ```no_run
/// ```rust
/// use ractor::{call, Actor, RpcReplyPort, ActorRef, ActorProcessingErr};
/// struct TestActor;
/// enum MessageFormat {
Expand Down Expand Up @@ -62,10 +62,13 @@ macro_rules! cast {
/// }
/// }
///
/// async fn test() {
/// let (actor, _handle) = Actor::spawn(None, TestActor, ()).await.unwrap();
/// #[tokio::main]
/// async fn main() {
/// let (actor, handle) = Actor::spawn(None, TestActor, ()).await.unwrap();
/// let result = call!(actor, MessageFormat::TestRpc, "Something".to_string()).unwrap();
/// assert_eq!(result, "Something".to_string())
/// assert_eq!(result, "Something".to_string());
/// actor.stop(None);
/// handle.await.unwrap();
/// }
/// ```
#[macro_export]
Expand Down Expand Up @@ -143,10 +146,13 @@ macro_rules! call {
/// }
/// }
///
/// async fn test() {
/// let (actor, _handle) = Actor::spawn(None, TestActor, ()).await.unwrap();
/// #[tokio::main]
/// async fn main() {
/// let (actor, handle) = Actor::spawn(None, TestActor, ()).await.unwrap();
/// let result = call_t!(actor, MessageFormat::TestRpc, 50, "Something".to_string()).unwrap();
/// assert_eq!(result, "Something".to_string())
/// assert_eq!(result, "Something".to_string());
/// actor.stop(None);
/// handle.await.unwrap();
/// }
/// ```
#[macro_export]
Expand Down
44 changes: 44 additions & 0 deletions ractor/src/pg/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,50 @@
//! supervision port of the [crate::Actor]
//!
//! Inspired from [Erlang's `pg` module](https://www.erlang.org/doc/man/pg.html)
//!
//! ## Examples
//!
//! ```rust
//! use ractor::{Actor, ActorRef, ActorProcessingErr};
//! use ractor::pg;
//!
//! struct ExampleActor;
//!
//! #[cfg_attr(feature = "async-trait", ractor::async_trait)]
//! impl Actor for ExampleActor {
//! type Msg = ();
//! type State = ();
//! type Arguments = ();
//!
//! async fn pre_start(&self, _myself: ActorRef<Self::Msg>, _args: Self::Arguments) -> Result<Self::State, ActorProcessingErr> {
//! println!("Starting");
//! Ok(())
//! }
//! }
//!
//! #[tokio::main]
//! async fn main() {
//! let (actor, handle) = Actor::spawn(None, ExampleActor, ()).await.expect("Failed to startup dummy actor");
//! let group = "the_group".to_string();
//!
//! // Join the actor to a group. This is also commonly done in `pre_start` or `post_start`
//! // of the actor itself without having to do it externally by some coordinator
//! pg::join(group.clone(), vec![actor.get_cell()]);
//! // Retrieve the pg group membership
//! let members = pg::get_members(&group);
//! // Send a message to the up-casted actor
//! let the_actor: ActorRef<()> = members.get(0).unwrap().clone().into();
//! ractor::cast!(the_actor, ()).expect("Failed to send message");
//!
//! // wait for actor exit
//! actor.stop(None);
//! handle.await.unwrap();
//!
//! // The actor will automatically be removed from the group upon shutdown.
//! let members = pg::get_members(&group);
//! assert_eq!(members.len(), 0);
//! }
//! ```
use std::collections::HashMap;
use std::sync::Arc;
Expand Down
40 changes: 39 additions & 1 deletion ractor/src/registry/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@
//! or agents will runtime panic on message reception, and supervision
//! processes would need to restart the actors.
//!
//! ## Example
//! ## Examples
//!
//! **Basic actor retrieval**
//! ```rust
//! async fn test() {
//! let maybe_actor = ractor::registry::where_is("my_actor".to_string());
Expand All @@ -31,6 +32,43 @@
//! }
//! }
//! ```
//!
//! **Full example**
//!
//! ```rust
//! use ractor::{Actor, ActorRef, ActorProcessingErr};
//! use ractor::registry;
//!
//! struct ExampleActor;
//!
//! #[cfg_attr(feature = "async-trait", ractor::async_trait)]
//! impl Actor for ExampleActor {
//! type Msg = ();
//! type State = ();
//! type Arguments = ();
//!
//! async fn pre_start(&self, _myself: ActorRef<Self::Msg>, _args: Self::Arguments) -> Result<Self::State, ActorProcessingErr> {
//! println!("Starting");
//! Ok(())
//! }
//! }
//!
//! #[tokio::main]
//! async fn main() {
//! let (actor, handle) = Actor::spawn(Some("my_actor".to_string()), ExampleActor, ()).await.expect("Failed to startup dummy actor");
//!
//! // Retrieve the actor by name from the registry
//! let who: ActorRef<()> = registry::where_is("my_actor".to_string()).expect("Failed to find actor").into();
//! who.cast(()).expect("Failed to send message");
//!
//! // wait for actor exit
//! actor.stop(None);
//! handle.await.unwrap();
//!
//! // Automatically removed from the registry upon shutdown
//! assert!(registry::where_is("my_actor".to_string()).is_none());
//! }
//! ```
use std::sync::Arc;

Expand Down
60 changes: 60 additions & 0 deletions ractor/src/rpc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,66 @@
//! standard [Erlang `gen_server`](https://www.erlang.org/doc/man/gen_server.html#cast-2).
//! The tl;dr is that `cast` is an send without waiting on a reply while `call` is expecting
//! a reply from the actor being communicated with.
//!
//! ## Examples
//!
//! ```rust
//! use ractor::{cast, call, call_t};
//! use ractor::concurrency::Duration;
//! use ractor::{Actor, ActorRef, ActorProcessingErr, RpcReplyPort};
//!
//! struct ExampleActor;
//!
//! enum ExampleMessage {
//! Cast,
//! Call(RpcReplyPort<String>),
//! }
//!
//! #[cfg(feature = "cluster")]
//! impl ractor::Message for ExampleMessage {}
//!
//! #[cfg_attr(feature = "async-trait", ractor::async_trait)]
//! impl Actor for ExampleActor {
//! type Msg = ExampleMessage;
//! type State = ();
//! type Arguments = ();
//!
//! async fn pre_start(&self, _myself: ActorRef<Self::Msg>, _args: Self::Arguments) -> Result<Self::State, ActorProcessingErr> {
//! println!("Starting");
//! Ok(())
//! }
//!
//! async fn handle(&self, _myself: ActorRef<Self::Msg>, message: Self::Msg, _state: &mut Self::State) -> Result<(), ActorProcessingErr> {
//! match message {
//! ExampleMessage::Cast => println!("Cast message"),
//! ExampleMessage::Call(reply) => {
//! println!("Call message");
//! let _ = reply.send("a reply".to_string());
//! }
//! }
//! Ok(())
//! }
//! }
//!
//! #[tokio::main]
//! async fn main() {
//! let (actor, handle) = Actor::spawn(None, ExampleActor, ()).await.expect("Failed to startup dummy actor");
//!
//! // send a 1-way message (equivalent patterns)
//! actor.cast(ExampleMessage::Cast).expect("Failed to send message");
//! cast!(actor, ExampleMessage::Cast).expect("Failed to send message");
//!
//! // Send a message to the actor, with an associated reply channel,
//! // and wait for the reply from the actor (optionally up to a timeout)
//! let _result = actor.call(ExampleMessage::Call, Some(Duration::from_millis(100))).await.expect("Failed to call actor");
//! let _result = call!(actor, ExampleMessage::Call).expect("Failed to call actor");
//! let _result = call_t!(actor, ExampleMessage::Call, 100).expect("Failed to call actor with timeout");
//!
//! // wait for actor exit
//! actor.stop(None);
//! handle.await.unwrap();
//! }
//! ```
use crate::concurrency::{self, Duration, JoinHandle};

Expand Down
58 changes: 58 additions & 0 deletions ractor/src/time/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,64 @@
//! 2. Send after a delay
//! 3. Stop after a delay
//! 4. Kill after a delay
//!
//! ## Examples
//!
//! ```rust
//! use ractor::concurrency::Duration;
//! use ractor::{Actor, ActorRef, ActorProcessingErr};
//!
//! struct ExampleActor;
//!
//! enum ExampleMessage {
//! AfterDelay,
//! OnPeriod,
//! }
//!
//! #[cfg(feature = "cluster")]
//! impl ractor::Message for ExampleMessage {}
//!
//! #[cfg_attr(feature = "async-trait", ractor::async_trait)]
//! impl Actor for ExampleActor {
//! type Msg = ExampleMessage;
//! type State = ();
//! type Arguments = ();
//!
//! async fn pre_start(&self, _myself: ActorRef<Self::Msg>, _args: Self::Arguments) -> Result<Self::State, ActorProcessingErr> {
//! println!("Starting");
//! Ok(())
//! }
//!
//! async fn handle(&self, _myself: ActorRef<Self::Msg>, message: Self::Msg, _state: &mut Self::State) -> Result<(), ActorProcessingErr> {
//! match message {
//! ExampleMessage::AfterDelay => println!("After delay"),
//! ExampleMessage::OnPeriod => println!("On period"),
//! }
//! Ok(())
//! }
//! }
//!
//! #[tokio::main]
//! async fn main() {
//! let (actor, handle) = Actor::spawn(None, ExampleActor, ()).await.expect("Failed to startup dummy actor");
//!
//! // send the message after a 100ms delay
//! actor.send_after(Duration::from_millis(100), || ExampleMessage::AfterDelay);
//!
//! // send this message every 10ms
//! actor.send_interval(Duration::from_millis(10), || ExampleMessage::OnPeriod);
//!
//! // Exit the actor after 200ms (equivalent of calling `stop(maybe_reason)`)
//! actor.exit_after(Duration::from_millis(200));
//!
//! // Kill the actor after 300ms (won't execute since we did stop before, but here
//! // as an example)
//! actor.kill_after(Duration::from_millis(300));
//!
//! // wait for actor exit
//! handle.await.unwrap();
//! }
//! ```
use crate::concurrency::{Duration, JoinHandle};

Expand Down
2 changes: 1 addition & 1 deletion ractor_cluster/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "ractor_cluster"
version = "0.9.6"
version = "0.9.7"
authors = ["Sean Lawlor", "Evan Au", "Dillon George"]
description = "Distributed cluster environment of Ractor actors"
documentation = "https://docs.rs/ractor"
Expand Down
2 changes: 1 addition & 1 deletion ractor_cluster_derive/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "ractor_cluster_derive"
version = "0.9.6"
version = "0.9.7"
authors = ["Sean Lawlor <seanlawlor@fb.com>"]
description = "Derives for ractor_cluster"
license = "MIT"
Expand Down

0 comments on commit 02e8338

Please sign in to comment.