@@ -1101,26 +1101,43 @@ If no `order` is specified it defaults to :sequentially_consistent.
11011101 @atomic a.b.x += addend
11021102 @atomic :release a.b.x = new
11031103 @atomic :acquire_release a.b.x += addend
1104+ @atomic m[idx] = new
1105+ @atomic m[idx] += addend
1106+ @atomic :release m[idx] = new
1107+ @atomic :acquire_release m[idx] += addend
11041108
11051109Perform the store operation expressed on the right atomically and return the
11061110new value.
11071111
1108- With `=`, this operation translates to a `setproperty!(a.b, :x, new)` call.
1109- With any operator also, this operation translates to a `modifyproperty!(a.b,
1110- :x, +, addend)[2]` call.
1112+ With assignment (`=`), this operation translates to a `setproperty!(a.b, :x, new)`
1113+ or, in case of reference, to a `setindex_atomic!(m, order, new, idx)` call,
1114+ with `order` defaulting to `:sequentially_consistent`.
1115+
1116+ With any modifying operator this operation translates to a
1117+ `modifyproperty!(a.b, :x, op, addend)[2]` or, in case of reference, to a
1118+ `modifyindex_atomic!(m, order, op, addend, idx...)[2]` call,
1119+ with `order` defaulting to `:sequentially_consistent`.
11111120
11121121 @atomic a.b.x max arg2
11131122 @atomic a.b.x + arg2
11141123 @atomic max(a.b.x, arg2)
11151124 @atomic :acquire_release max(a.b.x, arg2)
11161125 @atomic :acquire_release a.b.x + arg2
11171126 @atomic :acquire_release a.b.x max arg2
1127+ @atomic m[idx] max arg2
1128+ @atomic m[idx] + arg2
1129+ @atomic max(m[idx], arg2)
1130+ @atomic :acquire_release max(m[idx], arg2)
1131+ @atomic :acquire_release m[idx] + arg2
1132+ @atomic :acquire_release m[idx] max arg2
11181133
11191134Perform the binary operation expressed on the right atomically. Store the
1120- result into the field in the first argument and return the values `(old, new)`.
1121-
1122- This operation translates to a `modifyproperty!(a.b, :x, func, arg2)` call.
1135+ result into the field or the reference in the first argument, and return the values
1136+ `(old, new)`.
11231137
1138+ This operation translates to a `modifyproperty!(a.b, :x, func, arg2)` or,
1139+ in case of reference to a `modifyindex_atomic!(m, order, func, arg2, idx)` call,
1140+ with `order` defaulting to `:sequentially_consistent`.
11241141
11251142See [Per-field atomics](@ref man-atomics) section in the manual for more details.
11261143
@@ -1153,8 +1170,36 @@ julia> @atomic a.x max 5 # again change field x of a to the max value, with sequ
1153117010 => 10
11541171```
11551172
1173+ ```jldoctest
1174+ julia> mem = AtomicMemory{Int}(undef, 2);
1175+
1176+ julia> @atomic mem[1] = 2 # set mem[1] to value 2 with sequential consistency
1177+ 2
1178+
1179+ julia> @atomic :monotonic mem[1] # fetch the first value of mem, with monotonic consistency
1180+ 2
1181+
1182+ julia> @atomic mem[1] += 1 # increment the first value of mem, with sequential consistency
1183+ 3
1184+
1185+ julia> @atomic mem[1] + 1 # increment the first value of mem, with sequential consistency
1186+ 3 => 4
1187+
1188+ julia> @atomic mem[1] # fetch the first value of mem, with sequential consistency
1189+ 4
1190+
1191+ julia> @atomic max(mem[1], 10) # change the first value of mem to the max value, with sequential consistency
1192+ 4 => 10
1193+
1194+ julia> @atomic mem[1] max 5 # again change the first value of mem to the max value, with sequential consistency
1195+ 10 => 10
1196+ ```
1197+
11561198!!! compat "Julia 1.7"
1157- This functionality requires at least Julia 1.7.
1199+ Atomic fields functionality requires at least Julia 1.7.
1200+
1201+ !!! compat "Julia 1.12"
1202+ Atomic reference functionality requires at least Julia 1.12.
11581203"""
11591204macro atomic (ex)
11601205 if ! isa (ex, Symbol) && ! is_expr (ex, :(:: ))
@@ -1181,11 +1226,17 @@ function make_atomic(order, ex)
11811226 return :(getproperty ($ l, $ r, $ order))
11821227 elseif isexpr (ex, :call , 3 )
11831228 return make_atomic (order, ex. args[2 ], ex. args[1 ], ex. args[3 ])
1229+ elseif isexpr (ex, :ref )
1230+ x, idcs = esc (ex. args[1 ]), map (esc, ex. args[2 : end ])
1231+ return :(getindex_atomic ($ x, $ order, $ (idcs... )))
11841232 elseif ex. head === :(= )
11851233 l, r = ex. args[1 ], esc (ex. args[2 ])
11861234 if is_expr (l, :., 2 )
11871235 ll, lr = esc (l. args[1 ]), esc (l. args[2 ])
11881236 return :(setproperty! ($ ll, $ lr, $ r, $ order))
1237+ elseif is_expr (l, :ref )
1238+ x, idcs = esc (l. args[1 ]), map (esc, l. args[2 : end ])
1239+ return :(setindex_atomic! ($ x, $ order, $ r, $ (idcs... )))
11891240 end
11901241 end
11911242 if length (ex. args) == 2
@@ -1208,19 +1259,29 @@ function make_atomic(order, ex)
12081259end
12091260function make_atomic (order, a1, op, a2)
12101261 @nospecialize
1211- is_expr (a1, :., 2 ) || error (" @atomic modify expression missing field access" )
1212- a1l, a1r, op, a2 = esc (a1. args[1 ]), esc (a1. args[2 ]), esc (op), esc (a2)
1213- return :(modifyproperty! ($ a1l, $ a1r, $ op, $ a2, $ order))
1262+ if is_expr (a1, :., 2 )
1263+ a1l, a1r, op, a2 = esc (a1. args[1 ]), esc (a1. args[2 ]), esc (op), esc (a2)
1264+ return :(modifyproperty! ($ a1l, $ a1r, $ op, $ a2, $ order))
1265+ elseif is_expr (a1, :ref )
1266+ x, idcs, op, a2 = esc (a1. args[1 ]), map (esc, a1. args[2 : end ]), esc (op), esc (a2)
1267+ return :(modifyindex_atomic! ($ x, $ order, $ op, $ a2, $ (idcs... )))
1268+ end
1269+ error (" @atomic modify expression missing field access or indexing" )
12141270end
12151271
12161272
12171273"""
12181274 @atomicswap a.b.x = new
12191275 @atomicswap :sequentially_consistent a.b.x = new
1276+ @atomicswap m[idx] = new
1277+ @atomicswap :sequentially_consistent m[idx] = new
12201278
1221- Stores `new` into `a.b.x` and returns the old value of `a.b.x`.
1279+ Stores `new` into `a.b.x` (`m[idx]` in case of reference) and returns the old
1280+ value of `a.b.x` (the old value stored at `m[idx]`, respectively).
12221281
1223- This operation translates to a `swapproperty!(a.b, :x, new)` call.
1282+ This operation translates to a `swapproperty!(a.b, :x, new)` or,
1283+ in case of reference, `swapindex_atomic!(mem, order, new, idx)` call,
1284+ with `order` defaulting to `:sequentially_consistent`.
12241285
12251286See [Per-field atomics](@ref man-atomics) section in the manual for more details.
12261287
@@ -1238,8 +1299,23 @@ julia> @atomic a.x # fetch field x of a, with sequential consistency
123812994
12391300```
12401301
1302+ ```jldoctest
1303+ julia> mem = AtomicMemory{Int}(undef, 2);
1304+
1305+ julia> @atomic mem[1] = 1;
1306+
1307+ julia> @atomicswap mem[1] = 4 # replace the first value of `mem` with 4, with sequential consistency
1308+ 1
1309+
1310+ julia> @atomic mem[1] # fetch the first value of mem, with sequential consistency
1311+ 4
1312+ ```
1313+
12411314!!! compat "Julia 1.7"
1242- This functionality requires at least Julia 1.7.
1315+ Atomic fields functionality requires at least Julia 1.7.
1316+
1317+ !!! compat "Julia 1.12"
1318+ Atomic reference functionality requires at least Julia 1.12.
12431319"""
12441320macro atomicswap (order, ex)
12451321 order isa QuoteNode || (order = esc (order))
@@ -1252,22 +1328,33 @@ function make_atomicswap(order, ex)
12521328 @nospecialize
12531329 is_expr (ex, :(= ), 2 ) || error (" @atomicswap expression missing assignment" )
12541330 l, val = ex. args[1 ], esc (ex. args[2 ])
1255- is_expr (l, :., 2 ) || error (" @atomicswap expression missing field access" )
1256- ll, lr = esc (l. args[1 ]), esc (l. args[2 ])
1257- return :(swapproperty! ($ ll, $ lr, $ val, $ order))
1331+ if is_expr (l, :., 2 )
1332+ ll, lr = esc (l. args[1 ]), esc (l. args[2 ])
1333+ return :(swapproperty! ($ ll, $ lr, $ val, $ order))
1334+ elseif is_expr (l, :ref )
1335+ x, idcs = esc (l. args[1 ]), map (esc, l. args[2 : end ])
1336+ return :(swapindex_atomic! ($ x, $ order, $ val, $ (idcs... )))
1337+ end
1338+ error (" @atomicswap expression missing field access or indexing" )
12581339end
12591340
12601341
12611342"""
12621343 @atomicreplace a.b.x expected => desired
12631344 @atomicreplace :sequentially_consistent a.b.x expected => desired
12641345 @atomicreplace :sequentially_consistent :monotonic a.b.x expected => desired
1346+ @atomicreplace m[idx] expected => desired
1347+ @atomicreplace :sequentially_consistent m[idx] expected => desired
1348+ @atomicreplace :sequentially_consistent :monotonic m[idx] expected => desired
12651349
12661350Perform the conditional replacement expressed by the pair atomically, returning
12671351the values `(old, success::Bool)`. Where `success` indicates whether the
12681352replacement was completed.
12691353
1270- This operation translates to a `replaceproperty!(a.b, :x, expected, desired)` call.
1354+ This operation translates to a `replaceproperty!(a.b, :x, expected, desired)` or,
1355+ in case of reference, to a
1356+ `replaceindex_atomic!(mem, success_order, fail_order, expected, desired, idx)` call,
1357+ with both orders defaulting to `:sequentially_consistent`.
12711358
12721359See [Per-field atomics](@ref man-atomics) section in the manual for more details.
12731360
@@ -1284,7 +1371,7 @@ julia> @atomicreplace a.x 1 => 2 # replace field x of a with 2 if it was 1, with
12841371julia> @atomic a.x # fetch field x of a, with sequential consistency
128513722
12861373
1287- julia> @atomicreplace a.x 1 => 2 # replace field x of a with 2 if it was 1, with sequential consistency
1374+ julia> @atomicreplace a.x 1 => 3 # replace field x of a with 2 if it was 1, with sequential consistency
12881375(old = 2, success = false)
12891376
12901377julia> xchg = 2 => 0; # replace field x of a with 0 if it was 2, with sequential consistency
@@ -1296,8 +1383,34 @@ julia> @atomic a.x # fetch field x of a, with sequential consistency
129613830
12971384```
12981385
1386+ ```jldoctest
1387+ julia> mem = AtomicMemory{Int}(undef, 2);
1388+
1389+ julia> @atomic mem[1] = 1;
1390+
1391+ julia> @atomicreplace mem[1] 1 => 2 # replace the first value of mem with 2 if it was 1, with sequential consistency
1392+ (old = 1, success = true)
1393+
1394+ julia> @atomic mem[1] # fetch the first value of mem, with sequential consistency
1395+ 2
1396+
1397+ julia> @atomicreplace mem[1] 1 => 3 # replace field x of a with 2 if it was 1, with sequential consistency
1398+ (old = 2, success = false)
1399+
1400+ julia> xchg = 2 => 0; # replace field x of a with 0 if it was 2, with sequential consistency
1401+
1402+ julia> @atomicreplace mem[1] xchg
1403+ (old = 2, success = true)
1404+
1405+ julia> @atomic mem[1] # fetch the first value of mem, with sequential consistency
1406+ 0
1407+ ```
1408+
12991409!!! compat "Julia 1.7"
1300- This functionality requires at least Julia 1.7.
1410+ Atomic fields functionality requires at least Julia 1.7.
1411+
1412+ !!! compat "Julia 1.12"
1413+ Atomic reference functionality requires at least Julia 1.12.
13011414"""
13021415macro atomicreplace (success_order, fail_order, ex, old_new)
13031416 fail_order isa QuoteNode || (fail_order = esc (fail_order))
@@ -1313,27 +1426,42 @@ macro atomicreplace(ex, old_new)
13131426end
13141427function make_atomicreplace (success_order, fail_order, ex, old_new)
13151428 @nospecialize
1316- is_expr (ex, :., 2 ) || error (" @atomicreplace expression missing field access" )
1317- ll, lr = esc (ex. args[1 ]), esc (ex. args[2 ])
1318- if is_expr (old_new, :call , 3 ) && old_new. args[1 ] === :(=> )
1319- exp, rep = esc (old_new. args[2 ]), esc (old_new. args[3 ])
1320- return :(replaceproperty! ($ ll, $ lr, $ exp, $ rep, $ success_order, $ fail_order))
1321- else
1322- old_new = esc (old_new)
1323- return :(replaceproperty! ($ ll, $ lr, $ old_new:: Pair... , $ success_order, $ fail_order))
1429+ if is_expr (ex, :., 2 )
1430+ ll, lr = esc (ex. args[1 ]), esc (ex. args[2 ])
1431+ if is_expr (old_new, :call , 3 ) && old_new. args[1 ] === :(=> )
1432+ exp, rep = esc (old_new. args[2 ]), esc (old_new. args[3 ])
1433+ return :(replaceproperty! ($ ll, $ lr, $ exp, $ rep, $ success_order, $ fail_order))
1434+ else
1435+ old_new = esc (old_new)
1436+ return :(replaceproperty! ($ ll, $ lr, $ old_new:: Pair... , $ success_order, $ fail_order))
1437+ end
1438+ elseif is_expr (ex, :ref )
1439+ x, idcs = esc (ex. args[1 ]), map (esc, ex. args[2 : end ])
1440+ if is_expr (old_new, :call , 3 ) && old_new. args[1 ] === :(=> )
1441+ exp, rep = esc (old_new. args[2 ]), esc (old_new. args[3 ])
1442+ return :(replaceindex_atomic! ($ x, $ success_order, $ fail_order, $ exp, $ rep, $ (idcs... )))
1443+ else
1444+ old_new = esc (old_new)
1445+ return :(replaceindex_atomic! ($ x, $ success_order, $ fail_order, $ old_new:: Pair... , $ (idcs... )))
1446+ end
13241447 end
1448+ error (" @atomicreplace expression missing field access or indexing" )
13251449end
13261450
13271451"""
13281452 @atomiconce a.b.x = value
13291453 @atomiconce :sequentially_consistent a.b.x = value
13301454 @atomiconce :sequentially_consistent :monotonic a.b.x = value
1455+ @atomiconce m[idx] = value
1456+ @atomiconce :sequentially_consistent m[idx] = value
1457+ @atomiconce :sequentially_consistent :monotonic m[idx] = value
13311458
13321459Perform the conditional assignment of value atomically if it was previously
1333- unset, returning the value `success::Bool`. Where `success` indicates whether
1334- the assignment was completed.
1460+ unset. Returned value `success::Bool` indicates whether the assignment was completed.
13351461
1336- This operation translates to a `setpropertyonce!(a.b, :x, value)` call.
1462+ This operation translates to a `setpropertyonce!(a.b, :x, value)` or,
1463+ in case of reference, to a `setindexonce_atomic!(m, success_order, fail_order, value, idx)` call,
1464+ with both orders defaulting to `:sequentially_consistent`.
13371465
13381466See [Per-field atomics](@ref man-atomics) section in the manual for more details.
13391467
@@ -1353,12 +1481,39 @@ true
13531481julia> @atomic a.x # fetch field x of a, with sequential consistency
135414821
13551483
1356- julia> @atomiconce a.x = 1 # set field x of a to 1, if unset, with sequential consistency
1484+ julia> @atomiconce :monotonic a.x = 2 # set field x of a to 1, if unset, with monotonic consistence
13571485false
13581486```
13591487
1488+ ```jldoctest
1489+ julia> mem = AtomicMemory{Vector{Int}}(undef, 1);
1490+
1491+ julia> isassigned(mem, 1)
1492+ false
1493+
1494+ julia> @atomiconce mem[1] = [1] # set the first value of mem to [1], if unset, with sequential consistency
1495+ true
1496+
1497+ julia> isassigned(mem, 1)
1498+ true
1499+
1500+ julia> @atomic mem[1] # fetch the first value of mem, with sequential consistency
1501+ 1-element Vector{Int64}:
1502+ 1
1503+
1504+ julia> @atomiconce :monotonic mem[1] = [2] # set the first value of mem to [2], if unset, with monotonic
1505+ false
1506+
1507+ julia> @atomic mem[1]
1508+ 1-element Vector{Int64}:
1509+ 1
1510+ ```
1511+
13601512!!! compat "Julia 1.11"
1361- This functionality requires at least Julia 1.11.
1513+ Atomic fields functionality requires at least Julia 1.11.
1514+
1515+ !!! compat "Julia 1.12"
1516+ Atomic reference functionality requires at least Julia 1.12.
13621517"""
13631518macro atomiconce (success_order, fail_order, ex)
13641519 fail_order isa QuoteNode || (fail_order = esc (fail_order))
@@ -1376,7 +1531,12 @@ function make_atomiconce(success_order, fail_order, ex)
13761531 @nospecialize
13771532 is_expr (ex, :(= ), 2 ) || error (" @atomiconce expression missing assignment" )
13781533 l, val = ex. args[1 ], esc (ex. args[2 ])
1379- is_expr (l, :., 2 ) || error (" @atomiconce expression missing field access" )
1380- ll, lr = esc (l. args[1 ]), esc (l. args[2 ])
1381- return :(setpropertyonce! ($ ll, $ lr, $ val, $ success_order, $ fail_order))
1534+ if is_expr (l, :., 2 )
1535+ ll, lr = esc (l. args[1 ]), esc (l. args[2 ])
1536+ return :(setpropertyonce! ($ ll, $ lr, $ val, $ success_order, $ fail_order))
1537+ elseif is_expr (l, :ref )
1538+ x, idcs = esc (l. args[1 ]), map (esc, l. args[2 : end ])
1539+ return :(setindexonce_atomic! ($ x, $ success_order, $ fail_order, $ val, $ (idcs... )))
1540+ end
1541+ error (" @atomiconce expression missing field access or indexing" )
13821542end
0 commit comments