Skip to content

Commit 0f5e419

Browse files
committed
Add a generic CAS loop to std::sync::Atomic*
This adds a new method to all numeric `Atomic*` types with a safe compare-and-set loop, so users will no longer need to write their own, except in *very* strange circumstances. This solves #48384 with `x.fetch_max(_)`/`x.fetch_min(_)`. It also relates to #48655 (which I misuse as tracking issue for now). *note* This *might* need a crater run because the functions could clash with third party extension traits.
1 parent 5e4603f commit 0f5e419

File tree

1 file changed

+185
-0
lines changed

1 file changed

+185
-0
lines changed

src/libcore/sync/atomic.rs

+185
Original file line numberDiff line numberDiff line change
@@ -949,6 +949,7 @@ macro_rules! atomic_int {
949949
$stable_nand:meta,
950950
$s_int_type:expr, $int_ref:expr,
951951
$extra_feature:expr,
952+
$min_fn:ident, $max_fn:ident,
952953
$int_type:ident $atomic_type:ident $atomic_init:ident) => {
953954
/// An integer type which can be safely shared between threads.
954955
///
@@ -1418,6 +1419,128 @@ assert_eq!(foo.load(Ordering::SeqCst), 0b011110);
14181419
unsafe { atomic_xor(self.v.get(), val, order) }
14191420
}
14201421
}
1422+
1423+
doc_comment! {
1424+
concat!("Fetches the value, and applies a function to it that returns an optional
1425+
new value. Returns a `Result` (`Ok(_)` if the function returned `Some(_)`, else `Err(_)`) of the
1426+
previous value.
1427+
1428+
Note: This may call the function multiple times if the value has been changed from other threads in
1429+
the meantime, as long as the function returns `Some(_)`, but the function will have been applied
1430+
but once to the stored value.
1431+
1432+
# Examples
1433+
1434+
```rust
1435+
#![feature(no_more_cas)]
1436+
", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};
1437+
1438+
let x = ", stringify!($atomic_type), "::new(7);
1439+
assert_eq!(x.fetch_update(|_| None, Ordering::SeqCst, Ordering::SeqCst), Err(7));
1440+
assert_eq!(x.fetch_update(|x| Some(x + 1), Ordering::SeqCst, Ordering::SeqCst), Ok(7));
1441+
assert_eq!(x.fetch_update(|x| Some(x + 1), Ordering::SeqCst, Ordering::SeqCst), Ok(8));
1442+
assert_eq!(x.load(Ordering::SeqCst), 9);
1443+
```"),
1444+
#[inline]
1445+
#[unstable(feature = "no_more_cas",
1446+
reason = "no more CAS loops in user code",
1447+
issue = "48655")]
1448+
pub fn fetch_update<F>(&self,
1449+
mut f: F,
1450+
fetch_order: Ordering,
1451+
set_order: Ordering) -> Result<$int_type, $int_type>
1452+
where F: FnMut($int_type) -> Option<$int_type> {
1453+
let mut prev = self.load(fetch_order);
1454+
while let Some(next) = f(prev) {
1455+
match self.compare_exchange_weak(prev, next, set_order, fetch_order) {
1456+
x @ Ok(_) => return x,
1457+
Err(next_prev) => prev = next_prev
1458+
}
1459+
}
1460+
Err(prev)
1461+
}
1462+
}
1463+
1464+
doc_comment! {
1465+
concat!("Maximum with the current value.
1466+
1467+
Finds the maximum of the current value and the argument `val`, and
1468+
sets the new value to the result.
1469+
1470+
Returns the previous value.
1471+
1472+
# Examples
1473+
1474+
```
1475+
#![feature(atomic_min_max)]
1476+
", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};
1477+
1478+
let foo = ", stringify!($atomic_type), "::new(23);
1479+
assert_eq!(foo.fetch_max(42, Ordering::SeqCst), 23);
1480+
assert_eq!(foo.load(Ordering::SeqCst), 42);
1481+
```
1482+
1483+
If you want to obtain the maximum value in one step, you can use the following:
1484+
1485+
```
1486+
#![feature(atomic_min_max)]
1487+
", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};
1488+
1489+
let foo = ", stringify!($atomic_type), "::new(23);
1490+
let bar = 42;
1491+
let max_foo = foo.fetch_max(bar, Ordering::SeqCst).max(bar);
1492+
assert!(max_foo == 42);
1493+
```"),
1494+
#[inline]
1495+
#[unstable(feature = "atomic_min_max",
1496+
reason = "easier and faster min/max than writing manual CAS loop",
1497+
issue = "48655")]
1498+
pub fn fetch_max(&self, val: $int_type, order: Ordering) -> $int_type {
1499+
unsafe { $max_fn(self.v.get(), val, order) }
1500+
}
1501+
}
1502+
1503+
doc_comment! {
1504+
concat!("Minimum with the current value.
1505+
1506+
Finds the minimum of the current value and the argument `val`, and
1507+
sets the new value to the result.
1508+
1509+
Returns the previous value.
1510+
1511+
# Examples
1512+
1513+
```
1514+
#![feature(atomic_min_max)]
1515+
", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};
1516+
1517+
let foo = ", stringify!($atomic_type), "::new(23);
1518+
assert_eq!(foo.fetch_min(42, Ordering::Relaxed), 23);
1519+
assert_eq!(foo.load(Ordering::Relaxed), 23);
1520+
assert_eq!(foo.fetch_min(22, Ordering::Relaxed), 23);
1521+
assert_eq!(foo.load(Ordering::Relaxed), 22);
1522+
```
1523+
1524+
If you want to obtain the minimum value in one step, you can use the following:
1525+
1526+
```
1527+
#![feature(atomic_min_max)]
1528+
", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};
1529+
1530+
let foo = ", stringify!($atomic_type), "::new(23);
1531+
let bar = 12;
1532+
let min_foo = foo.fetch_min(bar, Ordering::SeqCst).min(bar);
1533+
assert_eq!(min_foo, 12);
1534+
```"),
1535+
#[inline]
1536+
#[unstable(feature = "atomic_min_max",
1537+
reason = "easier and faster min/max than writing manual CAS loop",
1538+
issue = "48655")]
1539+
pub fn fetch_min(&self, val: $int_type, order: Ordering) -> $int_type {
1540+
unsafe { $min_fn(self.v.get(), val, order) }
1541+
}
1542+
}
1543+
14211544
}
14221545
}
14231546
}
@@ -1432,6 +1555,7 @@ atomic_int! {
14321555
unstable(feature = "atomic_nand", issue = "13226"),
14331556
"i8", "../../../std/primitive.i8.html",
14341557
"#![feature(integer_atomics)]\n\n",
1558+
atomic_min, atomic_max,
14351559
i8 AtomicI8 ATOMIC_I8_INIT
14361560
}
14371561
#[cfg(target_has_atomic = "8")]
@@ -1444,6 +1568,7 @@ atomic_int! {
14441568
unstable(feature = "atomic_nand", issue = "13226"),
14451569
"u8", "../../../std/primitive.u8.html",
14461570
"#![feature(integer_atomics)]\n\n",
1571+
atomic_umin, atomic_umax,
14471572
u8 AtomicU8 ATOMIC_U8_INIT
14481573
}
14491574
#[cfg(target_has_atomic = "16")]
@@ -1456,6 +1581,7 @@ atomic_int! {
14561581
unstable(feature = "atomic_nand", issue = "13226"),
14571582
"i16", "../../../std/primitive.i16.html",
14581583
"#![feature(integer_atomics)]\n\n",
1584+
atomic_min, atomic_max,
14591585
i16 AtomicI16 ATOMIC_I16_INIT
14601586
}
14611587
#[cfg(target_has_atomic = "16")]
@@ -1468,6 +1594,7 @@ atomic_int! {
14681594
unstable(feature = "atomic_nand", issue = "13226"),
14691595
"u16", "../../../std/primitive.u16.html",
14701596
"#![feature(integer_atomics)]\n\n",
1597+
atomic_umin, atomic_umax,
14711598
u16 AtomicU16 ATOMIC_U16_INIT
14721599
}
14731600
#[cfg(target_has_atomic = "32")]
@@ -1480,6 +1607,7 @@ atomic_int! {
14801607
unstable(feature = "atomic_nand", issue = "13226"),
14811608
"i32", "../../../std/primitive.i32.html",
14821609
"#![feature(integer_atomics)]\n\n",
1610+
atomic_min, atomic_max,
14831611
i32 AtomicI32 ATOMIC_I32_INIT
14841612
}
14851613
#[cfg(target_has_atomic = "32")]
@@ -1492,6 +1620,7 @@ atomic_int! {
14921620
unstable(feature = "atomic_nand", issue = "13226"),
14931621
"u32", "../../../std/primitive.u32.html",
14941622
"#![feature(integer_atomics)]\n\n",
1623+
atomic_umin, atomic_umax,
14951624
u32 AtomicU32 ATOMIC_U32_INIT
14961625
}
14971626
#[cfg(target_has_atomic = "64")]
@@ -1504,6 +1633,7 @@ atomic_int! {
15041633
unstable(feature = "atomic_nand", issue = "13226"),
15051634
"i64", "../../../std/primitive.i64.html",
15061635
"#![feature(integer_atomics)]\n\n",
1636+
atomic_min, atomic_max,
15071637
i64 AtomicI64 ATOMIC_I64_INIT
15081638
}
15091639
#[cfg(target_has_atomic = "64")]
@@ -1516,6 +1646,7 @@ atomic_int! {
15161646
unstable(feature = "atomic_nand", issue = "13226"),
15171647
"u64", "../../../std/primitive.u64.html",
15181648
"#![feature(integer_atomics)]\n\n",
1649+
atomic_umin, atomic_umax,
15191650
u64 AtomicU64 ATOMIC_U64_INIT
15201651
}
15211652
#[cfg(target_has_atomic = "ptr")]
@@ -1528,6 +1659,7 @@ atomic_int!{
15281659
unstable(feature = "atomic_nand", issue = "13226"),
15291660
"isize", "../../../std/primitive.isize.html",
15301661
"",
1662+
atomic_min, atomic_max,
15311663
isize AtomicIsize ATOMIC_ISIZE_INIT
15321664
}
15331665
#[cfg(target_has_atomic = "ptr")]
@@ -1540,6 +1672,7 @@ atomic_int!{
15401672
unstable(feature = "atomic_nand", issue = "13226"),
15411673
"usize", "../../../std/primitive.usize.html",
15421674
"",
1675+
atomic_umin, atomic_umax,
15431676
usize AtomicUsize ATOMIC_USIZE_INIT
15441677
}
15451678

@@ -1717,6 +1850,58 @@ unsafe fn atomic_xor<T>(dst: *mut T, val: T, order: Ordering) -> T {
17171850
}
17181851
}
17191852

1853+
/// returns the max value (signed comparison)
1854+
#[inline]
1855+
unsafe fn atomic_max<T>(dst: *mut T, val: T, order: Ordering) -> T {
1856+
match order {
1857+
Acquire => intrinsics::atomic_max_acq(dst, val),
1858+
Release => intrinsics::atomic_max_rel(dst, val),
1859+
AcqRel => intrinsics::atomic_max_acqrel(dst, val),
1860+
Relaxed => intrinsics::atomic_max_relaxed(dst, val),
1861+
SeqCst => intrinsics::atomic_max(dst, val),
1862+
__Nonexhaustive => panic!("invalid memory ordering"),
1863+
}
1864+
}
1865+
1866+
/// returns the min value (signed comparison)
1867+
#[inline]
1868+
unsafe fn atomic_min<T>(dst: *mut T, val: T, order: Ordering) -> T {
1869+
match order {
1870+
Acquire => intrinsics::atomic_min_acq(dst, val),
1871+
Release => intrinsics::atomic_min_rel(dst, val),
1872+
AcqRel => intrinsics::atomic_min_acqrel(dst, val),
1873+
Relaxed => intrinsics::atomic_min_relaxed(dst, val),
1874+
SeqCst => intrinsics::atomic_min(dst, val),
1875+
__Nonexhaustive => panic!("invalid memory ordering"),
1876+
}
1877+
}
1878+
1879+
/// returns the max value (signed comparison)
1880+
#[inline]
1881+
unsafe fn atomic_umax<T>(dst: *mut T, val: T, order: Ordering) -> T {
1882+
match order {
1883+
Acquire => intrinsics::atomic_umax_acq(dst, val),
1884+
Release => intrinsics::atomic_umax_rel(dst, val),
1885+
AcqRel => intrinsics::atomic_umax_acqrel(dst, val),
1886+
Relaxed => intrinsics::atomic_umax_relaxed(dst, val),
1887+
SeqCst => intrinsics::atomic_umax(dst, val),
1888+
__Nonexhaustive => panic!("invalid memory ordering"),
1889+
}
1890+
}
1891+
1892+
/// returns the min value (signed comparison)
1893+
#[inline]
1894+
unsafe fn atomic_umin<T>(dst: *mut T, val: T, order: Ordering) -> T {
1895+
match order {
1896+
Acquire => intrinsics::atomic_umin_acq(dst, val),
1897+
Release => intrinsics::atomic_umin_rel(dst, val),
1898+
AcqRel => intrinsics::atomic_umin_acqrel(dst, val),
1899+
Relaxed => intrinsics::atomic_umin_relaxed(dst, val),
1900+
SeqCst => intrinsics::atomic_umin(dst, val),
1901+
__Nonexhaustive => panic!("invalid memory ordering"),
1902+
}
1903+
}
1904+
17201905
/// An atomic fence.
17211906
///
17221907
/// Depending on the specified order, a fence prevents the compiler and CPU from

0 commit comments

Comments
 (0)