diff --git a/crossbeam-epoch/src/atomic.rs b/crossbeam-epoch/src/atomic.rs index f01c793a4..9910512cc 100644 --- a/crossbeam-epoch/src/atomic.rs +++ b/crossbeam-epoch/src/atomic.rs @@ -9,8 +9,8 @@ use core::sync::atomic::Ordering; use crate::alloc::alloc; use crate::alloc::boxed::Box; -use crate::primitive::sync::atomic::AtomicUsize; use crate::guard::Guard; +use crate::primitive::sync::atomic::AtomicUsize; use crossbeam_utils::atomic::AtomicConsume; /// Given ordering for the success case in a compare-exchange operation, returns the strongest @@ -26,7 +26,12 @@ fn strongest_failure_ordering(ord: Ordering) -> Ordering { } /// The error returned on failed compare-and-set operation. -pub struct CompareAndSetError<'g, T: ?Sized + Pointable, P: Pointer> { +// TODO: remove in the next major version. +#[deprecated(note = "Use `CompareExchangeError` instead")] +pub type CompareAndSetError<'g, T, P> = CompareExchangeError<'g, T, P>; + +/// The error returned on failed compare-and-swap operation. +pub struct CompareExchangeError<'g, T: ?Sized + Pointable, P: Pointer> { /// The value in the atomic pointer at the time of the failed operation. pub current: Shared<'g, T>, @@ -34,9 +39,9 @@ pub struct CompareAndSetError<'g, T: ?Sized + Pointable, P: Pointer> { pub new: P, } -impl<'g, T: 'g, P: Pointer + fmt::Debug> fmt::Debug for CompareAndSetError<'g, T, P> { +impl + fmt::Debug> fmt::Debug for CompareExchangeError<'_, T, P> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("CompareAndSetError") + f.debug_struct("CompareExchangeError") .field("current", &self.current) .field("new", &self.new) .finish() @@ -54,6 +59,11 @@ impl<'g, T: 'g, P: Pointer + fmt::Debug> fmt::Debug for CompareAndSetError<'g /// ordering is chosen. /// 2. A pair of `Ordering`s. The first one is for the success case, while the second one is /// for the failure case. +// TODO: remove in the next major version. +#[deprecated( + note = "`compare_and_set` and `compare_and_set_weak` that use this trait are deprecated, \ + use `compare_exchange` or `compare_exchange_weak instead`" +)] pub trait CompareAndSetOrdering { /// The ordering of the operation when it succeeds. fn success(&self) -> Ordering; @@ -65,6 +75,7 @@ pub trait CompareAndSetOrdering { fn failure(&self) -> Ordering; } +#[allow(deprecated)] impl CompareAndSetOrdering for Ordering { #[inline] fn success(&self) -> Ordering { @@ -77,6 +88,7 @@ impl CompareAndSetOrdering for Ordering { } } +#[allow(deprecated)] impl CompareAndSetOrdering for (Ordering, Ordering) { #[inline] fn success(&self) -> Ordering { @@ -426,8 +438,14 @@ impl Atomic { /// pointer that was written is returned. On failure the actual current value and `new` are /// returned. /// - /// This method takes a [`CompareAndSetOrdering`] argument which describes the memory - /// ordering of this operation. + /// This method takes two `Ordering` arguments to describe the memory + /// ordering of this operation. `success` describes the required ordering for the + /// read-modify-write operation that takes place if the comparison with `current` succeeds. + /// `failure` describes the required ordering for the load operation that takes place when + /// the comparison fails. Using `Acquire` as success ordering makes the store part + /// of this operation `Relaxed`, and using `Release` makes the successful load + /// `Relaxed`. The failure ordering can only be `SeqCst`, `Acquire` or `Relaxed` + /// and must be equivalent to or weaker than the success ordering. /// /// # Examples /// @@ -439,32 +457,162 @@ impl Atomic { /// /// let guard = &epoch::pin(); /// let curr = a.load(SeqCst, guard); - /// let res1 = a.compare_and_set(curr, Shared::null(), SeqCst, guard); - /// let res2 = a.compare_and_set(curr, Owned::new(5678), SeqCst, guard); + /// let res1 = a.compare_exchange(curr, Shared::null(), SeqCst, SeqCst, guard); + /// let res2 = a.compare_exchange(curr, Owned::new(5678), SeqCst, SeqCst, guard); /// ``` - pub fn compare_and_set<'g, O, P>( + pub fn compare_exchange<'g, P>( &self, current: Shared<'_, T>, new: P, - ord: O, + success: Ordering, + failure: Ordering, _: &'g Guard, - ) -> Result, CompareAndSetError<'g, T, P>> + ) -> Result, CompareExchangeError<'g, T, P>> where - O: CompareAndSetOrdering, P: Pointer, { let new = new.into_usize(); self.data - .compare_exchange(current.into_usize(), new, ord.success(), ord.failure()) + .compare_exchange(current.into_usize(), new, success, failure) .map(|_| unsafe { Shared::from_usize(new) }) .map_err(|current| unsafe { - CompareAndSetError { + CompareExchangeError { current: Shared::from_usize(current), new: P::from_usize(new), } }) } + /// 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. + /// + /// Unlike [`compare_exchange`], this method is allowed to spuriously fail even when comparison + /// succeeds, which can result in more efficient code on some platforms. The return value is a + /// result indicating whether the new pointer was written. On success the pointer that was + /// written is returned. On failure the actual current value and `new` are returned. + /// + /// This method takes two `Ordering` arguments to describe the memory + /// ordering of this operation. `success` describes the required ordering for the + /// read-modify-write operation that takes place if the comparison with `current` succeeds. + /// `failure` describes the required ordering for the load operation that takes place when + /// the comparison fails. Using `Acquire` as success ordering makes the store part + /// of this operation `Relaxed`, and using `Release` makes the successful load + /// `Relaxed`. The failure ordering can only be `SeqCst`, `Acquire` or `Relaxed` + /// and must be equivalent to or weaker than the success ordering. + /// + /// [`compare_exchange`]: Atomic::compare_exchange + /// + /// # Examples + /// + /// ``` + /// use crossbeam_epoch::{self as epoch, Atomic, Owned, Shared}; + /// use std::sync::atomic::Ordering::SeqCst; + /// + /// let a = Atomic::new(1234); + /// let guard = &epoch::pin(); + /// + /// let mut new = Owned::new(5678); + /// let mut ptr = a.load(SeqCst, guard); + /// loop { + /// match a.compare_exchange_weak(ptr, new, SeqCst, SeqCst, guard) { + /// Ok(p) => { + /// ptr = p; + /// break; + /// } + /// Err(err) => { + /// ptr = err.current; + /// new = err.new; + /// } + /// } + /// } + /// + /// let mut curr = a.load(SeqCst, guard); + /// loop { + /// match a.compare_exchange_weak(curr, Shared::null(), SeqCst, SeqCst, guard) { + /// Ok(_) => break, + /// Err(err) => curr = err.current, + /// } + /// } + /// ``` + pub fn compare_exchange_weak<'g, P>( + &self, + current: Shared<'_, T>, + new: P, + success: Ordering, + failure: Ordering, + _: &'g Guard, + ) -> Result, CompareExchangeError<'g, T, P>> + where + P: Pointer, + { + let new = new.into_usize(); + self.data + .compare_exchange_weak(current.into_usize(), new, success, failure) + .map(|_| unsafe { Shared::from_usize(new) }) + .map_err(|current| unsafe { + CompareExchangeError { + current: Shared::from_usize(current), + new: P::from_usize(new), + } + }) + } + + /// 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. + /// + /// The return value is a result indicating whether the new pointer was written. On success the + /// pointer that was written is returned. On failure the actual current value and `new` are + /// returned. + /// + /// This method takes a [`CompareAndSetOrdering`] argument which describes the memory + /// ordering of this operation. + /// + /// # Migrating to `compare_exchange` + /// + /// `compare_and_set` is equivalent to `compare_exchange` with the following mapping for + /// memory orderings: + /// + /// Original | Success | Failure + /// -------- | ------- | ------- + /// Relaxed | Relaxed | Relaxed + /// Acquire | Acquire | Acquire + /// Release | Release | Relaxed + /// AcqRel | AcqRel | Acquire + /// SeqCst | SeqCst | SeqCst + /// + /// # Examples + /// + /// ``` + /// # #![allow(deprecated)] + /// use crossbeam_epoch::{self as epoch, Atomic, Owned, Shared}; + /// use std::sync::atomic::Ordering::SeqCst; + /// + /// let a = Atomic::new(1234); + /// + /// let guard = &epoch::pin(); + /// let curr = a.load(SeqCst, guard); + /// let res1 = a.compare_and_set(curr, Shared::null(), SeqCst, guard); + /// let res2 = a.compare_and_set(curr, Owned::new(5678), SeqCst, guard); + /// ``` + // TODO: remove in the next major version. + #[allow(deprecated)] + #[deprecated(note = "Use `compare_exchange` instead")] + pub fn compare_and_set<'g, O, P>( + &self, + current: Shared<'_, T>, + new: P, + ord: O, + guard: &'g Guard, + ) -> Result, CompareAndSetError<'g, T, P>> + where + O: CompareAndSetOrdering, + P: Pointer, + { + self.compare_exchange(current, new, ord.success(), ord.failure(), guard) + } + /// 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. @@ -479,9 +627,23 @@ impl Atomic { /// /// [`compare_and_set`]: Atomic::compare_and_set /// + /// # Migrating to `compare_exchange_weak` + /// + /// `compare_and_set_weak` is equivalent to `compare_exchange_weak` with the following mapping for + /// memory orderings: + /// + /// Original | Success | Failure + /// -------- | ------- | ------- + /// Relaxed | Relaxed | Relaxed + /// Acquire | Acquire | Acquire + /// Release | Release | Relaxed + /// AcqRel | AcqRel | Acquire + /// SeqCst | SeqCst | SeqCst + /// /// # Examples /// /// ``` + /// # #![allow(deprecated)] /// use crossbeam_epoch::{self as epoch, Atomic, Owned, Shared}; /// use std::sync::atomic::Ordering::SeqCst; /// @@ -511,27 +673,21 @@ impl Atomic { /// } /// } /// ``` + // TODO: remove in the next major version. + #[allow(deprecated)] + #[deprecated(note = "Use `compare_exchange_weak` instead")] pub fn compare_and_set_weak<'g, O, P>( &self, current: Shared<'_, T>, new: P, ord: O, - _: &'g Guard, + guard: &'g Guard, ) -> Result, CompareAndSetError<'g, T, P>> where O: CompareAndSetOrdering, P: Pointer, { - let new = new.into_usize(); - self.data - .compare_exchange_weak(current.into_usize(), new, ord.success(), ord.failure()) - .map(|_| unsafe { Shared::from_usize(new) }) - .map_err(|current| unsafe { - CompareAndSetError { - current: Shared::from_usize(current), - new: P::from_usize(new), - } - }) + self.compare_exchange_weak(current, new, ord.success(), ord.failure(), guard) } /// Bitwise "and" with the current tag. diff --git a/crossbeam-epoch/src/epoch.rs b/crossbeam-epoch/src/epoch.rs index c24c54a5e..90b28294b 100644 --- a/crossbeam-epoch/src/epoch.rs +++ b/crossbeam-epoch/src/epoch.rs @@ -106,7 +106,7 @@ impl AtomicEpoch { /// The return value is a result indicating whether the new value was written and containing /// the previous value. On success this value is guaranteed to be equal to `current`. /// - /// `compare_exchange` takes two `Ordering` arguments to describe the memory + /// This method takes two `Ordering` arguments to describe the memory /// ordering of this operation. `success` describes the required ordering for the /// read-modify-write operation that takes place if the comparison with `current` succeeds. /// `failure` describes the required ordering for the load operation that takes place when diff --git a/crossbeam-epoch/src/lib.rs b/crossbeam-epoch/src/lib.rs index 326599cbe..727eb4f72 100644 --- a/crossbeam-epoch/src/lib.rs +++ b/crossbeam-epoch/src/lib.rs @@ -161,9 +161,15 @@ cfg_if! { mod internal; mod sync; - pub use self::atomic::{Pointable, Atomic, CompareAndSetError, CompareAndSetOrdering, Owned, Pointer, Shared}; + pub use self::atomic::{ + Pointable, Atomic, CompareExchangeError, + Owned, Pointer, Shared, + }; pub use self::collector::{Collector, LocalHandle}; pub use self::guard::{unprotected, Guard}; + + #[allow(deprecated)] + pub use self::atomic::{CompareAndSetError, CompareAndSetOrdering}; } } diff --git a/crossbeam-epoch/src/sync/list.rs b/crossbeam-epoch/src/sync/list.rs index 4255f8742..e52431c6f 100644 --- a/crossbeam-epoch/src/sync/list.rs +++ b/crossbeam-epoch/src/sync/list.rs @@ -183,7 +183,7 @@ impl> List { // Set the Entry of the to-be-inserted element to point to the previous successor of // `to`. entry.next.store(next, Relaxed); - match to.compare_and_set_weak(next, entry_ptr, Release, guard) { + match to.compare_exchange_weak(next, entry_ptr, Release, Relaxed, guard) { Ok(_) => break, // We lost the race or weak CAS failed spuriously. Update the successor and try // again. @@ -250,7 +250,7 @@ impl<'g, T: 'g, C: IsElement> Iterator for Iter<'g, T, C> { // Try to unlink `curr` from the list, and get the new value of `self.pred`. let succ = match self .pred - .compare_and_set(self.curr, succ, Acquire, self.guard) + .compare_exchange(self.curr, succ, Acquire, Acquire, self.guard) { Ok(_) => { // We succeeded in unlinking `curr`, so we have to schedule diff --git a/crossbeam-epoch/src/sync/queue.rs b/crossbeam-epoch/src/sync/queue.rs index db9e8de04..fdbb74183 100644 --- a/crossbeam-epoch/src/sync/queue.rs +++ b/crossbeam-epoch/src/sync/queue.rs @@ -74,17 +74,21 @@ impl Queue { let next = o.next.load(Acquire, guard); if unsafe { next.as_ref().is_some() } { // if not, try to "help" by moving the tail pointer forward - let _ = self.tail.compare_and_set(onto, next, Release, guard); + let _ = self + .tail + .compare_exchange(onto, next, Release, Relaxed, guard); false } else { // looks like the actual tail; attempt to link in `n` let result = o .next - .compare_and_set(Shared::null(), new, Release, guard) + .compare_exchange(Shared::null(), new, Release, Relaxed, guard) .is_ok(); if result { // try to move the tail pointer forward - let _ = self.tail.compare_and_set(onto, new, Release, guard); + let _ = self + .tail + .compare_exchange(onto, new, Release, Relaxed, guard); } result } @@ -118,12 +122,14 @@ impl Queue { match unsafe { next.as_ref() } { Some(n) => unsafe { self.head - .compare_and_set(head, next, Release, guard) + .compare_exchange(head, next, Release, Relaxed, guard) .map(|_| { let tail = self.tail.load(Relaxed, guard); // Advance the tail so that we don't retire a pointer to a reachable node. if head == tail { - let _ = self.tail.compare_and_set(tail, next, Release, guard); + let _ = self + .tail + .compare_exchange(tail, next, Release, Relaxed, guard); } guard.defer_destroy(head); // TODO: Replace with MaybeUninit::read when api is stable @@ -149,12 +155,14 @@ impl Queue { match unsafe { next.as_ref() } { Some(n) if condition(unsafe { &*n.data.as_ptr() }) => unsafe { self.head - .compare_and_set(head, next, Release, guard) + .compare_exchange(head, next, Release, Relaxed, guard) .map(|_| { let tail = self.tail.load(Relaxed, guard); // Advance the tail so that we don't retire a pointer to a reachable node. if head == tail { - let _ = self.tail.compare_and_set(tail, next, Release, guard); + let _ = self + .tail + .compare_exchange(tail, next, Release, Relaxed, guard); } guard.defer_destroy(head); Some(n.data.as_ptr().read()) diff --git a/crossbeam-epoch/tests/loom.rs b/crossbeam-epoch/tests/loom.rs index fa4120f51..504f36b6b 100644 --- a/crossbeam-epoch/tests/loom.rs +++ b/crossbeam-epoch/tests/loom.rs @@ -81,7 +81,10 @@ fn treiber_stack() { let head = self.head.load(Relaxed, &guard); n.next.store(head, Relaxed); - match self.head.compare_and_set(head, n, Release, &guard) { + match self + .head + .compare_exchange(head, n, Release, Relaxed, &guard) + { Ok(_) => break, Err(e) => n = e.new, } @@ -102,7 +105,7 @@ fn treiber_stack() { if self .head - .compare_and_set(head, next, Relaxed, &guard) + .compare_exchange(head, next, Relaxed, Relaxed, &guard) .is_ok() { unsafe { diff --git a/crossbeam-skiplist/src/base.rs b/crossbeam-skiplist/src/base.rs index 26c1c64f3..7644dbe36 100644 --- a/crossbeam-skiplist/src/base.rs +++ b/crossbeam-skiplist/src/base.rs @@ -592,10 +592,11 @@ where ) -> Option>> { // If `succ` is marked, that means `curr` is removed. Let's try // unlinking it from the skip list at this level. - match pred.compare_and_set( + match pred.compare_exchange( Shared::from(curr as *const _), succ.with_tag(0), Ordering::Release, + Ordering::Relaxed, guard, ) { Ok(_) => { @@ -900,7 +901,13 @@ where // Try installing the new node into the skip list (at level 0). // TODO(Amanieu): can we use release ordering here? if search.left[0][0] - .compare_and_set(search.right[0], node, Ordering::SeqCst, guard) + .compare_exchange( + search.right[0], + node, + Ordering::SeqCst, + Ordering::SeqCst, + guard, + ) .is_ok() { break; @@ -992,7 +999,7 @@ where // should stop building the tower. // TODO(Amanieu): can we use release ordering here? if n.tower[level] - .compare_and_set(next, succ, Ordering::SeqCst, guard) + .compare_exchange(next, succ, Ordering::SeqCst, Ordering::SeqCst, guard) .is_err() { break 'build; @@ -1006,7 +1013,7 @@ where // Try installing the new node at the current level. // TODO(Amanieu): can we use release ordering here? if pred[level] - .compare_and_set(succ, node, Ordering::SeqCst, guard) + .compare_exchange(succ, node, Ordering::SeqCst, Ordering::SeqCst, guard) .is_ok() { // Success! Continue on the next level. @@ -1099,10 +1106,11 @@ where // Try linking the predecessor and successor at this level. // TODO(Amanieu): can we use release ordering here? if search.left[level][level] - .compare_and_set( + .compare_exchange( Shared::from(n as *const _), succ, Ordering::SeqCst, + Ordering::SeqCst, guard, ) .is_ok() diff --git a/crossbeam-utils/src/atomic/atomic_cell.rs b/crossbeam-utils/src/atomic/atomic_cell.rs index 3bb06f560..25bec96d0 100644 --- a/crossbeam-utils/src/atomic/atomic_cell.rs +++ b/crossbeam-utils/src/atomic/atomic_cell.rs @@ -228,6 +228,7 @@ impl AtomicCell { /// assert_eq!(a.compare_and_swap(1, 2), 1); /// assert_eq!(a.load(), 2); /// ``` + // TODO: remove in the next major version. #[deprecated(note = "Use `compare_exchange` instead")] pub fn compare_and_swap(&self, current: T, new: T) -> T { match self.compare_exchange(current, new) {