Skip to main content

ractor/actor/
messages.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//! Messages which are built-in for `ractor`'s processing routines
7//!
8//! Additionally contains definitions for [BoxedState]
9//! which are used to handle strongly-typed states in a
10//! generic way without having to know the strong type in the underlying framework
11
12use std::any::Any;
13use std::fmt::Debug;
14
15use crate::message::BoxedDowncastErr;
16use crate::ActorProcessingErr;
17use crate::State;
18
19/// A "boxed" message denoting a strong-type message
20/// but generic so it can be passed around without type
21/// constraints
22pub struct BoxedState {
23    /// The message value
24    pub msg: Option<Box<dyn Any + Send>>,
25}
26
27impl Debug for BoxedState {
28    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29        f.debug_struct("BoxedState").finish()
30    }
31}
32
33impl BoxedState {
34    /// Create a new [BoxedState] from a strongly-typed message
35    pub fn new<T>(msg: T) -> Self
36    where
37        T: State,
38    {
39        Self {
40            msg: Some(Box::new(msg)),
41        }
42    }
43
44    /// Try and take the resulting message as a specific type, consumes
45    /// the boxed message
46    pub fn take<T>(&mut self) -> Result<T, BoxedDowncastErr>
47    where
48        T: State,
49    {
50        match self.msg.take() {
51            Some(m) => {
52                if m.is::<T>() {
53                    Ok(*m.downcast::<T>().unwrap())
54                } else {
55                    Err(BoxedDowncastErr)
56                }
57            }
58            None => Err(BoxedDowncastErr),
59        }
60    }
61}
62
63/// Messages to stop an actor
64#[derive(Debug)]
65pub enum StopMessage {
66    /// Normal stop
67    Stop,
68    /// Stop with a reason
69    Reason(String),
70}
71
72impl std::fmt::Display for StopMessage {
73    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
74        match self {
75            Self::Stop => write!(f, "Stop"),
76            Self::Reason(reason) => write!(f, "Stop (reason = {reason})"),
77        }
78    }
79}
80
81#[cfg(feature = "cluster")]
82impl crate::Message for StopMessage {}
83
84/// A supervision event from the supervision tree
85pub enum SupervisionEvent {
86    /// An actor was started
87    ActorStarted(super::actor_cell::ActorCell),
88    /// An actor terminated. In the event it shutdown cleanly (i.e. didn't panic or get
89    /// signaled) we capture the last state of the actor which can be used to re-build an actor
90    /// should the need arise. Includes an optional "exit reason" if it could be captured
91    /// and was provided
92    ActorTerminated(
93        super::actor_cell::ActorCell,
94        Option<BoxedState>,
95        Option<String>,
96    ),
97    /// An actor failed (due to panic or error case)
98    ActorFailed(super::actor_cell::ActorCell, ActorProcessingErr),
99
100    /// A subscribed process group changed
101    ProcessGroupChanged(crate::pg::GroupChangeMessage),
102
103    /// A process lifecycle event occurred
104    #[cfg(feature = "cluster")]
105    PidLifecycleEvent(crate::registry::PidLifecycleEvent),
106}
107
108#[cfg(feature = "cluster")]
109impl crate::Message for SupervisionEvent {}
110
111impl SupervisionEvent {
112    /// If this supervision event refers to an [Actor] lifecycle event, return
113    /// the [ActorCell] for that [actor][Actor].
114    ///
115    ///
116    /// [ActorCell]: crate::ActorCell
117    /// [Actor]: crate::Actor
118    pub fn actor_cell(&self) -> Option<&super::actor_cell::ActorCell> {
119        match self {
120            Self::ActorStarted(who)
121            | Self::ActorFailed(who, _)
122            | Self::ActorTerminated(who, _, _) => Some(who),
123            _ => None,
124        }
125    }
126
127    /// If this supervision event refers to an [Actor] lifecycle event, return
128    /// the [ActorId] for that [actor][Actor].
129    ///
130    /// [ActorId]: crate::ActorId
131    /// [Actor]: crate::Actor
132    pub fn actor_id(&self) -> Option<super::actor_id::ActorId> {
133        self.actor_cell().map(|cell| cell.get_id())
134    }
135
136    /// Clone the supervision event, without requiring inner data
137    /// be cloneable. This means that the actor error (if present) is converted
138    /// to a string and copied as well as the state upon termination being not
139    /// propogated. If the state were cloneable, we could propogate it, however
140    /// that restriction is overly restrictive, so we've avoided it.
141    #[cfg(feature = "monitors")]
142    pub(crate) fn clone_no_data(&self) -> Self {
143        match self {
144            Self::ActorStarted(who) => Self::ActorStarted(who.clone()),
145            Self::ActorFailed(who, what) => {
146                Self::ActorFailed(who.clone(), From::from(format!("{what}")))
147            }
148            Self::ProcessGroupChanged(what) => Self::ProcessGroupChanged(what.clone()),
149            Self::ActorTerminated(who, _state, msg) => {
150                Self::ActorTerminated(who.clone(), None, msg.as_ref().cloned())
151            }
152            #[cfg(feature = "cluster")]
153            Self::PidLifecycleEvent(evt) => Self::PidLifecycleEvent(evt.clone()),
154        }
155    }
156}
157
158impl Debug for SupervisionEvent {
159    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
160        write!(f, "Supervision event: {self}")
161    }
162}
163
164impl std::fmt::Display for SupervisionEvent {
165    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
166        match self {
167            SupervisionEvent::ActorStarted(actor) => {
168                write!(f, "Started actor {actor:?}")
169            }
170            SupervisionEvent::ActorTerminated(actor, _, reason) => {
171                if let Some(r) = reason {
172                    write!(f, "Stopped actor {actor:?} (reason = {r})")
173                } else {
174                    write!(f, "Stopped actor {actor:?}")
175                }
176            }
177            SupervisionEvent::ActorFailed(actor, panic_msg) => {
178                write!(f, "Actor panicked {actor:?} - {panic_msg}")
179            }
180            SupervisionEvent::ProcessGroupChanged(change) => {
181                write!(
182                    f,
183                    "Process group {} in scope {} changed",
184                    change.get_group(),
185                    change.get_scope()
186                )
187            }
188            #[cfg(feature = "cluster")]
189            SupervisionEvent::PidLifecycleEvent(change) => {
190                write!(f, "PID lifecycle event {change:?}")
191            }
192        }
193    }
194}
195
196/// A signal message which takes priority above all else
197#[derive(Clone, Debug)]
198pub enum Signal {
199    /// Terminate the agent, cancelling all async work immediately
200    Kill,
201}
202
203impl std::fmt::Display for Signal {
204    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
205        match self {
206            Self::Kill => {
207                write!(f, "killed")
208            }
209        }
210    }
211}