Skip to content

Commit 51936d3

Browse files
committed
make atomic_swap a separate macro
1 parent c85f660 commit 51936d3

File tree

4 files changed

+53
-31
lines changed

4 files changed

+53
-31
lines changed

base/exports.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1018,6 +1018,7 @@ export
10181018
@assert,
10191019
@atomic,
10201020
@atomic!,
1021+
@atomic_swap!,
10211022
@atomic_replace!,
10221023
@__dot__,
10231024
@enum,

base/expr.jl

Lines changed: 48 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -525,14 +525,10 @@ end
525525
@atomic! :acquire_release a.b.x + new()
526526
@atomic! :acquire_release a.b.x max new()
527527
528-
@atomic! a.b.x = new()
529-
@atomic! :acquire_release a.b.x = new()
528+
Perform the binary operation expressed on the right atomically. Store the
529+
result into the field in the first argument and return the values `(old, new)`.
530530
531-
Perform the operation expressed on the right atomically, for the supported
532-
expressions shown, returning the values `(old, new)`.
533-
534-
The first set of operations translates to a `modifyproperty!` call.
535-
The second set of operations translates to a `swapproperty!` call.
531+
This operation translates to a `modifyproperty!(a.b, :x, func, arg2)` call.
536532
537533
See [atomics](#man-atomics) in the manual for more details.
538534
@@ -542,17 +538,14 @@ julia> mutable struct Atomic{T}; @atomic x::T; end
542538
julia> a = Atomic(1)
543539
Atomic{Int64}(1)
544540
545-
julia> @atomic! :sequentially_consistent a.x = 2 # swap field x of a, with sequential consistency
541+
julia> @atomic! a.x + 1 # increment field x of a, with sequential consistency
546542
(1, 2)
547543
548544
julia> @atomic a.x # fetch field x of a, with sequential consistency
549545
2
550546
551-
julia> @atomic! a.x + 1 # increment field x of a, with sequential consistency
552-
(2, 3)
553-
554547
julia> @atomic! max(a.x, 10) # change field x of a to the max value, with sequential consistency
555-
(3, 10)
548+
(2, 10)
556549
557550
julia> @atomic! a.x max 5 # again change field x of a to the max value, with sequential consistency
558551
(10, 10)
@@ -574,27 +567,55 @@ macro atomic!(ex)
574567
end
575568
function make_atomic!(order, ex)
576569
@nospecialize
577-
if ex isa Expr
578-
if ex.head === :call
579-
length(ex.args) == 3 || error("@atomic modify expression has the wrong number of function arguments")
580-
return make_atomic!(order, ex.args[2], ex.args[1], ex.args[3])
581-
elseif ex.head === :(=)
582-
l, r = ex.args[1], ex.args[2]
583-
is_expr(l, :., 2) || error("@atomic swap expression missing field access")
584-
ll, lr = esc(l.args[1]), esc(l.args[2])
585-
val = esc(r)
586-
return :(local val = $val; (swapproperty!($ll, $lr, val, $order), val))
587-
end
588-
end
589-
error("could not parse @atomic expression $ex")
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])
590572
end
591573
function make_atomic!(order, a1, op, a2)
592574
@nospecialize
593-
is_expr(a1, :., 2) || error("@atomic modify expression missing field access")
575+
is_expr(a1, :., 2) || error("@atomic! modify expression missing field access")
594576
a1l, a1r, op, a2 = esc(a1.args[1]), esc(a1.args[2]), esc(op), esc(a2)
595577
return :(modifyproperty!($a1l, $a1r, $op, $a2, $order))
596578
end
597579

580+
581+
"""
582+
@atomic_swap! a.b.x new
583+
@atomic_swap! :sequentially_consistent a.b.x new
584+
585+
Stores `new` into `a.b.x` and returns the old value of `a.b.x`.
586+
587+
This operation translates to a `swapproperty!(a.b, :x, new)` call.
588+
589+
See [atomics](#man-atomics) in the manual for more details.
590+
591+
```jldoctest
592+
julia> mutable struct Atomic{T}; @atomic x::T; end
593+
594+
julia> a = Atomic(1)
595+
Atomic{Int64}(1)
596+
597+
julia> @atomic_swap! a.x 2+2 # replace field x of a with 4, with sequential consistency
598+
1
599+
600+
julia> @atomic a.x # fetch field x of a, with sequential consistency
601+
4
602+
```
603+
"""
604+
macro atomic_swap!(order, ex, val)
605+
order isa QuoteNode || (order = esc(order))
606+
return make_atomic_swap!(order, ex, val)
607+
end
608+
macro atomic_swap!(ex, val)
609+
return make_atomic_swap!(QuoteNode(:sequentially_consistent), ex, val)
610+
end
611+
function make_atomic_swap!(order, ex, val)
612+
@nospecialize
613+
is_expr(ex, :., 2) || error("@atomic_swap! expression missing field access")
614+
l, r, val = esc(ex.args[1]), esc(ex.args[2]), esc(val)
615+
return :(swapproperty!($l, $r, $val, $order))
616+
end
617+
618+
598619
"""
599620
@atomic_replace! a.b.x expected => desired
600621
@atomic_replace! :sequentially_consistent a.b.x expected => desired
@@ -646,7 +667,7 @@ macro atomic_replace!(ex, old_new)
646667
end
647668
function make_atomic_replace!(success_order, fail_order, ex, old_new)
648669
@nospecialize
649-
is_expr(ex, :., 2) || error("@atomic replace expression missing field access")
670+
is_expr(ex, :., 2) || error("@atomic_replace! expression missing field access")
650671
ll, lr = esc(ex.args[1]), esc(ex.args[2])
651672
if is_expr(old_new, :call, 3) && old_new.args[1] === :(=>)
652673
exp, rep = esc(old_new.args[2]), esc(old_new.args[3])

doc/src/base/multi-threading.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ See also [Synchronization](@ref lib-task-sync).
2222
```@docs
2323
Base.@atomic
2424
Base.@atomic!
25+
Base.@atomic_swap!
2526
Base.@atomic_replace!
2627
```
2728

test/atomics.jl

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -317,10 +317,9 @@ let a = ARefxy(1, -1)
317317
@test_throws ConcurrencyViolationError @atomic! :not_atomic a.x max 30
318318

319319
@test 20 === @atomic a.x
320-
@test (20, 1) === @atomic! a.x = 1
321-
@test (1, 2) === @atomic! :monotonic a.x = 2
322-
@test_throws ConcurrencyViolationError @atomic! :not_atomic a.x = 1
323-
320+
@test 20 === @atomic_swap! a.x 1
321+
@test 1 === @atomic_swap! :monotonic a.x 2
322+
@test_throws ConcurrencyViolationError @atomic_swap! :not_atomic a.x 1
324323

325324
@test 2 === @atomic a.x
326325
@test (2, true) === @atomic_replace! a.x 2 => 1

0 commit comments

Comments
 (0)