Skip to content

Commit

Permalink
Add fetch_update methods to AtomicBool and AtomicPtr
Browse files Browse the repository at this point in the history
These methods were stabilized for the integer atomics in rust-lang#71843, but the methods
were not added for the non-integer atomics `AtomicBool` and `AtomicPtr`.
  • Loading branch information
mystor committed Nov 1, 2020
1 parent 1899c48 commit 00f32e6
Showing 1 changed file with 125 additions and 0 deletions.
125 changes: 125 additions & 0 deletions library/core/src/sync/atomic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -801,6 +801,64 @@ impl AtomicBool {
pub fn as_mut_ptr(&self) -> *mut bool {
self.v.get() as *mut bool
}

/// 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)`.
///
/// 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.
///
/// `fetch_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`] and must be equivalent to or weaker than the
/// success ordering.
///
/// **Note:** This method is only available on platforms that support atomic
/// operations on `u8`.
///
/// # Examples
///
/// ```rust
/// #![feature(atomic_fetch_update)]
/// use std::sync::atomic::{AtomicBool, Ordering};
///
/// let x = AtomicBool::new(false);
/// assert_eq!(x.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |_| None), Err(false));
/// assert_eq!(x.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(!x)), Ok(false));
/// assert_eq!(x.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(!x)), Ok(true));
/// assert_eq!(x.load(Ordering::SeqCst), false);
/// ```
#[inline]
#[unstable(feature = "atomic_fetch_update", reason = "recently added", issue = "78639")]
#[cfg(target_has_atomic = "8")]
pub fn fetch_update<F>(
&self,
set_order: Ordering,
fetch_order: Ordering,
mut f: F,
) -> Result<bool, bool>
where
F: FnMut(bool) -> Option<bool>,
{
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)
}
}

#[cfg(target_has_atomic_load_store = "ptr")]
Expand Down Expand Up @@ -1123,6 +1181,73 @@ impl<T> AtomicPtr<T> {
}
}
}

/// 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)`.
///
/// 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.
///
/// `fetch_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`] and must be equivalent to or weaker than the
/// success ordering.
///
/// **Note:** This method is only available on platforms that support atomic
/// operations on pointers.
///
/// # Examples
///
/// ```rust
/// #![feature(atomic_fetch_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.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |_| None), Err(ptr));
/// let result = some_ptr.fetch_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_fetch_update", reason = "recently added", issue = "78639")]
#[cfg(target_has_atomic = "ptr")]
pub fn fetch_update<F>(
&self,
set_order: Ordering,
fetch_order: Ordering,
mut f: F,
) -> Result<*mut T, *mut T>
where
F: FnMut(*mut T) -> Option<*mut T>,
{
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)
}
}

#[cfg(target_has_atomic_load_store = "8")]
Expand Down

0 comments on commit 00f32e6

Please sign in to comment.