Struct stakker::Actor

source ·
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>

source

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.

source

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.

source

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.

source

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.

source

pub fn id(&self) -> LogID

Get the logging-ID of this actor. If the logger feature isn’t enabled, returns 0.

source

pub fn defer(&self, f: impl FnOnce(&mut Stakker) + 'static)

This may be used to submit items to the Deferrer main queue from a drop handler, without needing a Core reference.

source

pub fn access_deferrer(&self) -> &Deferrer

Used in macros to get a Deferrer reference

source

pub fn access_actor(&self) -> &Self

Used in macros to get an Actor reference

source

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.

Trait Implementations§

source§

impl<A> Clone for Actor<A>

source§

fn clone(&self) -> Self

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more

Auto Trait Implementations§

§

impl<A> !RefUnwindSafe for Actor<A>

§

impl<A> !Send for Actor<A>

§

impl<A> !Sync for Actor<A>

§

impl<A> Unpin for Actor<A>

§

impl<A> !UnwindSafe for Actor<A>

Blanket Implementations§

source§

impl<T> Any for Twhere T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T, U> Into<U> for Twhere U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T> ToOwned for Twhere T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.