Actor API

Installation

YAActL.YAActLModule
YAActL

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")
source
julia> using YAActL

julia> YAActL.version
v"0.2.1"

The following functions provide a user interface to actors:

Start

YAActL.ActorFunction
Actor([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: process pid to create the actor on, this can also be given with lp,
  • bhv: a function implementing the actor's behavior,
  • args1...: first arguments to bhv (without possible msg arguments),
  • kwargs...: keyword arguments to bhv.

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
source

An actor is only represented by its link which it returns upon creation:

YAActL.LinkType
Link{C}(chn::C, pid::Int, type::Symbol)

A mailbox for communicating with actors.

Fields/Parameters

  • chn::C:
  • pid::Int:
  • type::Symbol:
source
YAActL.islocalFunction
islocal(lk::Link)
islocal(name::Symbol)

Returns true if the actor lk or name has the same pid as the caller, else false.

source

Actors correspond with other actors over links. There is a default link for users to communicate with actors.

For setting up links explicitly we have the following functions.

YAActL.LinkParamsType
LinkParams(pid=myid(), size=32; taskref=nothing, spawn=false)

Parameters for setting up an Actor.

  • pid::Int: process identification,
  • size::Int: channel buffer size, must be size ≥ 10,
  • taskref::Union{Nothing, Ref{Task}}: If you need a reference to the created task, pass a Ref{Task} object via the keyword argument taskref.
  • spawn::Bool: If spawn = true, the Task created may be scheduled on another thread in parallel, equivalent to creating a task via Threads.@spawn.
source
YAActL.parallelFunction
parallel(size=32; taskref=nothing)

Return LinkParams with spawn=true.

Example

julia> using YAActL, .Threads

julia> myactor = Actor(parallel(), threadid);

julia> call!(myactor)
2
source

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.ResponseType
Response(y, from::Link=self())

A Message representing a response to requests.

Fields

  • y: response content,
  • from::Link: sender link.
source

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!Function
send!(lk::Link, m::Message)
send!(name::Symbol, m::Message)

Send a message m to an actor lk or name (if registered).

source
YAActL.receive!Function
receive!(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. If from is provided, only messages with a from 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.

Returns

  • received message or Timeout().
source
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.DispatchType
Dispatch

Depending on its Dispatch mode an actor composes the arguments to the behavior function:

  • full: from the Become args... and the msg.x.... This is the default dispatch mode.
  • state: from the actor state and the msg.x.... In this case the actor updates its state with the result of the behavior function. The result is saved in a Tuple.
source
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!Function
become!(lk::Link, bhv::Function, args1...; kwargs...)
become!(name::Symbol, ....)

Cause an actor to change behavior.

Arguments

  • actor lk::Link (or name::Symbol if registered),
  • bhv: function implementing the new behavior,
  • args1...: first arguments to bhv (without a possible msg argument),
  • kwargs...: keyword arguments to bhv.
source
YAActL.cast!Function
cast!(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!.

source
YAActL.exit!Function
exit!(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.

This behavior is not yet fully implemented!

It is needed for supervision.

source
YAActL.set!Function
set!(lk::Link, dsp::Dispatch)
set!(name::Symbol, dsp::Dispatch)

Set the lk actor's Dispatch to dsp.

source
YAActL.update!Function
update!(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 or name::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
source

If a behavior function wants to control its own actor, it can use the following functions:

YAActL.becomeFunction
become(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 to bhv (without msg),
  • kwargs...: keyword arguments to bhv.
source

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!Function
request!(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

  • lk::Link: actor link, or name::Symbol (if registered),
  • msg::Message: a message,
  • Msg::Type{<:Message}: a message type,
  • args...: optional arguments to Msg,
  • full: if true return the full Response message.
  • timeout::Real=5.0: timeout in seconds after which a Timeout is returned,
  • kwargs...: full or timeout.
source

The following functions support both messaging styles:

  1. Send a message with an explicit from-link to an actor and it will respond to this link. Then you can asynchronously receive! the response from it.
  2. Send a message with an implicit link to an actor, block, wait for the response and return it.
YAActL.call!Function
call!(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 (or name::Symbol if registered),
  • from::Link: sender link; If from is omitted, call! blocks and returns the result.
  • args2...: second arguments to the actor.
  • timeout::Real=5.0: timeout in seconds.
source
YAActL.exec!Function
exec!(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 or name::Symbol of the actor,
  • from::Link: the link a Response should be sent to. If from is ommitted, exec! blocks, waits and returns the result. In that case there is a timeout.
  • f::Function, args...; kwargs... or
  • fu::Func: function arguments,
  • timeout::Real=5.0: timeout in seconds. Set timeout=Inf if you don't want a timeout.
source
YAActL.query!Function
query!(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
source

Actor Registry

Actors can be registered with Symbols to a registry. API functions on actors can then be called with their registered names.

YAActL.registerFunction
register(name::Symbol, lk::Link)

Register the actor lk with name. Returns true if the registration succeeds, false if name is already in use.

source
YAActL.whereisFunction
whereis(name::Symbol)

Find out whether name is registered. Return the actor link lk or missing if not found.

source

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!Function
init!(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.

This behavior is not yet implemented!

It is needed for supervision.

source
YAActL.term!Function
term!(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.

This behavior is not yet implemented!

It is needed for supervision.

source
  • 1bidirectional messages are Call, Get, Exec and Query.