Skip to content

Commit

Permalink
finalize atomic macro names
Browse files Browse the repository at this point in the history
  • Loading branch information
vtjnash committed May 28, 2021
1 parent 00e9ec6 commit 1ebe4a7
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 129 deletions.
5 changes: 2 additions & 3 deletions base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1019,9 +1019,8 @@ export

@assert,
@atomic,
@atomic!,
@atomic_swap!,
@atomic_replace!,
@atomicswap,
@atomicreplace,
@__dot__,
@enum,
@label,
Expand Down
154 changes: 64 additions & 90 deletions base/expr.jl
Original file line number Diff line number Diff line change
Expand Up @@ -453,43 +453,10 @@ end
Mark `var` or `ex` as being performed atomically, if `ex` is a supported expression.
See [atomics](#man-atomics) in the manual for more details.
```jldoctest
julia> mutable struct Atomic{T}; @atomic x::T; end
julia> a = Atomic(1)
Atomic{Int64}(1)
julia> @atomic a.x # fetch field x of a, with sequential consistency
1
```
"""
macro atomic(ex)
if !isa(ex, Symbol) && !is_expr(ex, :(::))
return make_atomic(QuoteNode(:sequentially_consistent), ex)
end
return esc(Expr(:atomic, ex))
end
macro atomic(order, ex)
order isa QuoteNode || (order = esc(order))
return make_atomic(order, ex)
end
function make_atomic(order, ex)
@nospecialize
if isexpr(ex, :., 2)
l, r = esc(ex.args[1]), esc(ex.args[2])
return :(getproperty($l, $r, $order))
end
error("could not parse @atomic expression $ex")
end


"""
@atomic! a.b.x = new
@atomic! a.b.x += addend
@atomic! :acquire_release a.b.x = new
@atomic! :acquire_release a.b.x += addend
@atomic a.b.x = new
@atomic a.b.x += addend
@atomic :acquire_release a.b.x = new
@atomic :acquire_release a.b.x += addend
Perform the store operation expressed on the right atomically and return the
new value.
Expand All @@ -498,18 +465,19 @@ With `=`, this operation translates to a `setproperty!(a.b, :x, new)` call.
With any operator also, this operation translates to a `modifyproperty!(a.b,
:x, +, addend)[2]` call.
@atomic! a.b.x max arg2
@atomic! a.b.x + arg2
@atomic! max(a.b.x, arg2)
@atomic! :acquire_release max(a.b.x, arg2)
@atomic! :acquire_release a.b.x + arg2
@atomic! :acquire_release a.b.x max arg2
@atomic a.b.x max arg2
@atomic a.b.x + arg2
@atomic max(a.b.x, arg2)
@atomic :acquire_release max(a.b.x, arg2)
@atomic :acquire_release a.b.x + arg2
@atomic :acquire_release a.b.x max arg2
Perform the binary operation expressed on the right atomically. Store the
result into the field in the first argument and return the values `(old, new)`.
This operation translates to a `modifyproperty!(a.b, :x, func, arg2)` call.
See [atomics](#man-atomics) in the manual for more details.
```jldoctest
Expand All @@ -518,44 +486,53 @@ julia> mutable struct Atomic{T}; @atomic x::T; end
julia> a = Atomic(1)
Atomic{Int64}(1)
julia> @atomic! :sequentially_consistent a.x = 2 # set field x of a, with sequential consistency
julia> @atomic a.x # fetch field x of a, with sequential consistency
1
julia> @atomic :sequentially_consistent a.x = 2 # set field x of a, with sequential consistency
2
julia> @atomic! a.x += 1 # increment field x of a, with sequential consistency
julia> @atomic a.x += 1 # increment field x of a, with sequential consistency
3
julia> @atomic! a.x + 1 # increment field x of a, with sequential consistency
julia> @atomic a.x + 1 # increment field x of a, with sequential consistency
(3, 4)
julia> @atomic a.x # fetch field x of a, with sequential consistency
4
julia> @atomic! max(a.x, 10) # change field x of a to the max value, with sequential consistency
julia> @atomic max(a.x, 10) # change field x of a to the max value, with sequential consistency
(4, 10)
julia> @atomic! a.x max 5 # again change field x of a to the max value, with sequential consistency
julia> @atomic a.x max 5 # again change field x of a to the max value, with sequential consistency
(10, 10)
```
"""
macro atomic!(order, a1, op, a2)
macro atomic(ex)
if !isa(ex, Symbol) && !is_expr(ex, :(::))
return make_atomic(QuoteNode(:sequentially_consistent), ex)
end
return esc(Expr(:atomic, ex))
end
macro atomic(order, ex)
order isa QuoteNode || (order = esc(order))
return make_atomic!(order, a1, op, a2)
return make_atomic(order, ex)
end
macro atomic!(a1, op, a2)
return make_atomic!(QuoteNode(:sequentially_consistent), a1, op, a2)
macro atomic(a1, op, a2)
return make_atomic(QuoteNode(:sequentially_consistent), a1, op, a2)
end
macro atomic!(order, ex)
macro atomic(order, a1, op, a2)
order isa QuoteNode || (order = esc(order))
return make_atomic!(order, ex)
end
macro atomic!(ex)
return make_atomic!(QuoteNode(:sequentially_consistent), ex)
return make_atomic(order, a1, op, a2)
end
function make_atomic!(order, ex)
function make_atomic(order, ex)
@nospecialize
if ex isa Expr
if isexpr(ex, :call, 3)
return make_atomic!(order, ex.args[2], ex.args[1], ex.args[3])
if isexpr(ex, :., 2)
l, r = esc(ex.args[1]), esc(ex.args[2])
return :(getproperty($l, $r, $order))
elseif isexpr(ex, :call, 3)
return make_atomic(order, ex.args[2], ex.args[1], ex.args[3])
elseif ex.head === :(=)
l, r = ex.args[1], ex.args[2]
if is_expr(l, :., 2)
Expand All @@ -575,26 +552,23 @@ function make_atomic!(order, ex)
end
end
if @isdefined(op)
l, r = ex.args[1], esc(ex.args[2])
is_expr(l, :.) || error("@atomic modify expression missing field access")
ll, lr, op = esc(l.args[1]), esc(l.args[2]), esc(op)
return :(modifyproperty!($ll, $lr, $op, $r, $order)[2])
return Expr(:ref, make_atomic(order, ex.args[1], op, ex.args[2]), 2)
end
end
end
error("could not parse @atomic! modify expression $ex")
error("could not parse @atomic expression $ex")
end
function make_atomic!(order, a1, op, a2)
function make_atomic(order, a1, op, a2)
@nospecialize
is_expr(a1, :., 2) || error("@atomic! modify expression missing field access")
is_expr(a1, :., 2) || error("@atomic modify expression missing field access")
a1l, a1r, op, a2 = esc(a1.args[1]), esc(a1.args[2]), esc(op), esc(a2)
return :(modifyproperty!($a1l, $a1r, $op, $a2, $order))
end


"""
@atomic_swap! a.b.x new
@atomic_swap! :sequentially_consistent a.b.x new
@atomicswap a.b.x new
@atomicswap :sequentially_consistent a.b.x new
Stores `new` into `a.b.x` and returns the old value of `a.b.x`.
Expand All @@ -608,32 +582,32 @@ julia> mutable struct Atomic{T}; @atomic x::T; end
julia> a = Atomic(1)
Atomic{Int64}(1)
julia> @atomic_swap! a.x 2+2 # replace field x of a with 4, with sequential consistency
julia> @atomicswap a.x 2+2 # replace field x of a with 4, with sequential consistency
1
julia> @atomic a.x # fetch field x of a, with sequential consistency
4
```
"""
macro atomic_swap!(order, ex, val)
macro atomicswap(order, ex, val)
order isa QuoteNode || (order = esc(order))
return make_atomic_swap!(order, ex, val)
return make_atomicswap(order, ex, val)
end
macro atomic_swap!(ex, val)
return make_atomic_swap!(QuoteNode(:sequentially_consistent), ex, val)
macro atomicswap(ex, val)
return make_atomicswap(QuoteNode(:sequentially_consistent), ex, val)
end
function make_atomic_swap!(order, ex, val)
function make_atomicswap(order, ex, val)
@nospecialize
is_expr(ex, :., 2) || error("@atomic_swap! expression missing field access")
is_expr(ex, :., 2) || error("@atomicswap expression missing field access")
l, r, val = esc(ex.args[1]), esc(ex.args[2]), esc(val)
return :(swapproperty!($l, $r, $val, $order))
end


"""
@atomic_replace! a.b.x expected => desired
@atomic_replace! :sequentially_consistent a.b.x expected => desired
@atomic_replace! :sequentially_consistent :monotonic a.b.x expected => desired
@atomicreplace a.b.x expected => desired
@atomicreplace :sequentially_consistent a.b.x expected => desired
@atomicreplace :sequentially_consistent :monotonic a.b.x expected => desired
Perform the conditional replacement expressed by the pair atomically, returning
the values `(old, success::Bool)`. Where `success` indicates whether the
Expand All @@ -649,39 +623,39 @@ julia> mutable struct Atomic{T}; @atomic x::T; end
julia> a = Atomic(1)
Atomic{Int64}(1)
julia> @atomic_replace! a.x 1 => 2 # replace field x of a with 2 if it was 1, with sequential consistency
julia> @atomicreplace a.x 1 => 2 # replace field x of a with 2 if it was 1, with sequential consistency
(1, true)
julia> @atomic a.x # fetch field x of a, with sequential consistency
2
julia> @atomic_replace! a.x 1 => 2 # replace field x of a with 2 if it was 1, with sequential consistency
julia> @atomicreplace a.x 1 => 2 # replace field x of a with 2 if it was 1, with sequential consistency
(2, false)
julia> xchg = 2 => 0; # replace field x of a with 0 if it was 1, with sequential consistency
julia> @atomic_replace! a.x xchg
julia> @atomicreplace a.x xchg
(2, true)
julia> @atomic a.x # fetch field x of a, with sequential consistency
0
```
"""
macro atomic_replace!(success_order, fail_order, ex, old_new)
macro atomicreplace(success_order, fail_order, ex, old_new)
fail_order isa QuoteNode || (fail_order = esc(fail_order))
success_order isa QuoteNode || (success_order = esc(success_order))
return make_atomic_replace!(success_order, fail_order, ex, old_new)
return make_atomicreplace(success_order, fail_order, ex, old_new)
end
macro atomic_replace!(order, ex, old_new)
macro atomicreplace(order, ex, old_new)
order isa QuoteNode || (order = esc(order))
return make_atomic_replace!(order, order, ex, old_new)
return make_atomicreplace(order, order, ex, old_new)
end
macro atomic_replace!(ex, old_new)
return make_atomic_replace!(QuoteNode(:sequentially_consistent), QuoteNode(:sequentially_consistent), ex, old_new)
macro atomicreplace(ex, old_new)
return make_atomicreplace(QuoteNode(:sequentially_consistent), QuoteNode(:sequentially_consistent), ex, old_new)
end
function make_atomic_replace!(success_order, fail_order, ex, old_new)
function make_atomicreplace(success_order, fail_order, ex, old_new)
@nospecialize
is_expr(ex, :., 2) || error("@atomic_replace! expression missing field access")
is_expr(ex, :., 2) || error("@atomicreplace expression missing field access")
ll, lr = esc(ex.args[1]), esc(ex.args[2])
if is_expr(old_new, :call, 3) && old_new.args[1] === :(=>)
exp, rep = esc(old_new.args[2]), esc(old_new.args[3])
Expand Down
5 changes: 2 additions & 3 deletions doc/src/base/multi-threading.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,8 @@ See also [Synchronization](@ref lib-task-sync).

```@docs
Base.@atomic
Base.@atomic!
Base.@atomic_swap!
Base.@atomic_replace!
Base.@atomicswap
Base.@atomicreplace
```

!!! note
Expand Down
66 changes: 33 additions & 33 deletions test/atomics.jl
Original file line number Diff line number Diff line change
Expand Up @@ -286,53 +286,53 @@ test_field_undef(ARefxy{UndefComplex{UndefComplex{Any}}})

@test_throws ErrorException @macroexpand @atomic foo()
@test_throws ErrorException @macroexpand @atomic foo += bar
@test_throws ErrorException @macroexpand @atomic! foo += bar
@test_throws ErrorException @macroexpand @atomic! foo = bar
@test_throws ErrorException @macroexpand @atomic! foo()
@test_throws ErrorException @macroexpand @atomic! foo(bar)
@test_throws ErrorException @macroexpand @atomic! foo(bar, baz)
@test_throws ErrorException @macroexpand @atomic! foo(bar, baz, bax)
@test_throws ErrorException @macroexpand @atomic_replace! foo bar
@test_throws ErrorException @macroexpand @atomic foo += bar
@test_throws ErrorException @macroexpand @atomic foo = bar
@test_throws ErrorException @macroexpand @atomic foo()
@test_throws ErrorException @macroexpand @atomic foo(bar)
@test_throws ErrorException @macroexpand @atomic foo(bar, baz)
@test_throws ErrorException @macroexpand @atomic foo(bar, baz, bax)
@test_throws ErrorException @macroexpand @atomicreplace foo bar

# test macroexpansions
let a = ARefxy(1, -1)
@test 1 === @atomic a.x
@test 2 === @atomic! :sequentially_consistent a.x = 2
@test 3 === @atomic! :monotonic a.x = 3
@test_throws ConcurrencyViolationError @atomic! :not_atomic a.x = 2
@test 2 === @atomic :sequentially_consistent a.x = 2
@test 3 === @atomic :monotonic a.x = 3
@test_throws ConcurrencyViolationError @atomic :not_atomic a.x = 2
@test_throws ConcurrencyViolationError @atomic :not_atomic a.x
@test_throws ConcurrencyViolationError @atomic! :not_atomic a.x += 1
@test_throws ConcurrencyViolationError @atomic :not_atomic a.x += 1

@test 3 === @atomic :monotonic a.x
@test 5 === @atomic! a.x += 2
@test 4 === @atomic! :monotonic a.x -= 1
@test 12 === @atomic! :monotonic a.x *= 3
@test 5 === @atomic a.x += 2
@test 4 === @atomic :monotonic a.x -= 1
@test 12 === @atomic :monotonic a.x *= 3

@test 12 === @atomic a.x
@test (12, 13) === @atomic! a.x + 1
@test (13, 15) === @atomic! :monotonic a.x + 2
@test (15, 19) === @atomic! a.x max 19
@test (19, 20) === @atomic! :monotonic a.x max 20
@test_throws ConcurrencyViolationError @atomic! :not_atomic a.x + 1
@test_throws ConcurrencyViolationError @atomic! :not_atomic a.x max 30
@test (12, 13) === @atomic a.x + 1
@test (13, 15) === @atomic :monotonic a.x + 2
@test (15, 19) === @atomic a.x max 19
@test (19, 20) === @atomic :monotonic a.x max 20
@test_throws ConcurrencyViolationError @atomic :not_atomic a.x + 1
@test_throws ConcurrencyViolationError @atomic :not_atomic a.x max 30

@test 20 === @atomic a.x
@test 20 === @atomic_swap! a.x 1
@test 1 === @atomic_swap! :monotonic a.x 2
@test_throws ConcurrencyViolationError @atomic_swap! :not_atomic a.x 1
@test 20 === @atomicswap a.x 1
@test 1 === @atomicswap :monotonic a.x 2
@test_throws ConcurrencyViolationError @atomicswap :not_atomic a.x 1

@test 2 === @atomic a.x
@test (2, true) === @atomic_replace! a.x 2 => 1
@test (1, false) === @atomic_replace! :monotonic a.x 2 => 1
@test (1, false) === @atomic_replace! :monotonic :monotonic a.x 2 => 1
@test_throws ConcurrencyViolationError @atomic_replace! :not_atomic a.x 1 => 2
@test_throws ConcurrencyViolationError @atomic_replace! :monotonic :acquire a.x 1 => 2
@test (2, true) === @atomicreplace a.x 2 => 1
@test (1, false) === @atomicreplace :monotonic a.x 2 => 1
@test (1, false) === @atomicreplace :monotonic :monotonic a.x 2 => 1
@test_throws ConcurrencyViolationError @atomicreplace :not_atomic a.x 1 => 2
@test_throws ConcurrencyViolationError @atomicreplace :monotonic :acquire a.x 1 => 2

@test 1 === @atomic a.x
xchg = 1 => 2
@test (1, true) === @atomic_replace! a.x xchg
@test (2, false) === @atomic_replace! :monotonic a.x xchg
@test (2, false) === @atomic_replace! :acquire_release :monotonic a.x xchg
@test_throws ConcurrencyViolationError @atomic_replace! :not_atomic a.x xchg
@test_throws ConcurrencyViolationError @atomic_replace! :monotonic :acquire a.x xchg
@test (1, true) === @atomicreplace a.x xchg
@test (2, false) === @atomicreplace :monotonic a.x xchg
@test (2, false) === @atomicreplace :acquire_release :monotonic a.x xchg
@test_throws ConcurrencyViolationError @atomicreplace :not_atomic a.x xchg
@test_throws ConcurrencyViolationError @atomicreplace :monotonic :acquire a.x xchg
end

0 comments on commit 1ebe4a7

Please sign in to comment.