mirror of
https://github.com/rust-lang/rust.git
synced 2025-05-02 21:17:39 +00:00
Implement AtomicT::update
& AtomicT::try_update
This commit is contained in:
parent
dee7d0e730
commit
c06ed545df
@ -1164,7 +1164,7 @@ impl AtomicBool {
|
||||
///
|
||||
/// # Considerations
|
||||
///
|
||||
/// This method is not magic; it is not provided by the hardware.
|
||||
/// This method is not magic; it is not provided by the hardware.
|
||||
/// It is implemented in terms of [`AtomicBool::compare_exchange_weak`], and suffers from the same drawbacks.
|
||||
/// In particular, this method will not circumvent the [ABA Problem].
|
||||
///
|
||||
@ -1203,6 +1203,125 @@ impl AtomicBool {
|
||||
}
|
||||
Err(prev)
|
||||
}
|
||||
|
||||
/// Fetches the value, and applies a function to it that returns an optional
|
||||
/// new value. Returns a `Result` of `Ok(previous_value)` if the function
|
||||
/// returned `Some(_)`, else `Err(previous_value)`.
|
||||
///
|
||||
/// See also: [`update`](`AtomicBool::update`).
|
||||
///
|
||||
/// 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 only once to
|
||||
/// the stored value.
|
||||
///
|
||||
/// `try_update` takes two [`Ordering`] arguments to describe the memory
|
||||
/// ordering of this operation. The first describes the required ordering for
|
||||
/// when the operation finally succeeds while the second describes the
|
||||
/// required ordering for loads. These correspond to the success and failure
|
||||
/// orderings of [`AtomicBool::compare_exchange`] respectively.
|
||||
///
|
||||
/// Using [`Acquire`] as success ordering makes the store part of this
|
||||
/// operation [`Relaxed`], and using [`Release`] makes the final successful
|
||||
/// load [`Relaxed`]. The (failed) load ordering can only be [`SeqCst`],
|
||||
/// [`Acquire`] or [`Relaxed`].
|
||||
///
|
||||
/// **Note:** This method is only available on platforms that support atomic
|
||||
/// operations on `u8`.
|
||||
///
|
||||
/// # Considerations
|
||||
///
|
||||
/// This method is not magic; it is not provided by the hardware.
|
||||
/// It is implemented in terms of [`AtomicBool::compare_exchange_weak`], and suffers from the same drawbacks.
|
||||
/// In particular, this method will not circumvent the [ABA Problem].
|
||||
///
|
||||
/// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// #![feature(atomic_try_update)]
|
||||
/// use std::sync::atomic::{AtomicBool, Ordering};
|
||||
///
|
||||
/// let x = AtomicBool::new(false);
|
||||
/// assert_eq!(x.try_update(Ordering::SeqCst, Ordering::SeqCst, |_| None), Err(false));
|
||||
/// assert_eq!(x.try_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(!x)), Ok(false));
|
||||
/// assert_eq!(x.try_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(!x)), Ok(true));
|
||||
/// assert_eq!(x.load(Ordering::SeqCst), false);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[unstable(feature = "atomic_try_update", issue = "135894")]
|
||||
#[cfg(target_has_atomic = "8")]
|
||||
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
|
||||
pub fn try_update(
|
||||
&self,
|
||||
set_order: Ordering,
|
||||
fetch_order: Ordering,
|
||||
f: impl FnMut(bool) -> Option<bool>,
|
||||
) -> Result<bool, bool> {
|
||||
// FIXME(atomic_try_update): this is currently an unstable alias to `fetch_update`;
|
||||
// when stabilizing, turn `fetch_update` into a deprecated alias to `try_update`.
|
||||
self.fetch_update(set_order, fetch_order, f)
|
||||
}
|
||||
|
||||
/// Fetches the value, applies a function to it that it return a new value.
|
||||
/// The new value is stored and the old value is returned.
|
||||
///
|
||||
/// See also: [`try_update`](`AtomicBool::try_update`).
|
||||
///
|
||||
/// Note: This may call the function multiple times if the value has been changed from other threads in
|
||||
/// the meantime, but the function will have been applied only once to the stored value.
|
||||
///
|
||||
/// `update` takes two [`Ordering`] arguments to describe the memory
|
||||
/// ordering of this operation. The first describes the required ordering for
|
||||
/// when the operation finally succeeds while the second describes the
|
||||
/// required ordering for loads. These correspond to the success and failure
|
||||
/// orderings of [`AtomicBool::compare_exchange`] respectively.
|
||||
///
|
||||
/// Using [`Acquire`] as success ordering makes the store part
|
||||
/// of this operation [`Relaxed`], and using [`Release`] makes the final successful load
|
||||
/// [`Relaxed`]. The (failed) load ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`].
|
||||
///
|
||||
/// **Note:** This method is only available on platforms that support atomic operations on `u8`.
|
||||
///
|
||||
/// # Considerations
|
||||
///
|
||||
/// This method is not magic; it is not provided by the hardware.
|
||||
/// It is implemented in terms of [`AtomicBool::compare_exchange_weak`], and suffers from the same drawbacks.
|
||||
/// In particular, this method will not circumvent the [ABA Problem].
|
||||
///
|
||||
/// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// #![feature(atomic_try_update)]
|
||||
///
|
||||
/// use std::sync::atomic::{AtomicBool, Ordering};
|
||||
///
|
||||
/// let x = AtomicBool::new(false);
|
||||
/// assert_eq!(x.update(Ordering::SeqCst, Ordering::SeqCst, |x| !x), false);
|
||||
/// assert_eq!(x.update(Ordering::SeqCst, Ordering::SeqCst, |x| !x), true);
|
||||
/// assert_eq!(x.load(Ordering::SeqCst), false);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[unstable(feature = "atomic_try_update", issue = "135894")]
|
||||
#[cfg(target_has_atomic = "8")]
|
||||
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
|
||||
pub fn update(
|
||||
&self,
|
||||
set_order: Ordering,
|
||||
fetch_order: Ordering,
|
||||
mut f: impl FnMut(bool) -> bool,
|
||||
) -> bool {
|
||||
let mut prev = self.load(fetch_order);
|
||||
loop {
|
||||
match self.compare_exchange_weak(prev, f(prev), set_order, fetch_order) {
|
||||
Ok(x) => break x,
|
||||
Err(next_prev) => prev = next_prev,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_has_atomic_load_store = "ptr")]
|
||||
@ -1684,7 +1803,7 @@ impl<T> AtomicPtr<T> {
|
||||
///
|
||||
/// # Considerations
|
||||
///
|
||||
/// This method is not magic; it is not provided by the hardware.
|
||||
/// This method is not magic; it is not provided by the hardware.
|
||||
/// It is implemented in terms of [`AtomicPtr::compare_exchange_weak`], and suffers from the same drawbacks.
|
||||
/// In particular, this method will not circumvent the [ABA Problem].
|
||||
///
|
||||
@ -1732,6 +1851,137 @@ impl<T> AtomicPtr<T> {
|
||||
}
|
||||
Err(prev)
|
||||
}
|
||||
/// Fetches the value, and applies a function to it that returns an optional
|
||||
/// new value. Returns a `Result` of `Ok(previous_value)` if the function
|
||||
/// returned `Some(_)`, else `Err(previous_value)`.
|
||||
///
|
||||
/// See also: [`update`](`AtomicPtr::update`).
|
||||
///
|
||||
/// 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 only once to
|
||||
/// the stored value.
|
||||
///
|
||||
/// `try_update` takes two [`Ordering`] arguments to describe the memory
|
||||
/// ordering of this operation. The first describes the required ordering for
|
||||
/// when the operation finally succeeds while the second describes the
|
||||
/// required ordering for loads. These correspond to the success and failure
|
||||
/// orderings of [`AtomicPtr::compare_exchange`] respectively.
|
||||
///
|
||||
/// Using [`Acquire`] as success ordering makes the store part of this
|
||||
/// operation [`Relaxed`], and using [`Release`] makes the final successful
|
||||
/// load [`Relaxed`]. The (failed) load ordering can only be [`SeqCst`],
|
||||
/// [`Acquire`] or [`Relaxed`].
|
||||
///
|
||||
/// **Note:** This method is only available on platforms that support atomic
|
||||
/// operations on pointers.
|
||||
///
|
||||
/// # Considerations
|
||||
///
|
||||
/// This method is not magic; it is not provided by the hardware.
|
||||
/// It is implemented in terms of [`AtomicPtr::compare_exchange_weak`], and suffers from the same drawbacks.
|
||||
/// In particular, this method will not circumvent the [ABA Problem].
|
||||
///
|
||||
/// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// #![feature(atomic_try_update)]
|
||||
/// use std::sync::atomic::{AtomicPtr, Ordering};
|
||||
///
|
||||
/// let ptr: *mut _ = &mut 5;
|
||||
/// let some_ptr = AtomicPtr::new(ptr);
|
||||
///
|
||||
/// let new: *mut _ = &mut 10;
|
||||
/// assert_eq!(some_ptr.try_update(Ordering::SeqCst, Ordering::SeqCst, |_| None), Err(ptr));
|
||||
/// let result = some_ptr.try_update(Ordering::SeqCst, Ordering::SeqCst, |x| {
|
||||
/// if x == ptr {
|
||||
/// Some(new)
|
||||
/// } else {
|
||||
/// None
|
||||
/// }
|
||||
/// });
|
||||
/// assert_eq!(result, Ok(ptr));
|
||||
/// assert_eq!(some_ptr.load(Ordering::SeqCst), new);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[unstable(feature = "atomic_try_update", issue = "135894")]
|
||||
#[cfg(target_has_atomic = "ptr")]
|
||||
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
|
||||
pub fn try_update(
|
||||
&self,
|
||||
set_order: Ordering,
|
||||
fetch_order: Ordering,
|
||||
f: impl FnMut(*mut T) -> Option<*mut T>,
|
||||
) -> Result<*mut T, *mut T> {
|
||||
// FIXME(atomic_try_update): this is currently an unstable alias to `fetch_update`;
|
||||
// when stabilizing, turn `fetch_update` into a deprecated alias to `try_update`.
|
||||
self.fetch_update(set_order, fetch_order, f)
|
||||
}
|
||||
|
||||
/// Fetches the value, applies a function to it that it return a new value.
|
||||
/// The new value is stored and the old value is returned.
|
||||
///
|
||||
/// See also: [`try_update`](`AtomicPtr::try_update`).
|
||||
///
|
||||
/// Note: This may call the function multiple times if the value has been changed from other threads in
|
||||
/// the meantime, but the function will have been applied only once to the stored value.
|
||||
///
|
||||
/// `update` takes two [`Ordering`] arguments to describe the memory
|
||||
/// ordering of this operation. The first describes the required ordering for
|
||||
/// when the operation finally succeeds while the second describes the
|
||||
/// required ordering for loads. These correspond to the success and failure
|
||||
/// orderings of [`AtomicPtr::compare_exchange`] respectively.
|
||||
///
|
||||
/// Using [`Acquire`] as success ordering makes the store part
|
||||
/// of this operation [`Relaxed`], and using [`Release`] makes the final successful load
|
||||
/// [`Relaxed`]. The (failed) load ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`].
|
||||
///
|
||||
/// **Note:** This method is only available on platforms that support atomic
|
||||
/// operations on pointers.
|
||||
///
|
||||
/// # Considerations
|
||||
///
|
||||
/// This method is not magic; it is not provided by the hardware.
|
||||
/// It is implemented in terms of [`AtomicPtr::compare_exchange_weak`], and suffers from the same drawbacks.
|
||||
/// In particular, this method will not circumvent the [ABA Problem].
|
||||
///
|
||||
/// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// #![feature(atomic_try_update)]
|
||||
///
|
||||
/// use std::sync::atomic::{AtomicPtr, Ordering};
|
||||
///
|
||||
/// let ptr: *mut _ = &mut 5;
|
||||
/// let some_ptr = AtomicPtr::new(ptr);
|
||||
///
|
||||
/// let new: *mut _ = &mut 10;
|
||||
/// let result = some_ptr.update(Ordering::SeqCst, Ordering::SeqCst, |_| new);
|
||||
/// assert_eq!(result, ptr);
|
||||
/// assert_eq!(some_ptr.load(Ordering::SeqCst), new);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[unstable(feature = "atomic_try_update", issue = "135894")]
|
||||
#[cfg(target_has_atomic = "8")]
|
||||
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
|
||||
pub fn update(
|
||||
&self,
|
||||
set_order: Ordering,
|
||||
fetch_order: Ordering,
|
||||
mut f: impl FnMut(*mut T) -> *mut T,
|
||||
) -> *mut T {
|
||||
let mut prev = self.load(fetch_order);
|
||||
loop {
|
||||
match self.compare_exchange_weak(prev, f(prev), set_order, fetch_order) {
|
||||
Ok(x) => break x,
|
||||
Err(next_prev) => prev = next_prev,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Offsets the pointer's address by adding `val` (in units of `T`),
|
||||
/// returning the previous pointer.
|
||||
@ -2875,7 +3125,7 @@ macro_rules! atomic_int {
|
||||
///
|
||||
/// # Considerations
|
||||
///
|
||||
/// This method is not magic; it is not provided by the hardware.
|
||||
/// This method is not magic; it is not provided by the hardware.
|
||||
/// It is implemented in terms of
|
||||
#[doc = concat!("[`", stringify!($atomic_type), "::compare_exchange_weak`],")]
|
||||
/// and suffers from the same drawbacks.
|
||||
@ -2913,6 +3163,127 @@ macro_rules! atomic_int {
|
||||
Err(prev)
|
||||
}
|
||||
|
||||
/// Fetches the value, and applies a function to it that returns an optional
|
||||
/// new value. Returns a `Result` of `Ok(previous_value)` if the function returned `Some(_)`, else
|
||||
/// `Err(previous_value)`.
|
||||
///
|
||||
#[doc = concat!("See also: [`update`](`", stringify!($atomic_type), "::update`).")]
|
||||
///
|
||||
/// 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
|
||||
/// only once to the stored value.
|
||||
///
|
||||
/// `try_update` takes two [`Ordering`] arguments to describe the memory ordering of this operation.
|
||||
/// The first describes the required ordering for when the operation finally succeeds while the second
|
||||
/// describes the required ordering for loads. These correspond to the success and failure orderings of
|
||||
#[doc = concat!("[`", stringify!($atomic_type), "::compare_exchange`]")]
|
||||
/// respectively.
|
||||
///
|
||||
/// Using [`Acquire`] as success ordering makes the store part
|
||||
/// of this operation [`Relaxed`], and using [`Release`] makes the final successful load
|
||||
/// [`Relaxed`]. The (failed) load ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`].
|
||||
///
|
||||
/// **Note**: This method is only available on platforms that support atomic operations on
|
||||
#[doc = concat!("[`", $s_int_type, "`].")]
|
||||
///
|
||||
/// # Considerations
|
||||
///
|
||||
/// This method is not magic; it is not provided by the hardware.
|
||||
/// It is implemented in terms of
|
||||
#[doc = concat!("[`", stringify!($atomic_type), "::compare_exchange_weak`],")]
|
||||
/// and suffers from the same drawbacks.
|
||||
/// In particular, this method will not circumvent the [ABA Problem].
|
||||
///
|
||||
/// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// #![feature(atomic_try_update)]
|
||||
#[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")]
|
||||
///
|
||||
#[doc = concat!("let x = ", stringify!($atomic_type), "::new(7);")]
|
||||
/// assert_eq!(x.try_update(Ordering::SeqCst, Ordering::SeqCst, |_| None), Err(7));
|
||||
/// assert_eq!(x.try_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(x + 1)), Ok(7));
|
||||
/// assert_eq!(x.try_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(x + 1)), Ok(8));
|
||||
/// assert_eq!(x.load(Ordering::SeqCst), 9);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[unstable(feature = "atomic_try_update", issue = "135894")]
|
||||
#[$cfg_cas]
|
||||
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
|
||||
pub fn try_update(
|
||||
&self,
|
||||
set_order: Ordering,
|
||||
fetch_order: Ordering,
|
||||
f: impl FnMut($int_type) -> Option<$int_type>,
|
||||
) -> Result<$int_type, $int_type> {
|
||||
// FIXME(atomic_try_update): this is currently an unstable alias to `fetch_update`;
|
||||
// when stabilizing, turn `fetch_update` into a deprecated alias to `try_update`.
|
||||
self.fetch_update(set_order, fetch_order, f)
|
||||
}
|
||||
|
||||
/// Fetches the value, applies a function to it that it return a new value.
|
||||
/// The new value is stored and the old value is returned.
|
||||
///
|
||||
#[doc = concat!("See also: [`try_update`](`", stringify!($atomic_type), "::try_update`).")]
|
||||
///
|
||||
/// Note: This may call the function multiple times if the value has been changed from other threads in
|
||||
/// the meantime, but the function will have been applied only once to the stored value.
|
||||
///
|
||||
/// `update` takes two [`Ordering`] arguments to describe the memory ordering of this operation.
|
||||
/// The first describes the required ordering for when the operation finally succeeds while the second
|
||||
/// describes the required ordering for loads. These correspond to the success and failure orderings of
|
||||
#[doc = concat!("[`", stringify!($atomic_type), "::compare_exchange`]")]
|
||||
/// respectively.
|
||||
///
|
||||
/// Using [`Acquire`] as success ordering makes the store part
|
||||
/// of this operation [`Relaxed`], and using [`Release`] makes the final successful load
|
||||
/// [`Relaxed`]. The (failed) load ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`].
|
||||
///
|
||||
/// **Note**: This method is only available on platforms that support atomic operations on
|
||||
#[doc = concat!("[`", $s_int_type, "`].")]
|
||||
///
|
||||
/// # Considerations
|
||||
///
|
||||
/// This method is not magic; it is not provided by the hardware.
|
||||
/// It is implemented in terms of
|
||||
#[doc = concat!("[`", stringify!($atomic_type), "::compare_exchange_weak`],")]
|
||||
/// and suffers from the same drawbacks.
|
||||
/// In particular, this method will not circumvent the [ABA Problem].
|
||||
///
|
||||
/// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// #![feature(atomic_try_update)]
|
||||
#[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")]
|
||||
///
|
||||
#[doc = concat!("let x = ", stringify!($atomic_type), "::new(7);")]
|
||||
/// assert_eq!(x.update(Ordering::SeqCst, Ordering::SeqCst, |x| x + 1), 7);
|
||||
/// assert_eq!(x.update(Ordering::SeqCst, Ordering::SeqCst, |x| x + 1), 8);
|
||||
/// assert_eq!(x.load(Ordering::SeqCst), 9);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[unstable(feature = "atomic_try_update", issue = "135894")]
|
||||
#[$cfg_cas]
|
||||
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
|
||||
pub fn update(
|
||||
&self,
|
||||
set_order: Ordering,
|
||||
fetch_order: Ordering,
|
||||
mut f: impl FnMut($int_type) -> $int_type,
|
||||
) -> $int_type {
|
||||
let mut prev = self.load(fetch_order);
|
||||
loop {
|
||||
match self.compare_exchange_weak(prev, f(prev), set_order, fetch_order) {
|
||||
Ok(x) => break x,
|
||||
Err(next_prev) => prev = next_prev,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Maximum with the current value.
|
||||
///
|
||||
/// Finds the maximum of the current value and the argument `val`, and
|
||||
|
Loading…
Reference in New Issue
Block a user