Skip to content

Commit

Permalink
Merge #706
Browse files Browse the repository at this point in the history
706: Add Atomic::fetch_update r=taiki-e a=PatrickNorton

Equivalent of [`AtomicN::fetch_update`](https://doc.rust-lang.org/nightly/core/sync/atomic/struct.AtomicUsize.html#method.fetch_update) and [`AtomicCell::fetch_update`](#704).

Co-authored-by: Patrick Norton <[email protected]>
  • Loading branch information
bors[bot] and PatrickNorton authored May 31, 2021
2 parents 1f3104e + 46ef7b7 commit 8bbe24e
Showing 1 changed file with 59 additions and 0 deletions.
59 changes: 59 additions & 0 deletions crossbeam-epoch/src/atomic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,65 @@ impl<T: ?Sized + Pointable> Atomic<T> {
})
}

/// Fetches the pointer, and then applies a function to it that returns a new value.
/// Returns a `Result` of `Ok(previous_value)` if the function returned `Some`, else `Err(_)`.
///
/// Note that the given function may be called multiple times if the value has been changed by
/// 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 [`Atomic::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.
///
/// [`Relaxed`]: Ordering::Relaxed
/// [`Acquire`]: Ordering::Acquire
/// [`Release`]: Ordering::Release
/// [`SeqCst`]: Ordering::SeqCst
///
/// # Examples
///
/// ```
/// use crossbeam_epoch::{self as epoch, Atomic};
/// use std::sync::atomic::Ordering::SeqCst;
///
/// let a = Atomic::new(1234);
/// let guard = &epoch::pin();
///
/// let res1 = a.fetch_update(SeqCst, SeqCst, guard, |x| Some(x.with_tag(1)));
/// assert!(res1.is_ok());
///
/// let res2 = a.fetch_update(SeqCst, SeqCst, guard, |x| None);
/// assert!(res2.is_err());
/// ```
pub fn fetch_update<'g, F>(
&self,
set_order: Ordering,
fail_order: Ordering,
guard: &'g Guard,
mut func: F,
) -> Result<Shared<'g, T>, Shared<'g, T>>
where
F: FnMut(Shared<'g, T>) -> Option<Shared<'g, T>>,
{
let mut prev = self.load(fail_order, guard);
while let Some(next) = func(prev) {
match self.compare_exchange_weak(prev, next, set_order, fail_order, guard) {
Ok(shared) => return Ok(shared),
Err(next_prev) => prev = next_prev.current,
}
}
Err(prev)
}

/// Stores the pointer `new` (either `Shared` or `Owned`) into the atomic pointer if the current
/// value is the same as `current`. The tag is also taken into account, so two pointers to the
/// same object, but with different tags, will not be considered equal.
Expand Down

0 comments on commit 8bbe24e

Please sign in to comment.