Skip to content

Commit

Permalink
move mutation to atomic! always
Browse files Browse the repository at this point in the history
  • Loading branch information
vtjnash committed May 27, 2021
1 parent 51936d3 commit 05f8ee1
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 55 deletions.
110 changes: 62 additions & 48 deletions base/expr.jl
Original file line number Diff line number Diff line change
Expand Up @@ -461,14 +461,8 @@ 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
2
julia> @atomic a.x # fetch field x of a, with sequential consistency
2
julia> @atomic a.x += 1 # increment field x of a, with sequential consistency
3
1
```
"""
macro atomic(ex)
Expand All @@ -483,47 +477,33 @@ macro atomic(order, ex)
end
function make_atomic(order, ex)
@nospecialize
if ex isa Expr
if ex.head === :.
l, r = esc(ex.args[1]), esc(ex.args[2])
return :(getproperty($l, $r, $order))
elseif ex.head === :(=)
l, r = ex.args[1], ex.args[2]
if is_expr(l, :., 2)
ll, lr = esc(l.args[1]), esc(l.args[2])
return :(setproperty!($ll, $lr, $r, $order))
end
end
if length(ex.args) == 2
if ex.head === :(+=)
op = :+
elseif ex.head === :(-=)
op = :-
elseif @isdefined string
shead = string(ex.head)
if endswith(shead, '=')
op = Symbol(shead[1:prevind(shead, end)])
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])
end
end
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 max new()
@atomic! a.b.x + new()
@atomic! max(a.b.x, new())
@atomic! :acquire_release max(a.b.x, new())
@atomic! :acquire_release a.b.x + new()
@atomic! :acquire_release a.b.x max new()
@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.
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
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)`.
Expand All @@ -538,14 +518,20 @@ 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
2
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
(1, 2)
(3, 4)
julia> @atomic a.x # fetch field x of a, with sequential consistency
2
4
julia> @atomic! max(a.x, 10) # change field x of a to the max value, with sequential consistency
(2, 10)
(4, 10)
julia> @atomic! a.x max 5 # again change field x of a to the max value, with sequential consistency
(10, 10)
Expand All @@ -567,8 +553,36 @@ macro atomic!(ex)
end
function make_atomic!(order, ex)
@nospecialize
isexpr(ex, :call, 3) || error("could not parse @atomic! modify expression $ex")
return make_atomic!(order, ex.args[2], ex.args[1], ex.args[3])
if ex isa Expr
if 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)
ll, lr = esc(l.args[1]), esc(l.args[2])
return :(setproperty!($ll, $lr, $r, $order))
end
end
if length(ex.args) == 2
if ex.head === :(+=)
op = :+
elseif ex.head === :(-=)
op = :-
elseif @isdefined string
shead = string(ex.head)
if endswith(shead, '=')
op = Symbol(shead[1:prevind(shead, end)])
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])
end
end
end
error("could not parse @atomic! modify expression $ex")
end
function make_atomic!(order, a1, op, a2)
@nospecialize
Expand Down Expand Up @@ -674,6 +688,6 @@ function make_atomic_replace!(success_order, fail_order, ex, old_new)
return :(replaceproperty!($ll, $lr, $exp, $rep, $success_order, $fail_order))
else
old_new = esc(old_new)
return :(local old_new = $old_new::Pair; replaceproperty!($ll, $lr, old_new[1], old_new[2], $success_order, $fail_order))
return :(replaceproperty!($ll, $lr, $old_new::Pair..., $success_order, $fail_order))
end
end
14 changes: 7 additions & 7 deletions test/atomics.jl
Original file line number Diff line number Diff line change
Expand Up @@ -297,16 +297,16 @@ test_field_undef(ARefxy{UndefComplex{UndefComplex{Any}}})
# 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
Expand Down

0 comments on commit 05f8ee1

Please sign in to comment.