Skip to content

Commit 8bbe24e

Browse files
Merge #706
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 <patrick.147.norton@gmail.com>
2 parents 1f3104e + 46ef7b7 commit 8bbe24e

File tree

1 file changed

+59
-0
lines changed

1 file changed

+59
-0
lines changed

crossbeam-epoch/src/atomic.rs

+59
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,65 @@ impl<T: ?Sized + Pointable> Atomic<T> {
562562
})
563563
}
564564

565+
/// Fetches the pointer, and then applies a function to it that returns a new value.
566+
/// Returns a `Result` of `Ok(previous_value)` if the function returned `Some`, else `Err(_)`.
567+
///
568+
/// Note that the given function may be called multiple times if the value has been changed by
569+
/// other threads in the meantime, as long as the function returns `Some(_)`, but the function
570+
/// will have been applied only once to the stored value.
571+
///
572+
/// `fetch_update` takes two [`Ordering`] arguments to describe the memory
573+
/// ordering of this operation. The first describes the required ordering for
574+
/// when the operation finally succeeds while the second describes the
575+
/// required ordering for loads. These correspond to the success and failure
576+
/// orderings of [`Atomic::compare_exchange`] respectively.
577+
///
578+
/// Using [`Acquire`] as success ordering makes the store part of this
579+
/// operation [`Relaxed`], and using [`Release`] makes the final successful
580+
/// load [`Relaxed`]. The (failed) load ordering can only be [`SeqCst`],
581+
/// [`Acquire`] or [`Relaxed`] and must be equivalent to or weaker than the
582+
/// success ordering.
583+
///
584+
/// [`Relaxed`]: Ordering::Relaxed
585+
/// [`Acquire`]: Ordering::Acquire
586+
/// [`Release`]: Ordering::Release
587+
/// [`SeqCst`]: Ordering::SeqCst
588+
///
589+
/// # Examples
590+
///
591+
/// ```
592+
/// use crossbeam_epoch::{self as epoch, Atomic};
593+
/// use std::sync::atomic::Ordering::SeqCst;
594+
///
595+
/// let a = Atomic::new(1234);
596+
/// let guard = &epoch::pin();
597+
///
598+
/// let res1 = a.fetch_update(SeqCst, SeqCst, guard, |x| Some(x.with_tag(1)));
599+
/// assert!(res1.is_ok());
600+
///
601+
/// let res2 = a.fetch_update(SeqCst, SeqCst, guard, |x| None);
602+
/// assert!(res2.is_err());
603+
/// ```
604+
pub fn fetch_update<'g, F>(
605+
&self,
606+
set_order: Ordering,
607+
fail_order: Ordering,
608+
guard: &'g Guard,
609+
mut func: F,
610+
) -> Result<Shared<'g, T>, Shared<'g, T>>
611+
where
612+
F: FnMut(Shared<'g, T>) -> Option<Shared<'g, T>>,
613+
{
614+
let mut prev = self.load(fail_order, guard);
615+
while let Some(next) = func(prev) {
616+
match self.compare_exchange_weak(prev, next, set_order, fail_order, guard) {
617+
Ok(shared) => return Ok(shared),
618+
Err(next_prev) => prev = next_prev.current,
619+
}
620+
}
621+
Err(prev)
622+
}
623+
565624
/// Stores the pointer `new` (either `Shared` or `Owned`) into the atomic pointer if the current
566625
/// value is the same as `current`. The tag is also taken into account, so two pointers to the
567626
/// same object, but with different tags, will not be considered equal.

0 commit comments

Comments
 (0)