Actors
Actor-oriented concurrent and distributed programming.
to use bindings from this package
(import :std/actor)
Note
This page documents the API of the actors package in Gerbil v0.18 and later; the legacy actor package is deprecated, but still available in :std/actor-v13
if you need time to port existing code.
Messaging Primitives
->
(-> dest msg
replyto: (replyto #f)
expiry: (expiry #f)
reply-expected: (reply-expected? #f))
Sends a message to dest
, which must be a thread or actor handle, wrapped in an envelope.
replyto
is an optional nonce when sending a reply to a previous message.expiry
is an optional expiry time (as a time object), which denotes the expiry of the message. Expired messages will not be processed by the reaction macro<-
below.reply-expected?
is a boolean indicated whether a reply is expected to this message. It is a hint for message routers to send back a control reply in case of routing errors. Returns the nonce of the message or#f
if the message was not sent because the destination actor was detected as dead.
->>
(->> dest msg
replyto: (replyto #f)
timeout: (timeo (default-reply-timeout)))
Sends a message to dest and waits for the reply.
replyto
is an optional nonce indicating a reply to a previous interaction.timeout
is the timeout for receiving the reply, which must be a relative time (a positive real) or an absolute time (a time object).
Returns the value received in the reply message.
Raises an actor error if the message could not be sent because the destination was detected as dead.
Raises a timeout error if waiting for the reply times out.
-->
(--> result)
Sends result as a reply to the source of the message in the current
reaction context; it must be invoked within the lexical scope of a
<-
reaction.
-->?
(-->? result)
Conditionally sends result as a reply to the source of the message in
the current reaction context, if a reply is expected; it must be
invoked within the lexical scope of a <-
reaction.
<-
(<- (pattern body ...) ...
[,(reaction-rule-macro ...) ...]
[,@(multiple-reaction-rules-macro ...) ...]
[timeout: timeo]
[(else body ...)])
Receives an enveloped message from the thread's mailbox matching one
of the patterns and dispatching the body accordingly; patterns are
matched with match
.
If there is a timeout specified, it will raise a timeout error if no message matching any of the patterns is received before the timeout elapses.
If there is an else
clause it will be dispatched immediately if
there is no message matching any of the patterns in the mailbox.
Within a reaction rule body, the following syntactic variables are set:
@envelope
is set to the message envelope; this is an instance of theevenelope
struct.@message
is set to the envelope payload; this can be anything.@dest
is set to the envelope destination; this is where the message was sent, a thread or a handle.@source
is set to the envelope source; this is where the message came from, a thread or a handle.@nonce
is set to the envelope nonce; a monotonically increasing nonnegative integer.@replyto
is set to the envelope reply nonce; it can be #f if this message is not a reply to a previous message, or the nonce of a previously sent message.@expiry
is set to the envelope expiry; this is atime
object, representing absolute expiration time.@reply-expected?
is set to the envelope reply-expected hint; this is #t if the source is expecting a reply to this message.
<<
(<< (pattern body ...) ...
[timeout: timeo]
[(else body ...)])
Like <-
but it destructures raw messages that can be anything, not
necessarily wrapped in an envelope. You don't normally have to use
this unless you are expecting to receive messages sent with a raw
primitive like thread-send
or send-message
and not with the send
operator(s).
There are no syntactic variables bound in the reaction context.
envelope
(defstruct envelope (message dest source nonce replyto expiry reply-expected?)
...)
Envelope structure for messages; you normally shouldn't construct those by hand and let the send operators construct it for you.
The structure is provided so that you program raw reactions with <<
,
for instance when writing a proxy actor.
The meanings and types of the struct fields are as defined as follows:
message
is the payload; this can be anything.dest
is the destination actor; this is where the message was sent, a thread or a handle.source
is the source of the message; this is where the message came from, a thread or a handle.nonce
is the source-specific message nonce; a monotonically increasing nonnegative integer.replyto
is the destination-specific reply nonce; it can be #f if this message is not a reply to a previous message, or the nonce of a previously sent message.expiry
is envelope expiry; this is atime
object, representing absolute expiration time.reply-expected?
is hint for proxies; this is #t if the source is expecting a reply to this message.
send-message
(send-message actor msg)
Sends a message to an actor, which must be a thread or a handle.
Returns #f
if the message was not sent because the actor was detected as dead.
You don't normally have to invoke this, it is invoked internally by the send operators. It is provided however in case you want to send pre-constructed envelopes, for instance when writing a proxy actor.
current-thread-nonce!
(current-thread-nonce!)
Returns the current thread's numeric nonce and post increments.
You don't normally have to use this procedure, the nonce is incremented automatically by the send operators. It is provided however in case you want to construct your own envelopes, for instance when writing a proxy actor.
actor-authorized?
(actor-authorized? actor)
Returns true if the actor (a thread or a handle) are authorized for administrative actions.
All threads within the process are implicitly authorized.
Remote actors must be authorized with the actor server using
admin-authorize
if the actor server requires authentication. This
happens by default if an administrative public key exists in the
ensemble directory. If none exists, the actor server will consider all
actors in the ensemble as authorized.
actor-error
(defstruct (actor-error <error>) ())
Structure for actor errors.
raise-actor-error
(raise-actor-error where what . irritants)
Raises an actor error.
default-reply-timeout
(default-reply-timeout)
The default reply timeout in seconds; initial value is 5s.
set-default-reply-timeout!
(set-default-reply-timeout! timeo)
Sets the default reply timeout.
Handles and References
make-handle
(handle proxy ref)
Creates a handle for sending messages to an actor through a proxy.
proxy
is the actor who will receive the messages, a thread.ref
is areference
to the actor being proxied; in general it is something the proxy can interpret; seereference
below.
handle?
(handle? obj)
Predicate for handles.
handle-proxy
(handle-proxy h)
Returns the proxy in the handle.
handle-ref
(handle-ref h)
Returns the actor reference in the handle.
reference
(defmessage reference (server id))
References for actors in the ensemble.
server
is the server identifier where the actor resides.id
is the actor identifier, generally a symbol for references by name.
reference->handle
(reference->handle ref (srv (current-actor-server)))
Creates a handle from a reference, using by default the current actor server as the proxy.
Protocols
defmessage
(defmessage id (field-name ...) struct-options ...)
Macro to define message types that can be efficiently marshalled as protocol messages.
The structure is final and transparent, and it is automatically registered in the message type registry where the unmarshaller can find it.
Note
Messages must be acyclic; if you want to send cyclic data you can use a normal struct, but be aware that such structs will be serialized/deserialized with the raw gambit serializer and carry the whole type descriptor (including methods) with them.
message?
(message? obj)
Predicate for instances of messages defined with defmessage
.
defcall-actor
(defcall-actor (proc arg ...)
expr
[error: error-msg error-irritant ...])
Macro for defining synchronous interaction entry points for actors.
The macro defines a procedure that invokes an actor with expr
and
unwraps the result. If it is !ok
the embedded value is returned. If
it is !error
an actor error is raised, using the optional error
message and irritants specified in the definition.
!ok
(defmessage !ok (value))
Message indicating a successful invocation of an actor.
!error
(defmessage !error (message))
Message indicating an error in an actor invocation. The message is a
diagnostic string, that will be included in the error raised by
defcall-actor
definitions.
with-result
(with-result expr [fail!])
Evaluates expr
and matches the result; if it is !ok
the embedded
value is returned. If it is !error
an error is raised by invoking
the fail!
(error
by default) procedure with the error message.
Actor Management Protocol
!ping
(defmessage !ping ())
Message sent to check liveness of an actor; the actor must reply with
(!ok 'OK)
if it is live.
@ping
(defrule (@ping)
((!ping) (--> (!ok 'OK))))
Reaction macro to automatically respond to !ping
messages.
You can use this in reaction context (<-
) with the gnostic ,(@ping)
syntax.
!shutdown
(defmessage !shutdown ())
Message sent to request an actor to gracefully shutdown.
@shutdown
(defrule (@shutdown exit ...)
((!shutdown)
(-->? (!ok (void)))
exit ...))
Reaction macro to automatically (conditionally) respond to a !shutdown
message.
You can use this in reaction context (<-
) with the gnostic
,(@shutdown exit-actor-loop ...)
syntax.
@unexpected
(defrule (@unexpected logf)
(unexpected
(logf "unexpected message from ~a: ~a" @source @message)
(-->? (!error "unexpected message"))))
Reaction macro to automatically log and conditionally respond to unexpected messages.
You can use this in reaction contex (<-
) with the gnostic
,(@unexpected warnf)
syntax.
Tickers
ticker
(ticker peer (period 1) (tick 'tick))
Runs in a loop sending !tick
messages to peer
every elapsed period
(in seconds).
You can spawn tickers to send heartbeat messages to an actor like this:
(spawn/name 'ticker ticker (current-thread))
ticker-after
(ticker-after peer initial-delay (period 1) (tick 'tick))
Runs ticker
after sleeping for initial-delay
(in seconds).
after
(after time peer (tick 'tick))
Sleeps for time
and sends a tick to the specified peer.
!tick
(defmessage !tick (id seqno))
This is the message sent by ticker
and related procedures to signify a temporal tick.
id
is the identifier of the tick, which is a hint for actors to demultiplex multiple tick sources.seqno
is the sequence number of the tick.
Actor Monitors
actor-monitor
(actor-monitor actor peer (send ->))
Waits for actor
to terminate by joining it and sends an
!actor-dead
message to peer
when it exits. The actor
must be a
thread.
The send
procedure is used to send the message; if you are
processing raw messages with <<
in your actor's reaction loop, you
can use send-message
instead of ->
to avoid wrapping the
notification in an envelope.
You can spawn an actor monitor to notify you of thread exits like this:
(spawn/name 'actor-monitor actor-monitor actor (current-thread))
!actor-dead
(defmessage !actor-dead (thread))
Message sent by actor monitors to notify or a monitored thread exit.
The Actor Server
Server Addresses
Actor server addresses can be:
- UNIX domain addresses.
- TCP addresses.
- TLS addresses.
Unix Addresses
A UNIX domain address is denoted like this:
[unix: hostname path]
hostname
is the name of the host where the server is
accessible and path
is the socket path; they are both strings.
Actor servers will never try to connect to UNIX addresses in different hosts.
TCP Addresses
A TCP address is denoted like this:
[tcp: inet-addr]
inet-addr
is an Internet address; normally a pair of a host address
and a port or a string encoded Internet address of the form
"host:port"
.
See the :std/net/address module for more details.
Do not use bare TCP addresses in production, as you will vulnerable to Man in the Middle attacks. Gerbil actors only support them for development and debugging purposes; you should use TLS in production.
TLS Addresses
A TLS address is denoted like this:
[tls: inet-addr]
inet-addr
is an Internet address; normally a pair of a host address
and a port or a string encoded Internet address of the form
"host:port"
.
See the :std/net/address module for more details.
current-actor-server
(current-actor-server)
Parameter denoting the current actor server.
This parameter is set automatically by start-actor-server!
and
call-with-ensemble-server
; you don't normally have to set it
manually.
start-actor-server!
(start-actor-server! identifier: (id (make-random-identifier))
tls-context: (tls-context (get-actor-tls-context id))
cookie: (cookie (get-actor-server-cookie))
admin: (admin (get-admin-pubkey))
auth: (auth #f)
addresses: (addrs [])
ensemble: (known-servers (default-known-servers)))
Starts an actor server, sets the current-actor-server
parameter and
returns the main server thread.
identifier
is the server identifier; if you don't specify one, a random server identifier will be generated.tls-context
is the server's TLS context, which can be omitted if this is a development server. By default it is looked up inGERBIL_PATH/ensemble/server/<server-id>/tls
.cookie
is the ensemble cookie; normally resides in$GERBIL_PATH/ensemble/cookie
. Note that the administrator has to explicitly create a cookie for the ensemble, it is not automatically created.admin
is the (optional) administrative public key; normally resindes in$GERBIL_PATH/ensemble/admin.pub
.auth
: a hash table mapping server ids to capabilities; these are preauthorized server capabilities.addresses
is the list of addresses the server should listen; by default it is empty, making this a transient actor server.ensemble
specifies statically known hosts; it is a hash table mapping server identifiers to lists of addresses. The default known servers only contain the registry with the default registry address.
stop-actor-server!
(stop-actor-server! (srv (current-actor-server)))
Stops and joins an actor server.
actor-server-identifier
(actor-server-identifier (srv (current-actor-server)))
Returns an actor server's identifier.
register-actor!
(register-actor! name (srv (current-actor-server)))
Registers the current thread in an actor server as an actor with the name name
.
connect-to-server!
(connect-to-server! id (addrs #f) (srv (current-actor-server)))
Instructs an actor server to connect to another server.
id
is the identifier of the target serveraddrs
is an optional list of addresses; if none is specified and the server is unknown, its addresses will be resolved through the ensemble registry.
list-actors
(list-actors (srv (current-actor-server)))
Lists the actors registered with an actor server. Returns a list of references.
list-connections
(list-connections (srv (current-actor-server)))
Lists the current connections of an actor server. Returns an associative list, with the server identifier at the car and the list of addresses connected at the cdr.
default-known-servers
(default-known-servers)
Returns a hash table with the default known servers. By default it only contains the registry reachable in its default address.
set-default-known-servers!
(set-default-known-servers! servers)
Sets the default known servers; servers
must be a hash table mapping
server identifiers to lists of addresses.
default-registry-addresses
(default-registry-addresses)
Returns the default registry addresses, as a list of addresses. By
default the registry is reachable at /tmp/ensemble/registry
in the
current host.
set-default-registry-addresses!
(set-default-registry-addresses! addrs)
Sets the default registry addresses.
server-address-cache-ttl
(server-address-cache-ttl)
Returns the actor's server address cache TTL in seconds (a real number); by default this is 5 minutes.
set-server-address-cache-ttl!
(set-server-address-cache-ttl! ttl)
Sets the actor server's address cache TTL (in seconds, a real number).
Ensemble Servers
call-with-ensemble-server
(call-with-ensemble-server server-id thunk
log-level: (log-level 'INFO)
log-file: (log-file #f)
listen: (listen-addrs [])
announce: (public-addrs #f)
registry: (registry-addrs #f)
roles: (roles [])
tls-context: (tls-context (get-actor-tls-context server-id))
cookie: (cookie (get-actor-server-cookie))
admin: (admin (get-admin-pubkey))
auth: (auth #f))
This is the programmatic equivalent of gxensemble run
; first it
starts the logger with the appropriate options, and then it starts a
new actor server with identifier server-id
, starts the loader
service, adds the server to the ensemble, and then invokes thunk.
When the thunk exits, it shuts down the actor server and removes the
server-id from the ensemble.
Options:
log-level
: logging level to use; INFO by defaultlog-file
: log file for the logger; if it is "-" then the canonical server log is used; this file is at$GERBIL_PATH/ensemble/server/<server-id>/log
.listen
: a list of addresses for the actor server to listen to, in addition to the default unix address.announce
: an optional list of addresses to announce to the registry, in addition to the default unix address. If it is not specified, then the listen addresses are announced.registry
: an optional list of registry addresses. If it is not specified, then the default registry address is used.roles
: a list of roles the server fullfills in the registry.tls-context
is the server's TLS context, which can be omitted if this is a development server. By default it is looked up inGERBIL_PATH/ensemble/server/<server-id>/tls
.cookie
: the cookie to use; by default it uses the ensemble cooke in$GERBIL_PATH/ensemble/cookie
.admin
: the administrative public key, if any; by default it uses the public key in$GERBIL_PATH/ensemble/admin.pub
if it exists.auth
: a hash table mapping server ids to capabilities; these are preauthorized server capabilities.
ensemble-base-path
(ensemble-base-path)
Returns the base directory for the ensemble; this is $GERBIL_PATH/ensemble
.
ensemble-server-path
(ensemble-server-path server-id)
Returns the directory for server specific data; this is $GERBIL_PATH/ensemble/server/<server-id>
.
Ensemble Control
This is programmatic functionality for operations normally performed
using the gxensemble
tool; see the ensemble tutorial
for more information.
stop-actor!
(stop-actor! ref (srv (current-actor-server)))
Stops an actor referred by ref
by sending it a !shutdown
request.
remote-stop-server!
(remote-stop-server! srv-id (srv (current-actor-server)))
Stops the remote server with identifier srv-id
.
remote-list-actors
(remote-list-actors srv-id (srv (current-actor-server)))
Lists registered actors at the remote server with identifier srv-id
.
remote-connect-to-server!
(remote-connect-to-server! from-id to-id (addrs #f) (srv (current-actor-server)))
Asks the remote server with identifier from-id
to connect to the
server with identifier to-id
. If the optional addresses addrs
are
not specified, then the to-id
server will be looked up in the registry.
remote-list-connections
(remote-list-connections srv-id (srv (current-actor-server)))
Lists the connections of a remote server with identifier srv-id
.
remote-load-library-module
(remote-load-library-module srv-id mod (srv (current-actor-server)))
Asks the remote server with identifier srv-id
to load the library module mod
.
remote-load-code
(remote-load-code srv-id object-file-path (srv (current-actor-server)))
Asks the remote server with identifier srv-id
to load a code object file.
remote-eval
(remote-eval srv-id expr (srv (current-actor-server)))
Evaluates expr
in the remote server with identifier srv-id
.
ping-server
(ping-server srv-id (srv (current-actor-server)))
Pings the remote server with identifier srv-id
.
ping-actor
(ping-actor ref (srv (current-actor-server)))
Pings the actor referred by ref
.
ensemble-add-server!
(ensemble-add-server! id addrs roles (srv (current-actor-server)))
Adds a server to the ensemble registry:
id
is the server identifieraddrs
is the list of actor server addresses.roles
is a list of roles for the server; a list of symbols.
ensemble-remove-server!
(ensemble-remove-server! id (srv (current-actor-server)))
Removes the server with identifier id
from the ensemble registry.
ensemble-list-servers
(ensemble-list-servers (srv (current-actor-server)))
Lists the current ensemble servers.
ensemble-lookup-server
(ensemble-lookup-server id (srv (current-actor-server)))
Looks up a server's addresses in the registry.
ensemble-lookup-servers/role
(ensemble-lookup-servers/role role (srv (current-actor-server)))
Looks up servers (and their addresses) that fulfill role
in the
ensemble.
admin-authorize
(admin-authorize privk srv-id authorized-server-id (srv (current-actor-server))
capabilities: (cap '(admin)))
Authorizes the capabilities specified by cap
with administrative
privileges for authorized-server-id
in the remote server srv-id
,
using the private key privk
. cap
is a list of symbols denoting
the capabilities of the authorized server; the admin
capability
implies all other capabilities.
admin-retract
(admin-retract srv-id authorized-server-id (srv (current-actor-server)))
Retracts the capabilities previously confered to
authorized-server-id
within the context of srv-id
. Note that the
in-process actor server must have admin
capabilities.
get-admin-pubkey
(get-admin-pubkey (path (default-admin-pubkey-path)))
Loads the administrative public key from the specified path
, if it exists
get-admin-privkey
(get-admin-privkey passphrase (path (default-admin-privkey-path)))
Loads and decrypts the administrative private key from the specified path
, if it exists
default-admin-pubkey-path
(default-admin-pubkey-path)
The default path for the ensemble admin public key; defaults to $GERBIL_PATH/ensemble/admin.pub
.
default-admin-privkey-path
(default-admin-privkey-path)
The default path for the encrypted ensemble admin private key; defaults to $GERBIL_PATH/ensemble/admin.priv
.
Actor TLS related procedures
The following procedures offer programmatic functionality used by
actor servers and the gxensemble
for managing TLS infrastructure.
ensemble-tls-base-path
(ensemble-tls-base-path)
Returns the base TLS directory for the ensemble; this is $GERBIL_PATH/ensemble/tls
.
ensemble-tls-server-path
(ensemble-tls-server-path server-id)
Returns the directory for server specific data; this is $GERBIL_PATH/ensemble/server/<server-id>
/tls.
ensemble-tls-cafile
(ensemble-tls-cafile)
Returns the base TLS CA file for the ensemble; this is $GERBIL_PATH/ensemble/tls/ca.pem
.
get-actor-tls-context
(get-actor-tls-context server-id) -> tls-context or #f
Create an appropriate TLS context for the actor server with id server-id
.
It loads the server's private key from (ensemble-tls-server-path)
.
actor-tls-certificate-id
(actor-tls-certificate-id x509)
Returns the actor server id for an x509 certificate.
actor-tls-certificate-cap
(actor-tls-certificate-cap x509)
Returns the actor server capabilities embedded in an x509 certificate.
actor-tls-host
(actor-tls-host server-id)
Returns the host name for an actor server as it is expected to appear in its certificate, for certificate verification purposes.
generate-actor-tls-root-ca!
(generate-actor-tls-root-ca! root-ca-passphrase
domain: (domain "ensemble.local")
country-name: (country-name "UN")
organization-name: (organization-name "Mighty Gerbils")
common-name: (common-name (string-append organization-name " Root CA")))
Generates a new root CA.
generate-actor-tls-sub-ca!
(generate-actor-tls-sub-ca! root-ca-passphrase
sub-ca-passphrase
country-name: (country-name "UN")
organization-name: (organization-name "Mighty Gerbils")
common-name: (common-name (string-append organization-name " Subordinate CA")))
Generates a new subordinate CA.
generate-actor-tls-cafiles!
(generate-actor-tls-cafiles! force: (force? #f))
Generates the CA files for the actor CA.
generate-actor-tls-cert!
(generate-actor-tls-cert! sub-ca-passphrase
server-id: server-id
capabilities: (capabilities [])
country-name: (country-name "UN")
organization-name: (organization-name "Mighty Gerbils")
location: (location "Internet"))
Issues a new actor TLS certificate.