pub struct Actor<A: 'static> { /* private fields */ }
Expand description
A ref-counting reference to an actor
This may be cloned to get another reference to the same actor.
Example implementation of an minimal actor
struct Light {
on: bool,
}
impl Light {
pub fn init(_cx: CX![], on: bool) -> Option<Self> {
Some(Self { on })
}
pub fn set(&mut self, _cx: CX![], on: bool) {
self.on = on;
}
pub fn get(&self, cx: CX![], ret: Ret<bool>) {
ret!([ret], self.on);
}
}
Internal state of an actor
An actor may be in one of three states: Prep, Ready or
Zombie. This is independent of whether there are still
references to it. The actor only has a self
value in the
Ready state.
Lifecycle of an actor
“Prep” state: As soon as ActorOwn::new
returns, the actor
exists and is in the Prep state. It has an actor reference,
but it does not yet have a self
value. It is possible to create
Fwd
or Ret
instances referring to methods in this actor,
and to pass the actor reference to other actors. However any
normal actor calls will be queued up until the actor becomes
ready. The only calls that are permitted on the actor in the
Prep state are calls to static methods with the signature fn method(cx: CX![], ...) -> Option<Self>
.
An actor call should be made to one of the static methods on the
actor to start the process of initialising it. Initialisation may
be immediate, or it may start an asynchronous process (for
example, making a connection to a remote server). Each call to a
static method may schedule callbacks to other static methods.
Eventually, one of these methods should either return
Some(value)
or else fail the actor.
“Ready” state: As soon as a Self
value is returned from a
Prep method, this is installed as the actor’s self
value,
and the actor moves to the Ready state. Any calls to the
actor that were queued up whilst it was in the Prep state are
flushed and executed at this point. Whilst in the Ready
state, the actor can only execute calls to methods with the
signatures fn method(&mut self, cx: CX![], ...)
or fn method(&self, cx: CX![], ...)
. Any Prep-style calls that
occur will be dropped. Now deferred calls from timers or other
actors will execute immediately on reaching the front of the
queue. This is the normal operating mode of the actor.
“Zombie” state: The Zombie state can be entered for
various reasons. The first is normal shutdown of the actor
through the Cx::stop
method. The second is failure of the
actor through the Cx::fail
, Cx::fail_str
or
Cx::fail_string
methods. The third is through being killed
externally through the ActorOwn::kill
, ActorOwn::kill_str
or ActorOwn::kill_string
methods. The fourth is through the
last owning reference to the actor being dropped. Termination of
the actor is notified to the StopCause
handler provided to the
ActorOwn::new
method when the actor was created.
Once an actor is a Zombie it never leaves that state. The
self
value is dropped and all resources are released.
References remain valid until the last reference is dropped, but
the Actor::is_zombie
method will return true. Any calls
queued for the actor will be dropped. (Note that if you need to
have a very long-lived actor but you also need to restart the
actor on failure, consider having one actor wrap another.)
Ownership of an actor and automatic termination on drop
There are two forms of reference to an actor: ActorOwn
instances are strong references, and Actor
, Fwd
and
Ret
instances are weak references. When the last ActorOwn
reference is dropped, the actor will be terminated automatically.
After termination, the actor self
state data is dropped, but the
actor stays in memory in the Zombie state until the final weak
references are dropped.
The normal approach is to use ActorOwn
references to control
the termination of the actor. If the actor relationships are
designed such that there can be no cycles in the ActorOwn
graph (e.g. it will always be a simple tree), then cleanup is safe
and straightforward even in the presence of Actor
, Fwd
or
Ret
reference cycles. Everything will be cleaned up correctly
by simply dropping things when they are no longer required. This
means that when an actor fails with fail!
, all of its child
actors will automatically be terminated too.
This also handles the case where many actors hold owning
references to a common shared actor. The common actor will only
be terminated when the last ActorOwn
reference is dropped.
However if the situation doesn’t fit the “no cyclic owning
references” model (i.e. ownership cannot be represented as a
directed-acyclic-graph), then other termination strategies are
possible, since an actor can be terminated externally using a
kill!
operation. This would normally be a design decision to
solve some particularly difficult problem, and in this case the
coder must ensure proper cleanup occurs using kill!
instead of
simply relying on drop.
Implementations§
source§impl<A> Actor<A>
impl<A> Actor<A>
sourcepub fn is_zombie(&self) -> bool
pub fn is_zombie(&self) -> bool
Check whether the actor is a zombie. Note that this call is
less useful than it appears, since the actor may become a
zombie between the time you make this call and whatever
asynchronous operation follows. It is better to make a call
with a ret_to!
callback which will send back a None
if
the actor has died or if the actor drops the Ret
for any
other reason.
sourcepub fn apply_prep(
&self,
s: &mut Stakker,
f: impl FnOnce(&mut Cx<'_, A>) -> Option<A>
)
pub fn apply_prep( &self, s: &mut Stakker, f: impl FnOnce(&mut Cx<'_, A>) -> Option<A> )
Apply a closure to the actor if it is in the Prep state, otherwise do nothing. This is used to implement deferred prep calls.
sourcepub fn apply(
&self,
s: &mut Stakker,
f: impl FnOnce(&mut A, &mut Cx<'_, A>) + 'static
)
pub fn apply( &self, s: &mut Stakker, f: impl FnOnce(&mut A, &mut Cx<'_, A>) + 'static )
Apply a closure to the actor when it reaches the Ready state. If the actor is already in the Ready state, the closure is executed immediately. If the actor is in the Prep state, then it queues the operation instead of executing it. This is used to implement deferred ready calls.
sourcepub fn query<R>(
&self,
s: &mut Stakker,
f: impl FnOnce(&mut A, &mut Cx<'_, A>) -> R
) -> Option<R>
pub fn query<R>( &self, s: &mut Stakker, f: impl FnOnce(&mut A, &mut Cx<'_, A>) -> R ) -> Option<R>
Query an actor from outside the runtime. This is a
synchronous call intended for use when interfacing to external
code. Executes the closure on the actor immediately if the
actor has a Self
value (i.e. is in the Ready state), and
returns the result. Otherwise returns None
.
sourcepub fn id(&self) -> LogID
pub fn id(&self) -> LogID
Get the logging-ID of this actor. If the logger feature isn’t enabled, returns 0.
sourcepub fn access_deferrer(&self) -> &Deferrer
pub fn access_deferrer(&self) -> &Deferrer
Used in macros to get a Deferrer
reference
sourcepub fn access_actor(&self) -> &Self
pub fn access_actor(&self) -> &Self
Used in macros to get an Actor
reference
sourcepub fn access_log_id(&self) -> LogID
pub fn access_log_id(&self) -> LogID
Used in macros to get the actor’s logging-ID. If the logger feature isn’t enabled, returns 0.