Pantomime
Getting StartedActors
Overview Hierarchy Ordering Supervision TestingStreams
Overview Stages TCP & UDP Actor Integration Supervision TestingIntegration
Futures TokioReference
API Documentation ExamplesActors / Overview
Actors are the building blocks of Pantomime. An actor is a stateful computation abstraction based on the principal of message passing. When an actor receives a message, it may mutate its internal state, spawn child actors, and message other actors.
Concepts
- Actor
- An actor receives messages and signals, and processes them serially. Upon receipt of a message or signal, it may mutate its own state and spawn other actors. Actors are lightweight -- you can have millions of them on a modest system.
- Dispatcher
- A dispatcher is responsible for executing thunks of work. For example, this may include executing an actor when it is messaged, or invoking a function that was previously scheduled. An actor may specify a custom dispatcher if it has particular execution requirements. For instance, an actor that performs blocking I/O may wish to be executed on a dispatcher that is reserved for blocking operations, to prevent starving the primary dispatcher.
- Mailbox
- When an actor is messaged, the message is placed in a mailbox. By default, actors are assigned to a shard to share common infrastructure and reduce memory consumption. An actor may specify a custom mailbox implementation if it wishes to control the delivery semantics, for instance message priority.
- Shard
- A shard consists of a mailbox and a number of actors. The number of shards is determined at runtime by configuration, but is static in that once the system has been started, the number of shards cannot change. If an actor specifies a custom dispatcher or mailbox, it is placed in its own shard.
Defining Actors
To define an actor, declare a struct with the state that your actor contains, and implement the Actor trait for the message type that your actor can receive.
Receiving Messages
When defining an actor, you must declare the receive method. When an actor is messaged, this method will be called with the message instance.Receiving Signals
An actor may also define a receive_signal method. This can be used to react to lifecycle notifications. For example, when an actor is spawned, this method will be invoked with a Signal::Started message.Example
The following example shows a simple actor that maintains a counter. When it receives a GetAndAdd message, it replies with the current value of the counter and then adds the supplied value.
use pantomime::prelude::*;
enum CountingMsg {
GetAndAdd(u64, ActorRef<u64>)
}
struct CountingActor {
value: u64
}
impl Actor<CountingMsg> for CountingActor {
fn receive(&mut self, msg: CountingMsg, _: ActorContext<CountingMsg>) {
match msg {
CountingMsg::GetAndAdd(value, reply_to) => {
reply_to.tell(self.value);
self.value += value;
}
}
}
}
Creating an ActorSystem
An ActorSystem contains all of the machinery required to spawn and execute actors. This involves spawning a small number of threads for executing the actors (related to available CPUs), a thread to handle I/O events, a thread to generate timer ticks, and a thread to handle actor system events.
A typical application will create an ActorSystem in its main function, spawn any root actors it needs, and then invoke join to start handling system events. For example:
use pantomime::prelude::*;
fn main() {
// create an ActorSystem
let mut system = ActorSystem::new();
// use system.spawn(..) to spawn your root actors, e.g.
// system.spawn(MyActor);
// system.spawn(MyOtherActor);
// use this thread to handle system messages
system.join();
}
Spawning Actors
Root
Root actors are top-level actors without parents. The only difference from child actors is how they are spawned. Instead of calling spawn on an ActorContext, root actors are spawned by calling spawn on an ActorSystem.
Child
Child actors are spawned from within other actors. To do this, spawn is called on the actor's ActorContext.