diff options
| author | kennytm <kennytm@gmail.com> | 2018-04-05 16:51:16 +0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2018-04-05 16:51:16 +0800 |
| commit | 920249abdd974caf4f0ea4ddbe068848beb0f1f2 (patch) | |
| tree | bf453510c153d60b4c81a64829413ab5ead61e90 /src/libcore/sync | |
| parent | 4bf76d6745046af32d0c0f33484aa15bab1c0e2c (diff) | |
| parent | 0f5e4191632de4bbc1ef4ef2be26b517861cbff0 (diff) | |
| download | rust-920249abdd974caf4f0ea4ddbe068848beb0f1f2.tar.gz rust-920249abdd974caf4f0ea4ddbe068848beb0f1f2.zip | |
Rollup merge of #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 #48384 with `x.update_and_fetch(|x| x.max(y))`. 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.
Diffstat (limited to 'src/libcore/sync')
| -rw-r--r-- | src/libcore/sync/atomic.rs | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/src/libcore/sync/atomic.rs b/src/libcore/sync/atomic.rs index d934706be67..d336934ec72 100644 --- a/src/libcore/sync/atomic.rs +++ b/src/libcore/sync/atomic.rs @@ -952,6 +952,7 @@ macro_rules! atomic_int { $stable_nand:meta, $s_int_type:expr, $int_ref:expr, $extra_feature:expr, + $min_fn:ident, $max_fn:ident, $int_type:ident $atomic_type:ident $atomic_init:ident) => { /// An integer type which can be safely shared between threads. /// @@ -1421,6 +1422,128 @@ assert_eq!(foo.load(Ordering::SeqCst), 0b011110); unsafe { atomic_xor(self.v.get(), val, order) } } } + + doc_comment! { + concat!("Fetches the value, and applies a function to it that returns an optional +new value. Returns a `Result` (`Ok(_)` if the function returned `Some(_)`, else `Err(_)`) of the +previous value. + +Note: This may call the function multiple times if the value has been changed from other threads in +the meantime, as long as the function returns `Some(_)`, but the function will have been applied +but once to the stored value. + +# Examples + +```rust +#![feature(no_more_cas)] +", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering}; + +let x = ", stringify!($atomic_type), "::new(7); +assert_eq!(x.fetch_update(|_| None, Ordering::SeqCst, Ordering::SeqCst), Err(7)); +assert_eq!(x.fetch_update(|x| Some(x + 1), Ordering::SeqCst, Ordering::SeqCst), Ok(7)); +assert_eq!(x.fetch_update(|x| Some(x + 1), Ordering::SeqCst, Ordering::SeqCst), Ok(8)); +assert_eq!(x.load(Ordering::SeqCst), 9); +```"), + #[inline] + #[unstable(feature = "no_more_cas", + reason = "no more CAS loops in user code", + issue = "48655")] + pub fn fetch_update<F>(&self, + mut f: F, + fetch_order: Ordering, + set_order: Ordering) -> Result<$int_type, $int_type> + where F: FnMut($int_type) -> Option<$int_type> { + let mut prev = self.load(fetch_order); + while let Some(next) = f(prev) { + match self.compare_exchange_weak(prev, next, set_order, fetch_order) { + x @ Ok(_) => return x, + Err(next_prev) => prev = next_prev + } + } + Err(prev) + } + } + + doc_comment! { + concat!("Maximum with the current value. + +Finds the maximum of the current value and the argument `val`, and +sets the new value to the result. + +Returns the previous value. + +# Examples + +``` +#![feature(atomic_min_max)] +", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering}; + +let foo = ", stringify!($atomic_type), "::new(23); +assert_eq!(foo.fetch_max(42, Ordering::SeqCst), 23); +assert_eq!(foo.load(Ordering::SeqCst), 42); +``` + +If you want to obtain the maximum value in one step, you can use the following: + +``` +#![feature(atomic_min_max)] +", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering}; + +let foo = ", stringify!($atomic_type), "::new(23); +let bar = 42; +let max_foo = foo.fetch_max(bar, Ordering::SeqCst).max(bar); +assert!(max_foo == 42); +```"), + #[inline] + #[unstable(feature = "atomic_min_max", + reason = "easier and faster min/max than writing manual CAS loop", + issue = "48655")] + pub fn fetch_max(&self, val: $int_type, order: Ordering) -> $int_type { + unsafe { $max_fn(self.v.get(), val, order) } + } + } + + doc_comment! { + concat!("Minimum with the current value. + +Finds the minimum of the current value and the argument `val`, and +sets the new value to the result. + +Returns the previous value. + +# Examples + +``` +#![feature(atomic_min_max)] +", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering}; + +let foo = ", stringify!($atomic_type), "::new(23); +assert_eq!(foo.fetch_min(42, Ordering::Relaxed), 23); +assert_eq!(foo.load(Ordering::Relaxed), 23); +assert_eq!(foo.fetch_min(22, Ordering::Relaxed), 23); +assert_eq!(foo.load(Ordering::Relaxed), 22); +``` + +If you want to obtain the minimum value in one step, you can use the following: + +``` +#![feature(atomic_min_max)] +", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering}; + +let foo = ", stringify!($atomic_type), "::new(23); +let bar = 12; +let min_foo = foo.fetch_min(bar, Ordering::SeqCst).min(bar); +assert_eq!(min_foo, 12); +```"), + #[inline] + #[unstable(feature = "atomic_min_max", + reason = "easier and faster min/max than writing manual CAS loop", + issue = "48655")] + pub fn fetch_min(&self, val: $int_type, order: Ordering) -> $int_type { + unsafe { $min_fn(self.v.get(), val, order) } + } + } + } } } @@ -1435,6 +1558,7 @@ atomic_int! { unstable(feature = "atomic_nand", issue = "13226"), "i8", "../../../std/primitive.i8.html", "#![feature(integer_atomics)]\n\n", + atomic_min, atomic_max, i8 AtomicI8 ATOMIC_I8_INIT } #[cfg(target_has_atomic = "8")] @@ -1447,6 +1571,7 @@ atomic_int! { unstable(feature = "atomic_nand", issue = "13226"), "u8", "../../../std/primitive.u8.html", "#![feature(integer_atomics)]\n\n", + atomic_umin, atomic_umax, u8 AtomicU8 ATOMIC_U8_INIT } #[cfg(target_has_atomic = "16")] @@ -1459,6 +1584,7 @@ atomic_int! { unstable(feature = "atomic_nand", issue = "13226"), "i16", "../../../std/primitive.i16.html", "#![feature(integer_atomics)]\n\n", + atomic_min, atomic_max, i16 AtomicI16 ATOMIC_I16_INIT } #[cfg(target_has_atomic = "16")] @@ -1471,6 +1597,7 @@ atomic_int! { unstable(feature = "atomic_nand", issue = "13226"), "u16", "../../../std/primitive.u16.html", "#![feature(integer_atomics)]\n\n", + atomic_umin, atomic_umax, u16 AtomicU16 ATOMIC_U16_INIT } #[cfg(target_has_atomic = "32")] @@ -1483,6 +1610,7 @@ atomic_int! { unstable(feature = "atomic_nand", issue = "13226"), "i32", "../../../std/primitive.i32.html", "#![feature(integer_atomics)]\n\n", + atomic_min, atomic_max, i32 AtomicI32 ATOMIC_I32_INIT } #[cfg(target_has_atomic = "32")] @@ -1495,6 +1623,7 @@ atomic_int! { unstable(feature = "atomic_nand", issue = "13226"), "u32", "../../../std/primitive.u32.html", "#![feature(integer_atomics)]\n\n", + atomic_umin, atomic_umax, u32 AtomicU32 ATOMIC_U32_INIT } #[cfg(target_has_atomic = "64")] @@ -1507,6 +1636,7 @@ atomic_int! { unstable(feature = "atomic_nand", issue = "13226"), "i64", "../../../std/primitive.i64.html", "#![feature(integer_atomics)]\n\n", + atomic_min, atomic_max, i64 AtomicI64 ATOMIC_I64_INIT } #[cfg(target_has_atomic = "64")] @@ -1519,6 +1649,7 @@ atomic_int! { unstable(feature = "atomic_nand", issue = "13226"), "u64", "../../../std/primitive.u64.html", "#![feature(integer_atomics)]\n\n", + atomic_umin, atomic_umax, u64 AtomicU64 ATOMIC_U64_INIT } #[cfg(target_has_atomic = "ptr")] @@ -1531,6 +1662,7 @@ atomic_int!{ unstable(feature = "atomic_nand", issue = "13226"), "isize", "../../../std/primitive.isize.html", "", + atomic_min, atomic_max, isize AtomicIsize ATOMIC_ISIZE_INIT } #[cfg(target_has_atomic = "ptr")] @@ -1543,6 +1675,7 @@ atomic_int!{ unstable(feature = "atomic_nand", issue = "13226"), "usize", "../../../std/primitive.usize.html", "", + atomic_umin, atomic_umax, usize AtomicUsize ATOMIC_USIZE_INIT } @@ -1720,6 +1853,58 @@ unsafe fn atomic_xor<T>(dst: *mut T, val: T, order: Ordering) -> T { } } +/// returns the max value (signed comparison) +#[inline] +unsafe fn atomic_max<T>(dst: *mut T, val: T, order: Ordering) -> T { + match order { + Acquire => intrinsics::atomic_max_acq(dst, val), + Release => intrinsics::atomic_max_rel(dst, val), + AcqRel => intrinsics::atomic_max_acqrel(dst, val), + Relaxed => intrinsics::atomic_max_relaxed(dst, val), + SeqCst => intrinsics::atomic_max(dst, val), + __Nonexhaustive => panic!("invalid memory ordering"), + } +} + +/// returns the min value (signed comparison) +#[inline] +unsafe fn atomic_min<T>(dst: *mut T, val: T, order: Ordering) -> T { + match order { + Acquire => intrinsics::atomic_min_acq(dst, val), + Release => intrinsics::atomic_min_rel(dst, val), + AcqRel => intrinsics::atomic_min_acqrel(dst, val), + Relaxed => intrinsics::atomic_min_relaxed(dst, val), + SeqCst => intrinsics::atomic_min(dst, val), + __Nonexhaustive => panic!("invalid memory ordering"), + } +} + +/// returns the max value (signed comparison) +#[inline] +unsafe fn atomic_umax<T>(dst: *mut T, val: T, order: Ordering) -> T { + match order { + Acquire => intrinsics::atomic_umax_acq(dst, val), + Release => intrinsics::atomic_umax_rel(dst, val), + AcqRel => intrinsics::atomic_umax_acqrel(dst, val), + Relaxed => intrinsics::atomic_umax_relaxed(dst, val), + SeqCst => intrinsics::atomic_umax(dst, val), + __Nonexhaustive => panic!("invalid memory ordering"), + } +} + +/// returns the min value (signed comparison) +#[inline] +unsafe fn atomic_umin<T>(dst: *mut T, val: T, order: Ordering) -> T { + match order { + Acquire => intrinsics::atomic_umin_acq(dst, val), + Release => intrinsics::atomic_umin_rel(dst, val), + AcqRel => intrinsics::atomic_umin_acqrel(dst, val), + Relaxed => intrinsics::atomic_umin_relaxed(dst, val), + SeqCst => intrinsics::atomic_umin(dst, val), + __Nonexhaustive => panic!("invalid memory ordering"), + } +} + /// An atomic fence. /// /// Depending on the specified order, a fence prevents the compiler and CPU from |
