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

Deprecate atomic compare_and_swap method #79261

Merged
merged 8 commits into from
Dec 23, 2020
107 changes: 89 additions & 18 deletions library/core/src/sync/atomic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,23 @@ impl AtomicBool {
/// **Note:** This method is only available on platforms that support atomic
/// operations on `u8`.
///
/// # Migrating to `compare_exchange` and `compare_exchange_weak`
///
/// `compare_and_swap` is equivalent to `compare_exchange` with the following mapping for
/// memory orderings:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not equivalent though, the return type is different...

See #80486.

///
/// Original | Success | Failure
/// -------- | ------- | -------
/// Relaxed | Relaxed | Relaxed
/// Acquire | Acquire | Acquire
/// Release | Release | Relaxed
/// AcqRel | AcqRel | Acquire
/// SeqCst | SeqCst | SeqCst
///
/// `compare_exchange_weak` is allowed to fail spuriously even when the comparison succeeds,
/// which allows the compiler to generate better assembly code when the compare and swap
/// is used in a loop.
///
/// # Examples
///
/// ```
Expand All @@ -479,6 +496,10 @@ impl AtomicBool {
/// ```
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_deprecated(
since = "1.50.0",
reason = "Use `compare_exchange` or `compare_exchange_weak` instead"
)]
#[cfg(target_has_atomic = "8")]
pub fn compare_and_swap(&self, current: bool, new: bool, order: Ordering) -> bool {
match self.compare_exchange(current, new, order, strongest_failure_ordering(order)) {
Expand All @@ -493,9 +514,10 @@ impl AtomicBool {
/// the previous value. On success this value is guaranteed to be equal to `current`.
///
/// `compare_exchange` takes two [`Ordering`] arguments to describe the memory
/// ordering of this operation. The first describes the required ordering if the
/// operation succeeds while the second describes the required ordering when the
/// operation fails. Using [`Acquire`] as success ordering makes the store part
/// 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.
Expand Down Expand Up @@ -525,6 +547,7 @@ impl AtomicBool {
/// ```
#[inline]
#[stable(feature = "extended_compare_and_swap", since = "1.10.0")]
#[doc(alias = "compare_and_swap")]
#[cfg(target_has_atomic = "8")]
pub fn compare_exchange(
&self,
Expand All @@ -550,9 +573,10 @@ impl AtomicBool {
/// previous value.
///
/// `compare_exchange_weak` takes two [`Ordering`] arguments to describe the memory
/// ordering of this operation. The first describes the required ordering if the
/// operation succeeds while the second describes the required ordering when the
/// operation fails. Using [`Acquire`] as success ordering makes the store part
/// 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.
Expand All @@ -578,6 +602,7 @@ impl AtomicBool {
/// ```
#[inline]
#[stable(feature = "extended_compare_and_swap", since = "1.10.0")]
#[doc(alias = "compare_and_swap")]
#[cfg(target_has_atomic = "8")]
pub fn compare_exchange_weak(
&self,
Expand Down Expand Up @@ -1066,6 +1091,23 @@ impl<T> AtomicPtr<T> {
/// **Note:** This method is only available on platforms that support atomic
/// operations on pointers.
///
/// # Migrating to `compare_exchange` and `compare_exchange_weak`
///
/// `compare_and_swap` 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
///
/// `compare_exchange_weak` is allowed to fail spuriously even when the comparison succeeds,
/// which allows the compiler to generate better assembly code when the compare and swap
/// is used in a loop.
///
/// # Examples
///
/// ```
Expand All @@ -1080,6 +1122,10 @@ impl<T> AtomicPtr<T> {
/// ```
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_deprecated(
since = "1.50.0",
reason = "Use `compare_exchange` or `compare_exchange_weak` instead"
)]
#[cfg(target_has_atomic = "ptr")]
pub fn compare_and_swap(&self, current: *mut T, new: *mut T, order: Ordering) -> *mut T {
match self.compare_exchange(current, new, order, strongest_failure_ordering(order)) {
Expand All @@ -1094,9 +1140,10 @@ impl<T> AtomicPtr<T> {
/// the previous value. On success this value is guaranteed to be equal to `current`.
///
/// `compare_exchange` takes two [`Ordering`] arguments to describe the memory
/// ordering of this operation. The first describes the required ordering if the
/// operation succeeds while the second describes the required ordering when the
/// operation fails. Using [`Acquire`] as success ordering makes the store part
/// 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.
Expand Down Expand Up @@ -1157,9 +1204,10 @@ impl<T> AtomicPtr<T> {
/// previous value.
///
/// `compare_exchange_weak` takes two [`Ordering`] arguments to describe the memory
/// ordering of this operation. The first describes the required ordering if the
/// operation succeeds while the second describes the required ordering when the
/// operation fails. Using [`Acquire`] as success ordering makes the store part
/// 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.
Expand Down Expand Up @@ -1604,6 +1652,23 @@ happens, and using [`Release`] makes the load part [`Relaxed`].
**Note**: This method is only available on platforms that support atomic
operations on [`", $s_int_type, "`](", $int_ref, ").

# Migrating to `compare_exchange` and `compare_exchange_weak`

`compare_and_swap` 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

`compare_exchange_weak` is allowed to fail spuriously even when the comparison succeeds,
which allows the compiler to generate better assembly code when the compare and swap
is used in a loop.

# Examples

```
Expand All @@ -1619,6 +1684,10 @@ assert_eq!(some_var.load(Ordering::Relaxed), 10);
```"),
#[inline]
#[$stable]
#[rustc_deprecated(
since = "1.50.0",
reason = "Use `compare_exchange` or `compare_exchange_weak` instead")
]
#[$cfg_cas]
pub fn compare_and_swap(&self,
current: $int_type,
Expand All @@ -1643,9 +1712,10 @@ containing the previous value. On success this value is guaranteed to be equal t
`current`.

`compare_exchange` takes two [`Ordering`] arguments to describe the memory
ordering of this operation. The first describes the required ordering if the
operation succeeds while the second describes the required ordering when the
operation fails. Using [`Acquire`] as success ordering makes the store part
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.
Expand Down Expand Up @@ -1695,9 +1765,10 @@ platforms. The return value is a result indicating whether the new value was
written and containing the previous value.

`compare_exchange_weak` takes two [`Ordering`] arguments to describe the memory
ordering of this operation. The first describes the required ordering if the
operation succeeds while the second describes the required ordering when the
operation fails. Using [`Acquire`] as success ordering makes the store part
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.
Expand Down
6 changes: 3 additions & 3 deletions library/core/tests/atomic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ use core::sync::atomic::*;
#[test]
fn bool_() {
let a = AtomicBool::new(false);
assert_eq!(a.compare_and_swap(false, true, SeqCst), false);
assert_eq!(a.compare_and_swap(false, true, SeqCst), true);
assert_eq!(a.compare_exchange(false, true, SeqCst, SeqCst), Ok(false));
assert_eq!(a.compare_exchange(false, true, SeqCst, SeqCst), Err(true));

a.store(false, SeqCst);
assert_eq!(a.compare_and_swap(false, true, SeqCst), false);
assert_eq!(a.compare_exchange(false, true, SeqCst, SeqCst), Ok(false));
}

#[test]
Expand Down
6 changes: 5 additions & 1 deletion library/std/src/sync/mpsc/blocking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,11 @@ pub fn tokens() -> (WaitToken, SignalToken) {

impl SignalToken {
pub fn signal(&self) -> bool {
let wake = !self.inner.woken.compare_and_swap(false, true, Ordering::SeqCst);
let wake = self
.inner
.woken
.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
.is_ok();
if wake {
self.inner.thread.unpark();
}
Expand Down
14 changes: 11 additions & 3 deletions library/std/src/sync/mpsc/oneshot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ impl<T> Packet<T> {
let ptr = unsafe { signal_token.cast_to_usize() };

// race with senders to enter the blocking state
if self.state.compare_and_swap(EMPTY, ptr, Ordering::SeqCst) == EMPTY {
if self.state.compare_exchange(EMPTY, ptr, Ordering::SeqCst, Ordering::SeqCst).is_ok() {
if let Some(deadline) = deadline {
let timed_out = !wait_token.wait_max_until(deadline);
// Try to reset the state
Expand Down Expand Up @@ -161,7 +161,12 @@ impl<T> Packet<T> {
// the state changes under our feet we'd rather just see that state
// change.
DATA => {
self.state.compare_and_swap(DATA, EMPTY, Ordering::SeqCst);
let _ = self.state.compare_exchange(
DATA,
EMPTY,
Ordering::SeqCst,
Ordering::SeqCst,
);
match (&mut *self.data.get()).take() {
Some(data) => Ok(data),
None => unreachable!(),
Expand Down Expand Up @@ -264,7 +269,10 @@ impl<T> Packet<T> {

// If we've got a blocked thread, then use an atomic to gain ownership
// of it (may fail)
ptr => self.state.compare_and_swap(ptr, EMPTY, Ordering::SeqCst),
ptr => self
.state
.compare_exchange(ptr, EMPTY, Ordering::SeqCst, Ordering::SeqCst)
.unwrap_or_else(|x| x),
};

// Now that we've got ownership of our state, figure out what to do
Expand Down
11 changes: 9 additions & 2 deletions library/std/src/sync/mpsc/shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -385,8 +385,15 @@ impl<T> Packet<T> {
self.port_dropped.store(true, Ordering::SeqCst);
let mut steals = unsafe { *self.steals.get() };
while {
let cnt = self.cnt.compare_and_swap(steals, DISCONNECTED, Ordering::SeqCst);
cnt != DISCONNECTED && cnt != steals
match self.cnt.compare_exchange(
steals,
DISCONNECTED,
Ordering::SeqCst,
Ordering::SeqCst,
) {
Ok(_) => false,
Err(old) => old != DISCONNECTED,
}
} {
// See the discussion in 'try_recv' for why we yield
// control of this thread.
Expand Down
9 changes: 6 additions & 3 deletions library/std/src/sync/mpsc/stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -322,12 +322,15 @@ impl<T> Packet<T> {
// (because there is a bounded number of senders).
let mut steals = unsafe { *self.queue.consumer_addition().steals.get() };
while {
let cnt = self.queue.producer_addition().cnt.compare_and_swap(
match self.queue.producer_addition().cnt.compare_exchange(
steals,
DISCONNECTED,
Ordering::SeqCst,
);
cnt != DISCONNECTED && cnt != steals
Ordering::SeqCst,
) {
Ok(_) => false,
Err(old) => old != DISCONNECTED,
}
} {
while self.queue.pop().is_some() {
steals += 1;
Expand Down
18 changes: 12 additions & 6 deletions library/std/src/sync/once.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
// must do so with Release ordering to make the result available.
// - `wait` inserts `Waiter` nodes as a pointer in `state_and_queue`, and
// needs to make the nodes available with Release ordering. The load in
// its `compare_and_swap` can be Relaxed because it only has to compare
// its `compare_exchange` can be Relaxed because it only has to compare
// the atomic, not to read other data.
// - `WaiterQueue::Drop` must see the `Waiter` nodes, so it must load
// `state_and_queue` with Acquire ordering.
Expand Down Expand Up @@ -110,7 +110,7 @@ use crate::thread::{self, Thread};
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Once {
// `state_and_queue` is actually an a pointer to a `Waiter` with extra state
// `state_and_queue` is actually a pointer to a `Waiter` with extra state
// bits, so we add the `PhantomData` appropriately.
state_and_queue: AtomicUsize,
_marker: marker::PhantomData<*const Waiter>,
Expand Down Expand Up @@ -395,12 +395,13 @@ impl Once {
}
POISONED | INCOMPLETE => {
// Try to register this thread as the one RUNNING.
let old = self.state_and_queue.compare_and_swap(
let exchange_result = self.state_and_queue.compare_exchange(
faern marked this conversation as resolved.
Show resolved Hide resolved
state_and_queue,
RUNNING,
Ordering::Acquire,
Ordering::Acquire,
);
if old != state_and_queue {
if let Err(old) = exchange_result {
state_and_queue = old;
continue;
}
Expand Down Expand Up @@ -452,8 +453,13 @@ fn wait(state_and_queue: &AtomicUsize, mut current_state: usize) {

// Try to slide in the node at the head of the linked list, making sure
// that another thread didn't just replace the head of the linked list.
let old = state_and_queue.compare_and_swap(current_state, me | RUNNING, Ordering::Release);
if old != current_state {
let exchange_result = state_and_queue.compare_exchange(
current_state,
me | RUNNING,
Ordering::Release,
Ordering::Relaxed,
);
if let Err(old) = exchange_result {
current_state = old;
continue;
}
Expand Down
8 changes: 4 additions & 4 deletions library/std/src/sys/sgx/abi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,20 @@ unsafe extern "C" fn tcs_init(secondary: bool) {
}

// Try to atomically swap UNINIT with BUSY. The returned state can be:
match RELOC_STATE.compare_and_swap(UNINIT, BUSY, Ordering::Acquire) {
match RELOC_STATE.compare_exchange(UNINIT, BUSY, Ordering::Acquire, Ordering::Acquire) {
// This thread just obtained the lock and other threads will observe BUSY
UNINIT => {
Ok(_) => {
reloc::relocate_elf_rela();
RELOC_STATE.store(DONE, Ordering::Release);
}
// We need to wait until the initialization is done.
BUSY => {
Err(BUSY) => {
while RELOC_STATE.load(Ordering::Acquire) == BUSY {
core::hint::spin_loop();
}
}
// Initialization is done.
DONE => {}
Err(DONE) => {}
_ => unreachable!(),
}
}
Expand Down
2 changes: 1 addition & 1 deletion library/std/src/sys/sgx/waitqueue/spin_mutex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ impl<T> SpinMutex<T> {

#[inline(always)]
pub fn try_lock(&self) -> Option<SpinMutexGuard<'_, T>> {
if !self.lock.compare_and_swap(false, true, Ordering::Acquire) {
if self.lock.compare_exchange(false, true, Ordering::Acquire, Ordering::Acquire).is_ok() {
Some(SpinMutexGuard { mutex: self })
} else {
None
Expand Down
Loading