Skip to content

Commit 770044c

Browse files
authored
chore: replace deprecated compare_and_swap with compare_exchange (#3331)
1 parent c492926 commit 770044c

File tree

7 files changed

+63
-106
lines changed

7 files changed

+63
-106
lines changed

tokio/src/io/split.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,11 @@ impl<T: AsyncWrite> AsyncWrite for WriteHalf<T> {
131131

132132
impl<T> Inner<T> {
133133
fn poll_lock(&self, cx: &mut Context<'_>) -> Poll<Guard<'_, T>> {
134-
if !self.locked.compare_and_swap(false, true, Acquire) {
134+
if self
135+
.locked
136+
.compare_exchange(false, true, Acquire, Acquire)
137+
.is_ok()
138+
{
135139
Poll::Ready(Guard { inner: self })
136140
} else {
137141
// Spin... but investigate a better strategy

tokio/src/loom/std/atomic_u64.rs

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ mod imp {
2424
}
2525

2626
impl AtomicU64 {
27-
pub(crate) fn new(val: u64) -> AtomicU64 {
28-
AtomicU64 {
27+
pub(crate) fn new(val: u64) -> Self {
28+
Self {
2929
inner: Mutex::new(val),
3030
}
3131
}
@@ -45,16 +45,31 @@ mod imp {
4545
prev
4646
}
4747

48-
pub(crate) fn compare_and_swap(&self, old: u64, new: u64, _: Ordering) -> u64 {
48+
pub(crate) fn compare_exchange(
49+
&self,
50+
current: u64,
51+
new: u64,
52+
_success: Ordering,
53+
_failure: Ordering,
54+
) -> Result<u64, u64> {
4955
let mut lock = self.inner.lock().unwrap();
50-
let prev = *lock;
5156

52-
if prev != old {
53-
return prev;
57+
if *lock == current {
58+
*lock = new;
59+
Ok(current)
60+
} else {
61+
Err(*lock)
5462
}
63+
}
5564

56-
*lock = new;
57-
prev
65+
pub(crate) fn compare_exchange_weak(
66+
&self,
67+
current: u64,
68+
new: u64,
69+
success: Ordering,
70+
failure: Ordering,
71+
) -> Result<u64, u64> {
72+
self.compare_exchange(current, new, success, failure)
5873
}
5974
}
6075
}

tokio/src/runtime/queue.rs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::runtime::task;
88
use std::marker::PhantomData;
99
use std::mem::MaybeUninit;
1010
use std::ptr::{self, NonNull};
11-
use std::sync::atomic::Ordering::{AcqRel, Acquire, Release};
11+
use std::sync::atomic::Ordering::{AcqRel, Acquire, Relaxed, Release};
1212

1313
/// Producer handle. May only be used from a single thread.
1414
pub(super) struct Local<T: 'static> {
@@ -194,13 +194,17 @@ impl<T> Local<T> {
194194
// work. This is because all tasks are pushed into the queue from the
195195
// current thread (or memory has been acquired if the local queue handle
196196
// moved).
197-
let actual = self.inner.head.compare_and_swap(
198-
prev,
199-
pack(head.wrapping_add(n), head.wrapping_add(n)),
200-
Release,
201-
);
202-
203-
if actual != prev {
197+
if self
198+
.inner
199+
.head
200+
.compare_exchange(
201+
prev,
202+
pack(head.wrapping_add(n), head.wrapping_add(n)),
203+
Release,
204+
Relaxed,
205+
)
206+
.is_err()
207+
{
204208
// We failed to claim the tasks, losing the race. Return out of
205209
// this function and try the full `push` routine again. The queue
206210
// may not be full anymore.

tokio/src/sync/mpsc/block.rs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -258,13 +258,15 @@ impl<T> Block<T> {
258258
pub(crate) unsafe fn try_push(
259259
&self,
260260
block: &mut NonNull<Block<T>>,
261-
ordering: Ordering,
261+
success: Ordering,
262+
failure: Ordering,
262263
) -> Result<(), NonNull<Block<T>>> {
263264
block.as_mut().start_index = self.start_index.wrapping_add(BLOCK_CAP);
264265

265266
let next_ptr = self
266267
.next
267-
.compare_and_swap(ptr::null_mut(), block.as_ptr(), ordering);
268+
.compare_exchange(ptr::null_mut(), block.as_ptr(), success, failure)
269+
.unwrap_or_else(|x| x);
268270

269271
match NonNull::new(next_ptr) {
270272
Some(next_ptr) => Err(next_ptr),
@@ -306,11 +308,11 @@ impl<T> Block<T> {
306308
//
307309
// `Release` ensures that the newly allocated block is available to
308310
// other threads acquiring the next pointer.
309-
let next = NonNull::new(self.next.compare_and_swap(
310-
ptr::null_mut(),
311-
new_block.as_ptr(),
312-
AcqRel,
313-
));
311+
let next = NonNull::new(
312+
self.next
313+
.compare_exchange(ptr::null_mut(), new_block.as_ptr(), AcqRel, Acquire)
314+
.unwrap_or_else(|x| x),
315+
);
314316

315317
let next = match next {
316318
Some(next) => next,
@@ -333,7 +335,7 @@ impl<T> Block<T> {
333335

334336
// TODO: Should this iteration be capped?
335337
loop {
336-
let actual = unsafe { curr.as_ref().try_push(&mut new_block, AcqRel) };
338+
let actual = unsafe { curr.as_ref().try_push(&mut new_block, AcqRel, Acquire) };
337339

338340
curr = match actual {
339341
Ok(_) => {

tokio/src/sync/mpsc/list.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -140,11 +140,11 @@ impl<T> Tx<T> {
140140
//
141141
// Acquire is not needed as any "actual" value is not accessed.
142142
// At this point, the linked list is walked to acquire blocks.
143-
let actual =
144-
self.block_tail
145-
.compare_and_swap(block_ptr, next_block.as_ptr(), Release);
146-
147-
if actual == block_ptr {
143+
if self
144+
.block_tail
145+
.compare_exchange(block_ptr, next_block.as_ptr(), Release, Relaxed)
146+
.is_ok()
147+
{
148148
// Synchronize with any senders
149149
let tail_position = self.tail_position.fetch_add(0, Release);
150150

@@ -191,7 +191,7 @@ impl<T> Tx<T> {
191191

192192
// TODO: Unify this logic with Block::grow
193193
for _ in 0..3 {
194-
match curr.as_ref().try_push(&mut block, AcqRel) {
194+
match curr.as_ref().try_push(&mut block, AcqRel, Acquire) {
195195
Ok(_) => {
196196
reused = true;
197197
break;

tokio/src/sync/task/atomic_waker.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,11 @@ impl AtomicWaker {
171171
where
172172
W: WakerRef,
173173
{
174-
match self.state.compare_and_swap(WAITING, REGISTERING, Acquire) {
174+
match self
175+
.state
176+
.compare_exchange(WAITING, REGISTERING, Acquire, Acquire)
177+
.unwrap_or_else(|x| x)
178+
{
175179
WAITING => {
176180
unsafe {
177181
// Locked acquired, update the waker cell

tokio/src/time/driver/entry.rs

Lines changed: 2 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
//! refuse to mark the timer as pending.
5454
5555
use crate::loom::cell::UnsafeCell;
56+
use crate::loom::sync::atomic::AtomicU64;
5657
use crate::loom::sync::atomic::Ordering;
5758

5859
use crate::sync::AtomicWaker;
@@ -71,79 +72,6 @@ const STATE_DEREGISTERED: u64 = u64::max_value();
7172
const STATE_PENDING_FIRE: u64 = STATE_DEREGISTERED - 1;
7273
const STATE_MIN_VALUE: u64 = STATE_PENDING_FIRE;
7374

74-
/// Not all platforms support 64-bit compare-and-swap. This hack replaces the
75-
/// AtomicU64 with a mutex around a u64 on platforms that don't. This is slow,
76-
/// unfortunately, but 32-bit platforms are a bit niche so it'll do for now.
77-
///
78-
/// Note: We use "x86 or 64-bit pointers" as the condition here because
79-
/// target_has_atomic is not stable.
80-
#[cfg(all(
81-
not(tokio_force_time_entry_locked),
82-
any(target_arch = "x86", target_pointer_width = "64")
83-
))]
84-
type AtomicU64 = crate::loom::sync::atomic::AtomicU64;
85-
86-
#[cfg(not(all(
87-
not(tokio_force_time_entry_locked),
88-
any(target_arch = "x86", target_pointer_width = "64")
89-
)))]
90-
#[derive(Debug)]
91-
struct AtomicU64 {
92-
inner: crate::loom::sync::Mutex<u64>,
93-
}
94-
95-
#[cfg(not(all(
96-
not(tokio_force_time_entry_locked),
97-
any(target_arch = "x86", target_pointer_width = "64")
98-
)))]
99-
impl AtomicU64 {
100-
fn new(v: u64) -> Self {
101-
Self {
102-
inner: crate::loom::sync::Mutex::new(v),
103-
}
104-
}
105-
106-
fn load(&self, _order: Ordering) -> u64 {
107-
debug_assert_ne!(_order, Ordering::SeqCst); // we only provide AcqRel with the lock
108-
*self.inner.lock()
109-
}
110-
111-
fn store(&self, v: u64, _order: Ordering) {
112-
debug_assert_ne!(_order, Ordering::SeqCst); // we only provide AcqRel with the lock
113-
*self.inner.lock() = v;
114-
}
115-
116-
fn compare_exchange(
117-
&self,
118-
current: u64,
119-
new: u64,
120-
_success: Ordering,
121-
_failure: Ordering,
122-
) -> Result<u64, u64> {
123-
debug_assert_ne!(_success, Ordering::SeqCst); // we only provide AcqRel with the lock
124-
debug_assert_ne!(_failure, Ordering::SeqCst);
125-
126-
let mut lock = self.inner.lock();
127-
128-
if *lock == current {
129-
*lock = new;
130-
Ok(current)
131-
} else {
132-
Err(*lock)
133-
}
134-
}
135-
136-
fn compare_exchange_weak(
137-
&self,
138-
current: u64,
139-
new: u64,
140-
success: Ordering,
141-
failure: Ordering,
142-
) -> Result<u64, u64> {
143-
self.compare_exchange(current, new, success, failure)
144-
}
145-
}
146-
14775
/// This structure holds the current shared state of the timer - its scheduled
14876
/// time (if registered), or otherwise the result of the timer completing, as
14977
/// well as the registered waker.
@@ -300,7 +228,7 @@ impl StateCell {
300228
/// expiration time.
301229
///
302230
/// While this function is memory-safe, it should only be called from a
303-
/// context holding both `&mut TimerEntry` and the driver lock.
231+
/// context holding both `&mut TimerEntry` and the driver lock.
304232
fn set_expiration(&self, timestamp: u64) {
305233
debug_assert!(timestamp < STATE_MIN_VALUE);
306234

0 commit comments

Comments
 (0)