Skip to content

Commit 05f8ee1

Browse files
committed
move mutation to atomic! always
1 parent 51936d3 commit 05f8ee1

File tree

2 files changed

+69
-55
lines changed

2 files changed

+69
-55
lines changed

base/expr.jl

+62-48
Original file line numberDiff line numberDiff line change
@@ -461,14 +461,8 @@ julia> mutable struct Atomic{T}; @atomic x::T; end
461461
julia> a = Atomic(1)
462462
Atomic{Int64}(1)
463463
464-
julia> @atomic :sequentially_consistent a.x = 2 # set field x of a, with sequential consistency
465-
2
466-
467464
julia> @atomic a.x # fetch field x of a, with sequential consistency
468-
2
469-
470-
julia> @atomic a.x += 1 # increment field x of a, with sequential consistency
471-
3
465+
1
472466
```
473467
"""
474468
macro atomic(ex)
@@ -483,47 +477,33 @@ macro atomic(order, ex)
483477
end
484478
function make_atomic(order, ex)
485479
@nospecialize
486-
if ex isa Expr
487-
if ex.head === :.
488-
l, r = esc(ex.args[1]), esc(ex.args[2])
489-
return :(getproperty($l, $r, $order))
490-
elseif ex.head === :(=)
491-
l, r = ex.args[1], ex.args[2]
492-
if is_expr(l, :., 2)
493-
ll, lr = esc(l.args[1]), esc(l.args[2])
494-
return :(setproperty!($ll, $lr, $r, $order))
495-
end
496-
end
497-
if length(ex.args) == 2
498-
if ex.head === :(+=)
499-
op = :+
500-
elseif ex.head === :(-=)
501-
op = :-
502-
elseif @isdefined string
503-
shead = string(ex.head)
504-
if endswith(shead, '=')
505-
op = Symbol(shead[1:prevind(shead, end)])
506-
end
507-
end
508-
if @isdefined(op)
509-
l, r = ex.args[1], esc(ex.args[2])
510-
is_expr(l, :.) || error("@atomic modify expression missing field access")
511-
ll, lr, op = esc(l.args[1]), esc(l.args[2]), esc(op)
512-
return :(modifyproperty!($ll, $lr, $op, $r, $order)[2])
513-
end
514-
end
480+
if isexpr(ex, :., 2)
481+
l, r = esc(ex.args[1]), esc(ex.args[2])
482+
return :(getproperty($l, $r, $order))
515483
end
516484
error("could not parse @atomic expression $ex")
517485
end
518486

519487

520488
"""
521-
@atomic! a.b.x max new()
522-
@atomic! a.b.x + new()
523-
@atomic! max(a.b.x, new())
524-
@atomic! :acquire_release max(a.b.x, new())
525-
@atomic! :acquire_release a.b.x + new()
526-
@atomic! :acquire_release a.b.x max new()
489+
@atomic! a.b.x = new
490+
@atomic! a.b.x += addend
491+
@atomic! :acquire_release a.b.x = new
492+
@atomic! :acquire_release a.b.x += addend
493+
494+
Perform the store operation expressed on the right atomically and return the
495+
new value.
496+
497+
With `=`, this operation translates to a `setproperty!(a.b, :x, new)` call.
498+
With any operator also, this operation translates to a `modifyproperty!(a.b,
499+
:x, +, addend)[2]` call.
500+
501+
@atomic! a.b.x max arg2
502+
@atomic! a.b.x + arg2
503+
@atomic! max(a.b.x, arg2)
504+
@atomic! :acquire_release max(a.b.x, arg2)
505+
@atomic! :acquire_release a.b.x + arg2
506+
@atomic! :acquire_release a.b.x max arg2
527507
528508
Perform the binary operation expressed on the right atomically. Store the
529509
result into the field in the first argument and return the values `(old, new)`.
@@ -538,14 +518,20 @@ julia> mutable struct Atomic{T}; @atomic x::T; end
538518
julia> a = Atomic(1)
539519
Atomic{Int64}(1)
540520
521+
julia> @atomic! :sequentially_consistent a.x = 2 # set field x of a, with sequential consistency
522+
2
523+
524+
julia> @atomic! a.x += 1 # increment field x of a, with sequential consistency
525+
3
526+
541527
julia> @atomic! a.x + 1 # increment field x of a, with sequential consistency
542-
(1, 2)
528+
(3, 4)
543529
544530
julia> @atomic a.x # fetch field x of a, with sequential consistency
545-
2
531+
4
546532
547533
julia> @atomic! max(a.x, 10) # change field x of a to the max value, with sequential consistency
548-
(2, 10)
534+
(4, 10)
549535
550536
julia> @atomic! a.x max 5 # again change field x of a to the max value, with sequential consistency
551537
(10, 10)
@@ -567,8 +553,36 @@ macro atomic!(ex)
567553
end
568554
function make_atomic!(order, ex)
569555
@nospecialize
570-
isexpr(ex, :call, 3) || error("could not parse @atomic! modify expression $ex")
571-
return make_atomic!(order, ex.args[2], ex.args[1], ex.args[3])
556+
if ex isa Expr
557+
if isexpr(ex, :call, 3)
558+
return make_atomic!(order, ex.args[2], ex.args[1], ex.args[3])
559+
elseif ex.head === :(=)
560+
l, r = ex.args[1], ex.args[2]
561+
if is_expr(l, :., 2)
562+
ll, lr = esc(l.args[1]), esc(l.args[2])
563+
return :(setproperty!($ll, $lr, $r, $order))
564+
end
565+
end
566+
if length(ex.args) == 2
567+
if ex.head === :(+=)
568+
op = :+
569+
elseif ex.head === :(-=)
570+
op = :-
571+
elseif @isdefined string
572+
shead = string(ex.head)
573+
if endswith(shead, '=')
574+
op = Symbol(shead[1:prevind(shead, end)])
575+
end
576+
end
577+
if @isdefined(op)
578+
l, r = ex.args[1], esc(ex.args[2])
579+
is_expr(l, :.) || error("@atomic modify expression missing field access")
580+
ll, lr, op = esc(l.args[1]), esc(l.args[2]), esc(op)
581+
return :(modifyproperty!($ll, $lr, $op, $r, $order)[2])
582+
end
583+
end
584+
end
585+
error("could not parse @atomic! modify expression $ex")
572586
end
573587
function make_atomic!(order, a1, op, a2)
574588
@nospecialize
@@ -674,6 +688,6 @@ function make_atomic_replace!(success_order, fail_order, ex, old_new)
674688
return :(replaceproperty!($ll, $lr, $exp, $rep, $success_order, $fail_order))
675689
else
676690
old_new = esc(old_new)
677-
return :(local old_new = $old_new::Pair; replaceproperty!($ll, $lr, old_new[1], old_new[2], $success_order, $fail_order))
691+
return :(replaceproperty!($ll, $lr, $old_new::Pair..., $success_order, $fail_order))
678692
end
679693
end

test/atomics.jl

+7-7
Original file line numberDiff line numberDiff line change
@@ -297,16 +297,16 @@ test_field_undef(ARefxy{UndefComplex{UndefComplex{Any}}})
297297
# test macroexpansions
298298
let a = ARefxy(1, -1)
299299
@test 1 === @atomic a.x
300-
@test 2 === @atomic :sequentially_consistent a.x = 2
301-
@test 3 === @atomic :monotonic a.x = 3
302-
@test_throws ConcurrencyViolationError @atomic :not_atomic a.x = 2
300+
@test 2 === @atomic! :sequentially_consistent a.x = 2
301+
@test 3 === @atomic! :monotonic a.x = 3
302+
@test_throws ConcurrencyViolationError @atomic! :not_atomic a.x = 2
303303
@test_throws ConcurrencyViolationError @atomic :not_atomic a.x
304-
@test_throws ConcurrencyViolationError @atomic :not_atomic a.x += 1
304+
@test_throws ConcurrencyViolationError @atomic! :not_atomic a.x += 1
305305

306306
@test 3 === @atomic :monotonic a.x
307-
@test 5 === @atomic a.x += 2
308-
@test 4 === @atomic :monotonic a.x -= 1
309-
@test 12 === @atomic :monotonic a.x *= 3
307+
@test 5 === @atomic! a.x += 2
308+
@test 4 === @atomic! :monotonic a.x -= 1
309+
@test 12 === @atomic! :monotonic a.x *= 3
310310

311311
@test 12 === @atomic a.x
312312
@test (12, 13) === @atomic! a.x + 1

0 commit comments

Comments
 (0)