Actor API
Installation
YAActL.YAActL — ModuleYAActLYet Another Actor Library (in Julia VERSION ≥ v"1.3").
The current stable, registered version is installed with
pkg> add YAActLThe 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: processpidto create the actor on, this can also be given withlp,bhv: a function implementing the actor's behavior,args1...: first arguments tobhv(without possiblemsgarguments),kwargs...: keyword arguments tobhv.
Returns
- a
Linkto 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
1Links
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)
2Actors 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}:Messagetype,from::Link: local or remote link of sender. Iffromis provided, only messages with afromfield can be matched.timeout::Real=5.0: maximum waiting time in seconds.- If
timeout==0,lkis scanned only for existing messages. - Set
timeout=Infif 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 — TypeDispatchDepending on its Dispatch mode an actor composes the arguments to the behavior function:
full: from theBecomeargs...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::Symbolif registered), bhv: function implementing the new behavior,args1...: first arguments tobhv(without a possiblemsgargument),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::Linkorname::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
20If 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::Symbolif registered), from::Link: sender link; Iffromis 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::Linkorname::Symbolof the actor,from::Link: the link aResponseshould be sent to. Iffromis 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=Infif 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::Symbolcan 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)
11Actor Registry
Actors can be registered with Symbols 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.