Skip to content

Commit

Permalink
asynchronous calls to guard actors
Browse files Browse the repository at this point in the history
  • Loading branch information
pbayer committed Dec 18, 2020
1 parent 11afe31 commit 65670bf
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 17 deletions.
8 changes: 6 additions & 2 deletions docs/src/guards.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,13 @@ CurrentModule = Guards
As for now a `:guard` actor

- is a "single receptionist" in Agha's sense or it
- is similar to an *agent* in Elixir.
- is similar to an *agent* in Elixir.

The guarded variable is protected by message passing and the `:guard` actor being the only entity allowed to directly modify it. In order to modify a guarded variable we must send a modifier function with its arguments to the `:guard` actor. It then responds with a deep copy of the result or of the variable. So e.g. threads can safely do iterations on those.
The guarded variable is protected by message passing and the `:guard` actor being the only entity allowed to directly modify it. In order to modify a guarded variable we must [`call`](@ref) the `:guard` actor with a modifier function and its arguments. The actor then applies that to the guarded variable and responds with a deep copy of the result.

If we `call` the `:guard` actor without further arguments, it responds with a deep copy of the guarded variable. So e.g. threads can safely do iterations on the copy.

The [`@grd`](@ref) macro is syntactical sugar for `call` and lets you work with the guard actor as if it were the guarded variable itself.

## Interfaces

Expand Down
2 changes: 1 addition & 1 deletion docs/src/intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ In order to secure a mutable variable from concurrent access by multiple threads

Then it can be accessed only by message passing via [`cast`](@ref) and [`call`](@ref) over that link. Calls to it return deep copies. You can still modify a guarded variable by sending the `:guard` actor a modifier function and arguments to it.

The [`@grd`](@ref) macro expands a call to the link into a `call` request.
The [`@grd`](@ref) macro expands a call to the link into a synchronous `call` request.

## Example

Expand Down
39 changes: 30 additions & 9 deletions src/api.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,38 @@
#

"""
call(gd::Guard, f, args...)
```
call(gd::Guard, [lk::Link,] f::Function, args...)
```
Call a guard actor `gd` to execute `f(var, args...)` on
its guarded variable var and to send respond with a
its guarded variable `var` and to respond with a
deep copy of the result.
# Arguments
- `gd::Guard`: link to the `:guard` actor,
- `f`: callable object taking the guarded variable `var`
- `lk::Link`: if a link `lk` is provided send the response
to that, else do a synchronous `request`,
- `f`: function taking the guarded variable `var`
as first argument,
- `args...`: further arguments to `f`.
"""
Actors.call(gd::Guard, f, args...) = call(gd.link, f, args...)
Actors.call(gd::Guard, lk::Link, f::F, args...) where F<:Function = call(gd.link, lk, f, args...)
Actors.call(gd::Guard, f::F, args...) where F<:Function = call(gd.link, f, args...)

"""
```
call(gd::Guard [, lk::Link])
```
Call a guard actor `gd` to respond with a deep copy of
the guarded variable `var`.
# Arguments
# Arguments
- `gd::Guard`: link to the `:guard` actor,
- `lk::Link`: if a link `lk` is provided send the response
to that, else do a synchronous `request`,
"""
Actors.call(gd::Guard, lk::Link) = call(gd.link, lk)
Actors.call(gd::Guard) = call(gd.link)

"""
Expand Down Expand Up @@ -48,15 +67,17 @@ function Actors.update!(gd::Guard, var)
end

"""
```
@grd f(gd, args...)
@grd gd
```
@grd f(gd, args...)
Execute a function `f` on a guarded variable `var`
represented by an actor link `gd` and return a deep copy
of the result or return a deep copy of `var`. This is
a wrapper to [`call`](@ref).
@grd gd
Return a deep copy of the guarded variable.
# Parameters
- `gd::Guard`: a link to the `:guard` actor,
- `f`: callable object taking the guarded variable `var`
Expand Down
16 changes: 11 additions & 5 deletions test/test_basics.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,25 @@
#

using Guards, Test
import Actors: newLink

me = newLink()
gd = guard([1,2,3])
@test gd isa Guards.Guard{Array{Int64,1}}
@test call(gd) == [1,2,3]
call(gd, me)
@test receive(me).y == [1,2,3]
@test push!(call(gd), 4) == [1,2,3,4]
@test call(gd) == [1,2,3]
@test call(gd, push!, 4) == [1,2,3,4]
@test call(gd) == [1,2,3,4]
cast(gd, push!, 5)
@test (@grd gd) == [1,2,3,4,5]
@test (@grd pop!(gd)) == 5
@test pop!(update!(gd, [5,6,7,8])) == 8
@test (@grd gd) == [5,6,7,8]
call(gd, me, push!, 5)
@test receive(me).y == [1,2,3,4,5]
cast(gd, push!, 6)
@test (@grd gd) == [1,2,3,4,5,6]
@test (@grd pop!(gd)) == 6
@test pop!(update!(gd, [6,7,8])) == 8
@test (@grd gd) == [6,7,8]

@test (@grd 1) == "@grd: not a symbol or a call!"
@test_throws AssertionError update!(gd, [10.0, 11.0])

0 comments on commit 65670bf

Please sign in to comment.