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 all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
59 changes: 59 additions & 0 deletions crossbeam-epoch/src/atomic.rs
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| 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