Skip to content

Commit 920249a

Browse files
authored
Rollup merge of rust-lang#48658 - llogiq:no-more-cas, r=kennytm
Add a generic CAS loop to std::sync::Atomic* This adds two new methods to both `AtomicIsize` and `AtomicUsize` with optimized safe compare-and-set loops, so users will no longer need to write their own, except in *very* strange circumstances. `update_and_fetch` will apply the function and return its result, whereas `fetch_and_update` will apply the function and return the previous value. This solves rust-lang#48384 with `x.update_and_fetch(|x| x.max(y))`. It also relates to rust-lang#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.
2 parents 4bf76d6 + 0f5e419 commit 920249a

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
@@ -952,6 +952,7 @@ macro_rules! atomic_int {
952952
$stable_nand:meta,
953953
$s_int_type:expr, $int_ref:expr,
954954
$extra_feature:expr,
955+
$min_fn:ident, $max_fn:ident,
955956
$int_type:ident $atomic_type:ident $atomic_init:ident) => {
956957
/// An integer type which can be safely shared between threads.
957958
///
@@ -1421,6 +1422,128 @@ assert_eq!(foo.load(Ordering::SeqCst), 0b011110);
14211422
unsafe { atomic_xor(self.v.get(), val, order) }
14221423
}
14231424
}
1425+
1426+
doc_comment! {
1427+
concat!("Fetches the value, and applies a function to it that returns an optional
1428+
new value. Returns a `Result` (`Ok(_)` if the function returned `Some(_)`, else `Err(_)`) of the
1429+
previous value.
1430+
1431+
Note: This may call the function multiple times if the value has been changed from other threads in
1432+
the meantime, as long as the function returns `Some(_)`, but the function will have been applied
1433+
but once to the stored value.
1434+
1435+
# Examples
1436+
1437+
```rust
1438+
#![feature(no_more_cas)]
1439+
", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};
1440+
1441+
let x = ", stringify!($atomic_type), "::new(7);
1442+
assert_eq!(x.fetch_update(|_| None, Ordering::SeqCst, Ordering::SeqCst), Err(7));
1443+
assert_eq!(x.fetch_update(|x| Some(x + 1), Ordering::SeqCst, Ordering::SeqCst), Ok(7));
1444+
assert_eq!(x.fetch_update(|x| Some(x + 1), Ordering::SeqCst, Ordering::SeqCst), Ok(8));
1445+
assert_eq!(x.load(Ordering::SeqCst), 9);
1446+
```"),
1447+
#[inline]
1448+
#[unstable(feature = "no_more_cas",
1449+
reason = "no more CAS loops in user code",
1450+
issue = "48655")]
1451+
pub fn fetch_update<F>(&self,
1452+
mut f: F,
1453+
fetch_order: Ordering,
1454+
set_order: Ordering) -> Result<$int_type, $int_type>
1455+
where F: FnMut($int_type) -> Option<$int_type> {
1456+
let mut prev = self.load(fetch_order);
1457+
while let Some(next) = f(prev) {
1458+
match self.compare_exchange_weak(prev, next, set_order, fetch_order) {
1459+
x @ Ok(_) => return x,
1460+
Err(next_prev) => prev = next_prev
1461+
}
1462+
}
1463+
Err(prev)
1464+
}
1465+
}
1466+
1467+
doc_comment! {
1468+
concat!("Maximum with the current value.
1469+
1470+
Finds the maximum of the current value and the argument `val`, and
1471+
sets the new value to the result.
1472+
1473+
Returns the previous value.
1474+
1475+
# Examples
1476+
1477+
```
1478+
#![feature(atomic_min_max)]
1479+
", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};
1480+
1481+
let foo = ", stringify!($atomic_type), "::new(23);
1482+
assert_eq!(foo.fetch_max(42, Ordering::SeqCst), 23);
1483+
assert_eq!(foo.load(Ordering::SeqCst), 42);
1484+
```
1485+
1486+
If you want to obtain the maximum value in one step, you can use the following:
1487+
1488+
```
1489+
#![feature(atomic_min_max)]
1490+
", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};
1491+
1492+
let foo = ", stringify!($atomic_type), "::new(23);
1493+
let bar = 42;
1494+
let max_foo = foo.fetch_max(bar, Ordering::SeqCst).max(bar);
1495+
assert!(max_foo == 42);
1496+
```"),
1497+
#[inline]
1498+
#[unstable(feature = "atomic_min_max",
1499+
reason = "easier and faster min/max than writing manual CAS loop",
1500+
issue = "48655")]
1501+
pub fn fetch_max(&self, val: $int_type, order: Ordering) -> $int_type {
1502+
unsafe { $max_fn(self.v.get(), val, order) }
1503+
}
1504+
}
1505+
1506+
doc_comment! {
1507+
concat!("Minimum with the current value.
1508+
1509+
Finds the minimum of the current value and the argument `val`, and
1510+
sets the new value to the result.
1511+
1512+
Returns the previous value.
1513+
1514+
# Examples
1515+
1516+
```
1517+
#![feature(atomic_min_max)]
1518+
", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};
1519+
1520+
let foo = ", stringify!($atomic_type), "::new(23);
1521+
assert_eq!(foo.fetch_min(42, Ordering::Relaxed), 23);
1522+
assert_eq!(foo.load(Ordering::Relaxed), 23);
1523+
assert_eq!(foo.fetch_min(22, Ordering::Relaxed), 23);
1524+
assert_eq!(foo.load(Ordering::Relaxed), 22);
1525+
```
1526+
1527+
If you want to obtain the minimum value in one step, you can use the following:
1528+
1529+
```
1530+
#![feature(atomic_min_max)]
1531+
", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};
1532+
1533+
let foo = ", stringify!($atomic_type), "::new(23);
1534+
let bar = 12;
1535+
let min_foo = foo.fetch_min(bar, Ordering::SeqCst).min(bar);
1536+
assert_eq!(min_foo, 12);
1537+
```"),
1538+
#[inline]
1539+
#[unstable(feature = "atomic_min_max",
1540+
reason = "easier and faster min/max than writing manual CAS loop",
1541+
issue = "48655")]
1542+
pub fn fetch_min(&self, val: $int_type, order: Ordering) -> $int_type {
1543+
unsafe { $min_fn(self.v.get(), val, order) }
1544+
}
1545+
}
1546+
14241547
}
14251548
}
14261549
}
@@ -1435,6 +1558,7 @@ atomic_int! {
14351558
unstable(feature = "atomic_nand", issue = "13226"),
14361559
"i8", "../../../std/primitive.i8.html",
14371560
"#![feature(integer_atomics)]\n\n",
1561+
atomic_min, atomic_max,
14381562
i8 AtomicI8 ATOMIC_I8_INIT
14391563
}
14401564
#[cfg(target_has_atomic = "8")]
@@ -1447,6 +1571,7 @@ atomic_int! {
14471571
unstable(feature = "atomic_nand", issue = "13226"),
14481572
"u8", "../../../std/primitive.u8.html",
14491573
"#![feature(integer_atomics)]\n\n",
1574+
atomic_umin, atomic_umax,
14501575
u8 AtomicU8 ATOMIC_U8_INIT
14511576
}
14521577
#[cfg(target_has_atomic = "16")]
@@ -1459,6 +1584,7 @@ atomic_int! {
14591584
unstable(feature = "atomic_nand", issue = "13226"),
14601585
"i16", "../../../std/primitive.i16.html",
14611586
"#![feature(integer_atomics)]\n\n",
1587+
atomic_min, atomic_max,
14621588
i16 AtomicI16 ATOMIC_I16_INIT
14631589
}
14641590
#[cfg(target_has_atomic = "16")]
@@ -1471,6 +1597,7 @@ atomic_int! {
14711597
unstable(feature = "atomic_nand", issue = "13226"),
14721598
"u16", "../../../std/primitive.u16.html",
14731599
"#![feature(integer_atomics)]\n\n",
1600+
atomic_umin, atomic_umax,
14741601
u16 AtomicU16 ATOMIC_U16_INIT
14751602
}
14761603
#[cfg(target_has_atomic = "32")]
@@ -1483,6 +1610,7 @@ atomic_int! {
14831610
unstable(feature = "atomic_nand", issue = "13226"),
14841611
"i32", "../../../std/primitive.i32.html",
14851612
"#![feature(integer_atomics)]\n\n",
1613+
atomic_min, atomic_max,
14861614
i32 AtomicI32 ATOMIC_I32_INIT
14871615
}
14881616
#[cfg(target_has_atomic = "32")]
@@ -1495,6 +1623,7 @@ atomic_int! {
14951623
unstable(feature = "atomic_nand", issue = "13226"),
14961624
"u32", "../../../std/primitive.u32.html",
14971625
"#![feature(integer_atomics)]\n\n",
1626+
atomic_umin, atomic_umax,
14981627
u32 AtomicU32 ATOMIC_U32_INIT
14991628
}
15001629
#[cfg(target_has_atomic = "64")]
@@ -1507,6 +1636,7 @@ atomic_int! {
15071636
unstable(feature = "atomic_nand", issue = "13226"),
15081637
"i64", "../../../std/primitive.i64.html",
15091638
"#![feature(integer_atomics)]\n\n",
1639+
atomic_min, atomic_max,
15101640
i64 AtomicI64 ATOMIC_I64_INIT
15111641
}
15121642
#[cfg(target_has_atomic = "64")]
@@ -1519,6 +1649,7 @@ atomic_int! {
15191649
unstable(feature = "atomic_nand", issue = "13226"),
15201650
"u64", "../../../std/primitive.u64.html",
15211651
"#![feature(integer_atomics)]\n\n",
1652+
atomic_umin, atomic_umax,
15221653
u64 AtomicU64 ATOMIC_U64_INIT
15231654
}
15241655
#[cfg(target_has_atomic = "ptr")]
@@ -1531,6 +1662,7 @@ atomic_int!{
15311662
unstable(feature = "atomic_nand", issue = "13226"),
15321663
"isize", "../../../std/primitive.isize.html",
15331664
"",
1665+
atomic_min, atomic_max,
15341666
isize AtomicIsize ATOMIC_ISIZE_INIT
15351667
}
15361668
#[cfg(target_has_atomic = "ptr")]
@@ -1543,6 +1675,7 @@ atomic_int!{
15431675
unstable(feature = "atomic_nand", issue = "13226"),
15441676
"usize", "../../../std/primitive.usize.html",
15451677
"",
1678+
atomic_umin, atomic_umax,
15461679
usize AtomicUsize ATOMIC_USIZE_INIT
15471680
}
15481681

@@ -1720,6 +1853,58 @@ unsafe fn atomic_xor<T>(dst: *mut T, val: T, order: Ordering) -> T {
17201853
}
17211854
}
17221855

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

0 commit comments

Comments
 (0)