Skip to content

Commit 06229f5

Browse files
bors[bot]taiki-e
andauthored
Merge #628
628: Add Atomic::compare_exchange(_weak) and deprecate Atomic::compare_and_set(_weak) r=jeehoonkang a=taiki-e Instead of changing the argument of `Atomic::compare_and_set(_weak)`, add new methods `Atomic::compare_exchange(_weak)` that always use two orderings. This PR adds: - `Atomic::compare_exchange` Unlike `compare_and_set`, this always receive two orderings. - `Atomic::compare_exchange_weak` Unlike `compare_and_set_weak`, this always receive two orderings. - `CompareExchangeError` (error returned by `Atomic::compare_exchange(_weak)`) The definition is the same as `CompareAndSetError`, just renamed `CompareExchangeError`. This PR deprecates: - [`Atomic::compare_and_set`](https://docs.rs/crossbeam-epoch/0.9/crossbeam_epoch/struct.Atomic.html#method.compare_and_set) - [`Atomic::compare_and_set_weak`](https://docs.rs/crossbeam-epoch/0.9/crossbeam_epoch/struct.Atomic.html#method.compare_and_set_weak) - [`CompareAndSetError`](https://docs.rs/crossbeam-epoch/0.9/crossbeam_epoch/struct.CompareAndSetError.html) (error returned by `Atomic::compare_and_set(_weak)`) This is now a type alias of `CompareExchangeError`. - [`CompareAndSetOrdering`](https://docs.rs/crossbeam-epoch/0.9/crossbeam_epoch/trait.CompareAndSetOrdering.html) Closes #621 Co-authored-by: Taiki Endo <[email protected]>
2 parents f81df02 + a172017 commit 06229f5

File tree

8 files changed

+225
-43
lines changed

8 files changed

+225
-43
lines changed

crossbeam-epoch/src/atomic.rs

Lines changed: 181 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ use core::sync::atomic::Ordering;
99

1010
use crate::alloc::alloc;
1111
use crate::alloc::boxed::Box;
12-
use crate::primitive::sync::atomic::AtomicUsize;
1312
use crate::guard::Guard;
13+
use crate::primitive::sync::atomic::AtomicUsize;
1414
use crossbeam_utils::atomic::AtomicConsume;
1515

1616
/// Given ordering for the success case in a compare-exchange operation, returns the strongest
@@ -26,17 +26,22 @@ fn strongest_failure_ordering(ord: Ordering) -> Ordering {
2626
}
2727

2828
/// The error returned on failed compare-and-set operation.
29-
pub struct CompareAndSetError<'g, T: ?Sized + Pointable, P: Pointer<T>> {
29+
// TODO: remove in the next major version.
30+
#[deprecated(note = "Use `CompareExchangeError` instead")]
31+
pub type CompareAndSetError<'g, T, P> = CompareExchangeError<'g, T, P>;
32+
33+
/// The error returned on failed compare-and-swap operation.
34+
pub struct CompareExchangeError<'g, T: ?Sized + Pointable, P: Pointer<T>> {
3035
/// The value in the atomic pointer at the time of the failed operation.
3136
pub current: Shared<'g, T>,
3237

3338
/// The new value, which the operation failed to store.
3439
pub new: P,
3540
}
3641

37-
impl<'g, T: 'g, P: Pointer<T> + fmt::Debug> fmt::Debug for CompareAndSetError<'g, T, P> {
42+
impl<T, P: Pointer<T> + fmt::Debug> fmt::Debug for CompareExchangeError<'_, T, P> {
3843
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39-
f.debug_struct("CompareAndSetError")
44+
f.debug_struct("CompareExchangeError")
4045
.field("current", &self.current)
4146
.field("new", &self.new)
4247
.finish()
@@ -54,6 +59,11 @@ impl<'g, T: 'g, P: Pointer<T> + fmt::Debug> fmt::Debug for CompareAndSetError<'g
5459
/// ordering is chosen.
5560
/// 2. A pair of `Ordering`s. The first one is for the success case, while the second one is
5661
/// for the failure case.
62+
// TODO: remove in the next major version.
63+
#[deprecated(
64+
note = "`compare_and_set` and `compare_and_set_weak` that use this trait are deprecated, \
65+
use `compare_exchange` or `compare_exchange_weak instead`"
66+
)]
5767
pub trait CompareAndSetOrdering {
5868
/// The ordering of the operation when it succeeds.
5969
fn success(&self) -> Ordering;
@@ -65,6 +75,7 @@ pub trait CompareAndSetOrdering {
6575
fn failure(&self) -> Ordering;
6676
}
6777

78+
#[allow(deprecated)]
6879
impl CompareAndSetOrdering for Ordering {
6980
#[inline]
7081
fn success(&self) -> Ordering {
@@ -77,6 +88,7 @@ impl CompareAndSetOrdering for Ordering {
7788
}
7889
}
7990

91+
#[allow(deprecated)]
8092
impl CompareAndSetOrdering for (Ordering, Ordering) {
8193
#[inline]
8294
fn success(&self) -> Ordering {
@@ -426,8 +438,14 @@ impl<T: ?Sized + Pointable> Atomic<T> {
426438
/// pointer that was written is returned. On failure the actual current value and `new` are
427439
/// returned.
428440
///
429-
/// This method takes a [`CompareAndSetOrdering`] argument which describes the memory
430-
/// ordering of this operation.
441+
/// This method takes two `Ordering` arguments to describe the memory
442+
/// ordering of this operation. `success` describes the required ordering for the
443+
/// read-modify-write operation that takes place if the comparison with `current` succeeds.
444+
/// `failure` describes the required ordering for the load operation that takes place when
445+
/// the comparison fails. Using `Acquire` as success ordering makes the store part
446+
/// of this operation `Relaxed`, and using `Release` makes the successful load
447+
/// `Relaxed`. The failure ordering can only be `SeqCst`, `Acquire` or `Relaxed`
448+
/// and must be equivalent to or weaker than the success ordering.
431449
///
432450
/// # Examples
433451
///
@@ -439,32 +457,162 @@ impl<T: ?Sized + Pointable> Atomic<T> {
439457
///
440458
/// let guard = &epoch::pin();
441459
/// let curr = a.load(SeqCst, guard);
442-
/// let res1 = a.compare_and_set(curr, Shared::null(), SeqCst, guard);
443-
/// let res2 = a.compare_and_set(curr, Owned::new(5678), SeqCst, guard);
460+
/// let res1 = a.compare_exchange(curr, Shared::null(), SeqCst, SeqCst, guard);
461+
/// let res2 = a.compare_exchange(curr, Owned::new(5678), SeqCst, SeqCst, guard);
444462
/// ```
445-
pub fn compare_and_set<'g, O, P>(
463+
pub fn compare_exchange<'g, P>(
446464
&self,
447465
current: Shared<'_, T>,
448466
new: P,
449-
ord: O,
467+
success: Ordering,
468+
failure: Ordering,
450469
_: &'g Guard,
451-
) -> Result<Shared<'g, T>, CompareAndSetError<'g, T, P>>
470+
) -> Result<Shared<'g, T>, CompareExchangeError<'g, T, P>>
452471
where
453-
O: CompareAndSetOrdering,
454472
P: Pointer<T>,
455473
{
456474
let new = new.into_usize();
457475
self.data
458-
.compare_exchange(current.into_usize(), new, ord.success(), ord.failure())
476+
.compare_exchange(current.into_usize(), new, success, failure)
459477
.map(|_| unsafe { Shared::from_usize(new) })
460478
.map_err(|current| unsafe {
461-
CompareAndSetError {
479+
CompareExchangeError {
462480
current: Shared::from_usize(current),
463481
new: P::from_usize(new),
464482
}
465483
})
466484
}
467485

486+
/// Stores the pointer `new` (either `Shared` or `Owned`) into the atomic pointer if the current
487+
/// value is the same as `current`. The tag is also taken into account, so two pointers to the
488+
/// same object, but with different tags, will not be considered equal.
489+
///
490+
/// Unlike [`compare_exchange`], this method is allowed to spuriously fail even when comparison
491+
/// succeeds, which can result in more efficient code on some platforms. The return value is a
492+
/// result indicating whether the new pointer was written. On success the pointer that was
493+
/// written is returned. On failure the actual current value and `new` are returned.
494+
///
495+
/// This method takes two `Ordering` arguments to describe the memory
496+
/// ordering of this operation. `success` describes the required ordering for the
497+
/// read-modify-write operation that takes place if the comparison with `current` succeeds.
498+
/// `failure` describes the required ordering for the load operation that takes place when
499+
/// the comparison fails. Using `Acquire` as success ordering makes the store part
500+
/// of this operation `Relaxed`, and using `Release` makes the successful load
501+
/// `Relaxed`. The failure ordering can only be `SeqCst`, `Acquire` or `Relaxed`
502+
/// and must be equivalent to or weaker than the success ordering.
503+
///
504+
/// [`compare_exchange`]: Atomic::compare_exchange
505+
///
506+
/// # Examples
507+
///
508+
/// ```
509+
/// use crossbeam_epoch::{self as epoch, Atomic, Owned, Shared};
510+
/// use std::sync::atomic::Ordering::SeqCst;
511+
///
512+
/// let a = Atomic::new(1234);
513+
/// let guard = &epoch::pin();
514+
///
515+
/// let mut new = Owned::new(5678);
516+
/// let mut ptr = a.load(SeqCst, guard);
517+
/// loop {
518+
/// match a.compare_exchange_weak(ptr, new, SeqCst, SeqCst, guard) {
519+
/// Ok(p) => {
520+
/// ptr = p;
521+
/// break;
522+
/// }
523+
/// Err(err) => {
524+
/// ptr = err.current;
525+
/// new = err.new;
526+
/// }
527+
/// }
528+
/// }
529+
///
530+
/// let mut curr = a.load(SeqCst, guard);
531+
/// loop {
532+
/// match a.compare_exchange_weak(curr, Shared::null(), SeqCst, SeqCst, guard) {
533+
/// Ok(_) => break,
534+
/// Err(err) => curr = err.current,
535+
/// }
536+
/// }
537+
/// ```
538+
pub fn compare_exchange_weak<'g, P>(
539+
&self,
540+
current: Shared<'_, T>,
541+
new: P,
542+
success: Ordering,
543+
failure: Ordering,
544+
_: &'g Guard,
545+
) -> Result<Shared<'g, T>, CompareExchangeError<'g, T, P>>
546+
where
547+
P: Pointer<T>,
548+
{
549+
let new = new.into_usize();
550+
self.data
551+
.compare_exchange_weak(current.into_usize(), new, success, failure)
552+
.map(|_| unsafe { Shared::from_usize(new) })
553+
.map_err(|current| unsafe {
554+
CompareExchangeError {
555+
current: Shared::from_usize(current),
556+
new: P::from_usize(new),
557+
}
558+
})
559+
}
560+
561+
/// Stores the pointer `new` (either `Shared` or `Owned`) into the atomic pointer if the current
562+
/// value is the same as `current`. The tag is also taken into account, so two pointers to the
563+
/// same object, but with different tags, will not be considered equal.
564+
///
565+
/// The return value is a result indicating whether the new pointer was written. On success the
566+
/// pointer that was written is returned. On failure the actual current value and `new` are
567+
/// returned.
568+
///
569+
/// This method takes a [`CompareAndSetOrdering`] argument which describes the memory
570+
/// ordering of this operation.
571+
///
572+
/// # Migrating to `compare_exchange`
573+
///
574+
/// `compare_and_set` is equivalent to `compare_exchange` with the following mapping for
575+
/// memory orderings:
576+
///
577+
/// Original | Success | Failure
578+
/// -------- | ------- | -------
579+
/// Relaxed | Relaxed | Relaxed
580+
/// Acquire | Acquire | Acquire
581+
/// Release | Release | Relaxed
582+
/// AcqRel | AcqRel | Acquire
583+
/// SeqCst | SeqCst | SeqCst
584+
///
585+
/// # Examples
586+
///
587+
/// ```
588+
/// # #![allow(deprecated)]
589+
/// use crossbeam_epoch::{self as epoch, Atomic, Owned, Shared};
590+
/// use std::sync::atomic::Ordering::SeqCst;
591+
///
592+
/// let a = Atomic::new(1234);
593+
///
594+
/// let guard = &epoch::pin();
595+
/// let curr = a.load(SeqCst, guard);
596+
/// let res1 = a.compare_and_set(curr, Shared::null(), SeqCst, guard);
597+
/// let res2 = a.compare_and_set(curr, Owned::new(5678), SeqCst, guard);
598+
/// ```
599+
// TODO: remove in the next major version.
600+
#[allow(deprecated)]
601+
#[deprecated(note = "Use `compare_exchange` instead")]
602+
pub fn compare_and_set<'g, O, P>(
603+
&self,
604+
current: Shared<'_, T>,
605+
new: P,
606+
ord: O,
607+
guard: &'g Guard,
608+
) -> Result<Shared<'g, T>, CompareAndSetError<'g, T, P>>
609+
where
610+
O: CompareAndSetOrdering,
611+
P: Pointer<T>,
612+
{
613+
self.compare_exchange(current, new, ord.success(), ord.failure(), guard)
614+
}
615+
468616
/// Stores the pointer `new` (either `Shared` or `Owned`) into the atomic pointer if the current
469617
/// value is the same as `current`. The tag is also taken into account, so two pointers to the
470618
/// same object, but with different tags, will not be considered equal.
@@ -479,9 +627,23 @@ impl<T: ?Sized + Pointable> Atomic<T> {
479627
///
480628
/// [`compare_and_set`]: Atomic::compare_and_set
481629
///
630+
/// # Migrating to `compare_exchange_weak`
631+
///
632+
/// `compare_and_set_weak` is equivalent to `compare_exchange_weak` with the following mapping for
633+
/// memory orderings:
634+
///
635+
/// Original | Success | Failure
636+
/// -------- | ------- | -------
637+
/// Relaxed | Relaxed | Relaxed
638+
/// Acquire | Acquire | Acquire
639+
/// Release | Release | Relaxed
640+
/// AcqRel | AcqRel | Acquire
641+
/// SeqCst | SeqCst | SeqCst
642+
///
482643
/// # Examples
483644
///
484645
/// ```
646+
/// # #![allow(deprecated)]
485647
/// use crossbeam_epoch::{self as epoch, Atomic, Owned, Shared};
486648
/// use std::sync::atomic::Ordering::SeqCst;
487649
///
@@ -511,27 +673,21 @@ impl<T: ?Sized + Pointable> Atomic<T> {
511673
/// }
512674
/// }
513675
/// ```
676+
// TODO: remove in the next major version.
677+
#[allow(deprecated)]
678+
#[deprecated(note = "Use `compare_exchange_weak` instead")]
514679
pub fn compare_and_set_weak<'g, O, P>(
515680
&self,
516681
current: Shared<'_, T>,
517682
new: P,
518683
ord: O,
519-
_: &'g Guard,
684+
guard: &'g Guard,
520685
) -> Result<Shared<'g, T>, CompareAndSetError<'g, T, P>>
521686
where
522687
O: CompareAndSetOrdering,
523688
P: Pointer<T>,
524689
{
525-
let new = new.into_usize();
526-
self.data
527-
.compare_exchange_weak(current.into_usize(), new, ord.success(), ord.failure())
528-
.map(|_| unsafe { Shared::from_usize(new) })
529-
.map_err(|current| unsafe {
530-
CompareAndSetError {
531-
current: Shared::from_usize(current),
532-
new: P::from_usize(new),
533-
}
534-
})
690+
self.compare_exchange_weak(current, new, ord.success(), ord.failure(), guard)
535691
}
536692

537693
/// Bitwise "and" with the current tag.

crossbeam-epoch/src/epoch.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ impl AtomicEpoch {
106106
/// The return value is a result indicating whether the new value was written and containing
107107
/// the previous value. On success this value is guaranteed to be equal to `current`.
108108
///
109-
/// `compare_exchange` takes two `Ordering` arguments to describe the memory
109+
/// This method takes two `Ordering` arguments to describe the memory
110110
/// ordering of this operation. `success` describes the required ordering for the
111111
/// read-modify-write operation that takes place if the comparison with `current` succeeds.
112112
/// `failure` describes the required ordering for the load operation that takes place when

crossbeam-epoch/src/lib.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,9 +161,15 @@ cfg_if! {
161161
mod internal;
162162
mod sync;
163163

164-
pub use self::atomic::{Pointable, Atomic, CompareAndSetError, CompareAndSetOrdering, Owned, Pointer, Shared};
164+
pub use self::atomic::{
165+
Pointable, Atomic, CompareExchangeError,
166+
Owned, Pointer, Shared,
167+
};
165168
pub use self::collector::{Collector, LocalHandle};
166169
pub use self::guard::{unprotected, Guard};
170+
171+
#[allow(deprecated)]
172+
pub use self::atomic::{CompareAndSetError, CompareAndSetOrdering};
167173
}
168174
}
169175

crossbeam-epoch/src/sync/list.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ impl<T, C: IsElement<T>> List<T, C> {
183183
// Set the Entry of the to-be-inserted element to point to the previous successor of
184184
// `to`.
185185
entry.next.store(next, Relaxed);
186-
match to.compare_and_set_weak(next, entry_ptr, Release, guard) {
186+
match to.compare_exchange_weak(next, entry_ptr, Release, Relaxed, guard) {
187187
Ok(_) => break,
188188
// We lost the race or weak CAS failed spuriously. Update the successor and try
189189
// again.
@@ -250,7 +250,7 @@ impl<'g, T: 'g, C: IsElement<T>> Iterator for Iter<'g, T, C> {
250250
// Try to unlink `curr` from the list, and get the new value of `self.pred`.
251251
let succ = match self
252252
.pred
253-
.compare_and_set(self.curr, succ, Acquire, self.guard)
253+
.compare_exchange(self.curr, succ, Acquire, Acquire, self.guard)
254254
{
255255
Ok(_) => {
256256
// We succeeded in unlinking `curr`, so we have to schedule

0 commit comments

Comments
 (0)