Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Atomic::fetch_update #706

Merged
merged 7 commits into from
May 31, 2021
Merged
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions crossbeam-epoch/src/atomic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,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, guard| Some(x.with_tag(1)));
/// assert!(res1.is_ok());
///
/// let res2 = a.fetch_update(SeqCst, SeqCst, guard, |x, guard| 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: for<'a> FnMut(Shared<'a, T>, &'a Guard) -> Option<Shared<'a, T>>,
taiki-e marked this conversation as resolved.
Show resolved Hide resolved
{
let mut prev = self.load(fail_order, guard);
while let Some(next) = func(prev, guard) {
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