Actor API
Installation
YAActL.YAActL
— ModuleYAActL
Yet Another Actor Library (in Julia VERSION ≥ v"1.3"
).
The current stable, registered version is installed with
pkg> add YAActL
The development version is installed with:
pkg> add("https://github.com/pbayer/YAActL.jl")
YAActL.version
— ConstantGives the package version.
julia> using YAActL
julia> YAActL.version
v"0.2.1"
The following functions provide a user interface to actors:
Start
YAActL.Actor
— FunctionActor([lp::LinkParams], bhv::Function, args1...; kwargs...)
Actor(pid::Int, bhv::Function, args1...; kwargs...)
Create a new actor. Start a task listening to messages msg
sent over the returned self and executing bhv(args1..., msg; kwargs...)
for each message. The actor stops if sent Stop()
.
Arguments
[lp::LinkParams]
: parameters for creating the actor,pid::Int
: processpid
to create the actor on, this can also be given withlp
,bhv
: a function implementing the actor's behavior,args1...
: first arguments tobhv
(without possiblemsg
arguments),kwargs...
: keyword arguments tobhv
.
Returns
- a
Link
to a created actor.
see also: LinkParams
Example
julia> using YAActL, .Threads
julia> act1 = Actor(threadid) # start an actor who gives its threadid
Channel{Message}(sz_max:32,sz_curr:0)
julia> call!(act1) # call it
1
Links
An actor is only represented by its link which it returns upon creation:
YAActL.Link
— TypeLink{C}(chn::C, pid::Int, type::Symbol)
A mailbox for communicating with actors.
Fields/Parameters
chn::C
:pid::Int
:type::Symbol
:
YAActL.islocal
— Functionislocal(lk::Link)
islocal(name::Symbol)
Returns true
if the actor lk
or name
has the same pid as the caller, else false.
Actors correspond with other actors over links. There is a default link for users to communicate with actors.
YAActL.USR
— ConstantUser remote channel for interacting with actors.
For setting up links explicitly we have the following functions.
YAActL.LinkParams
— TypeLinkParams(pid=myid(), size=32; taskref=nothing, spawn=false)
Parameters for setting up an Actor
.
pid::Int
: process identification,size::Int
: channel buffer size, must besize ≥ 10
,taskref::Union{Nothing, Ref{Task}}
: If you need a reference to the created task, pass aRef{Task}
object via the keyword argumenttaskref
.spawn::Bool
: If spawn = true, the Task created may be scheduled on another thread in parallel, equivalent to creating a task viaThreads.@spawn
.
YAActL.parallel
— Functionparallel(size=32; taskref=nothing)
Return LinkParams
with spawn=true
.
Example
julia> using YAActL, .Threads
julia> myactor = Actor(parallel(), threadid);
julia> call!(myactor)
2
Actors can send!
and receive!
messages over links.
Messages
Messages in YAActL
have a common abstract type. Only a few predefined messages are exported:
YAActL.Message
— TypeAbstract type for messages to actors.
YAActL.Response
— TypeResponse(y, from::Link=self())
A Message
representing a response to requests.
Fields
y
: response content,from::Link
: sender link.
YAActL.Request
— TypeRequest(x, from::Link)
A generic Message
for user requests.
YAActL.Timeout
— TypeTimeout()
A return value to signal that a timeout has occurred.
Actors operate with internal messages. Further messages can be implemented by a user. If an actor receives a Request
or a user implemented message, it passes it as last argument to the behavior function.
Send and Receive
Messages are sent and received using the following basic functions:
YAActL.send!
— Functionsend!(lk::Link, m::Message)
send!(name::Symbol, m::Message)
Send a message m
to an actor lk
or name
(if registered).
YAActL.receive!
— Functionreceive!(lk; timeout=5.0)
receive!(lk, from; timeout=5.0)
receive!(lk, Msg; timeout=5.0)
receive!(lk, Msg, from; timeout=5.0)
Receive a message over a link lk
.
If Msg
or from
are provided, receive!
returns only a matching message. Other messages in lk
are restored to it in their previous order.
Parameters
lk::Link
: local or remote link over which the message is received,Msg::Type{<:Message}
:Message
type,from::Link
: local or remote link of sender. Iffrom
is provided, only messages with afrom
field can be matched.timeout::Real=5.0
: maximum waiting time in seconds.- If
timeout==0
,lk
is scanned only for existing messages. - Set
timeout=Inf
if you don't want to timeout.
- If
Returns
- received message or
Timeout()
.
julia> myactor = Actor(parallel(), threadid);
julia> send!(myactor, YAActL.Call(USR)); # the same as call!(myactor, USR)
julia> receive!(USR).y # receive the result
2
julia> receive!(USR) # gives after some seconds ...
Timeout()
Dispatch mode
Actors have a dispatch mode:
YAActL.Dispatch
— TypeDispatch
Depending on its Dispatch
mode an actor composes the arguments to the behavior function:
full
: from theBecome
args...
and themsg.x...
. This is the default dispatch mode.state
: from the actor state and themsg.x...
. In this case the actor updates its state with the result of the behavior function. The result is saved in aTuple
.
julia> set!(myactor, state) # set state dispatch
YAActL.Update{Dispatch}(:dsp, state)
julia> set!(myactor, full) # set full dispatch
YAActL.Update{Dispatch}(:dsp, full)
Actor Control
The following functions control actor behavior and state by sending implicit messages. Actors don't send a Response
to those.
YAActL.become!
— Functionbecome!(lk::Link, bhv::Function, args1...; kwargs...)
become!(name::Symbol, ....)
Cause an actor to change behavior.
Arguments
- actor
lk::Link
(orname::Symbol
if registered), bhv
: function implementing the new behavior,args1...
: first arguments tobhv
(without a possiblemsg
argument),kwargs...
: keyword arguments tobhv
.
YAActL.cast!
— Functioncast!(lk::Link, args2...)
cast!(name::Symbol, args2...)
Cast a message to the actor lk
(or name
if registered) to execute its behavior with args2...
without sending a response.
Note: you can prompt the returned value with query!
.
YAActL.exit!
— Functionexit!(lk::Link, code=0)
exit!(name::Symbol, code=0)
Tell an actor lk
(or name
if registered) to exit. If it has a term
function, it calls it with code
as last argument.
It is needed for supervision.
YAActL.set!
— Functionset!(lk::Link, dsp::Dispatch)
set!(name::Symbol, dsp::Dispatch)
Set the lk
actor's Dispatch
to dsp
.
YAActL.update!
— Functionupdate!(lk::Link, x; s::Symbol=:sta)
update!(lk::Link, arg::Args)
update!(name::Symbol, ....)
Update an actor's internal state s
with args...
.
Arguments
- an actor
lk::Link
orname::Symbol
(if registered), x
: value/variable to update the choosen state with,arg::Args
: arguments to update,s::Symbol
: can be one of:sta
,:dsp
,:arg
,:self
,:name
.
Note: If you want to update the stored arguments to the behavior function with s=:arg
, you must pass an Args
to arg
. If Args
has keyword arguments, they are merged with existing keyword arguments to the behavior function.
Example
julia> update!(fact, 5) # note that fact is in state dispatch
YAActL.Update{Int64}(:sta, 5)
julia> call!(fact, 5) # call it with 5
10
julia> update!(fact, Args(0, u=5)); # update arguments
julia> call!(fact, 5) # add the last result, 5 and u=5
20
If a behavior function wants to control its own actor, it can use the following functions:
YAActL.become
— Functionbecome(bhv::Function, args...; kwargs...)
Cause your actor to take on a new behavior. This can only be called from inside an actor/behavior.
Arguments
bhv::Function
: function implementing the new behavior,args...
: arguments tobhv
(withoutmsg
),kwargs...
: keyword arguments tobhv
.
YAActL.self
— Functionself()
Get the Link
of your actor.
YAActL.stop
— Functionstop(code=0)
Cause your actor to exit with code
.
Bidirectional Messaging
Some messages to actors cause them to send a Response
[1]. The exchange of messages may be carried out asynchronously, or may use a synchronous "rendezvous" style in which the sender blocks until the message is received.
YAActL
has a primitive for synchronous communication:
YAActL.request!
— Functionrequest!(lk::Link, msg::Message; full=false, timeout::Real=5.0)
request!(lk::Link, Msg::Type{<:Message}, args...; kwargs...)
request!(name::Symbol, args...; kwargs...)
Send a message to an actor, block, receive and return the result.
Arguments
The following functions support both messaging styles:
- Send a message with an explicit
from
-link to an actor and it will respond to this link. Then you can asynchronouslyreceive!
the response from it. - Send a message with an implicit link to an actor, block, wait for the response and return it.
YAActL.call!
— Functioncall!(lk::Link, from::Link, args2...)
call!(lk::Link, args2...; timeout::Real=5.0)
call!(name::Symbol, ....)
Call an actor to execute its behavior and to send a Response
with the result.
Arguments
- actor
lk::Link
(orname::Symbol
if registered), from::Link
: sender link; Iffrom
is omitted,call!
blocks and returns the result.args2...
: second arguments to the actor.timeout::Real=5.0
: timeout in seconds.
YAActL.exec!
— Functionexec!(lk::Link, from::Link, f::Function, args...; kwargs...)
exec!(lk::Link, from::Link, fu::Func)
exec!(lk::Link, fu::Func; timeout::Real=5.0)
exec!(name::Symbol, ....)
Ask an actor lk
(or name
if registered) to execute an arbitrary function and to send the returned value as Response
.
Arguments
lk::Link
orname::Symbol
of the actor,from::Link
: the link aResponse
should be sent to. Iffrom
is ommitted,exec!
blocks, waits and returns the result. In that case there is atimeout
.f::Function, args...; kwargs...
orfu::Func
: function arguments,timeout::Real=5.0
: timeout in seconds. Settimeout=Inf
if you don't want a timeout.
YAActL.query!
— Functionquery!(lk::Link, from::Link, s::Symbol)
query!(lk::Link, s::Symbol; timeout::Real=5.0)
query!(name::Symbol, ....)
Ask the lk
actor to send a Response
message to from
with an internal state variable s
.
If from
is omitted, query!
blocks and returns the response. In that case there is a timeout
.
s::Symbol
can be one of:sta
,:res
,:bhv
,:dsp
.
Examples
julia> f(x, y; u=0, v=0) = x+y+u+v # implement a behavior
f (generic function with 1 method)
julia> fact = Actor(f, 1) # start an actor with it
Channel{Message}(sz_max:32,sz_curr:0)
julia> cast!(fact, 1) # cast a second parameter to it
YAActL.Cast{Tuple{Int64}}((1,))
julia> query!(fact, :res) # query the result
2
julia> query!(fact, :bhv) # query the behavior
f (generic function with 1 method)
julia> set!(fact, state) # set dispatch mode
YAActL.Update{Dispatch}(:dsp, state)
julia> query!(fact, :dsp) # query the dispatch mode
state::Dispatch = 1
julia> update!(fact, 10) # update the state
YAActL.Update{Int64}(:sta, 10)
julia> query!(fact) # query the state variable
10
julia> call!(fact, 1)
11
Actor Registry
Actors can be registered with Symbol
s to a registry. API functions on actors can then be called with their registered names.
YAActL.register
— Functionregister(name::Symbol, lk::Link)
Register the actor lk
with name
. Returns true
if the registration succeeds, false
if name
is already in use.
YAActL.unregister
— Functionunregister(name::Symbol)
Remove any registrations associated with name
.
YAActL.whereis
— Functionwhereis(name::Symbol)
Find out whether name is registered. Return the actor link lk
or missing
if not found.
YAActL.registered
— Functionregistered()
Return an Array of all registered actors in the system.
The registry works transparently over distributed worker processes such that local links are transformed to remote links when shared between workers.
Actor Supervision
This is not yet implemented.
YAActL.init!
— Functioninit!(lk::Link, f::Function, args...; kwargs...)
init!(name::Symbol, ....)
Tell an actor lk
to save the function f
with the given arguments as an init
function, to execute it and to save the returned value as state sta
variable.
The init
function will be called at actor restart.
It is needed for supervision.
YAActL.term!
— Functionterm!(lk::Link, f::Function, args1...; kwargs...)
term!(name::Symbol, ....)
Tell an actor lk
to execute a function f
with the given arguments when it terminates. f
must accept a code=0
as last argument. This is added by the actor to args1...
when it exit!
s.
It is needed for supervision.