Skip to content

Commit a0f58c2

Browse files
committed
Revert "Optimize no-wake path of AtomicWaker::wake"
This reverts commit 3f93491.
1 parent 53700b8 commit a0f58c2

File tree

1 file changed

+64
-119
lines changed

1 file changed

+64
-119
lines changed

futures-core/src/task/__internal/atomic_waker.rs

Lines changed: 64 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use core::fmt;
22
use core::cell::UnsafeCell;
33
use core::sync::atomic::AtomicUsize;
4-
use core::sync::atomic::Ordering::{Acquire, Release, AcqRel, SeqCst};
4+
use core::sync::atomic::Ordering::{Acquire, Release, AcqRel};
55
use crate::task::{LocalWaker, Waker};
66

77
/// A synchronization primitive for task wakeup.
@@ -124,53 +124,14 @@ pub struct AtomicWaker {
124124
// Thread A still holds the `wake` lock, the call to `register` will result
125125
// in the task waking itself and get scheduled again.
126126

127-
/// Idle state empty
128-
const EMPTY: usize = 0;
129-
130-
/// Idle state with a waker in the cell
131-
const HOLDS: usize = 0b100;
127+
/// Idle state
128+
const WAITING: usize = 0;
132129

133130
/// A new waker value is being registered with the `AtomicWaker` cell.
134-
const REGISTERING: usize = 0b001;
131+
const REGISTERING: usize = 0b01;
135132

136133
/// The waker currently registered with the `AtomicWaker` cell is being woken.
137-
const WAKING: usize = 0b010;
138-
139-
// State transition diagram
140-
// Can be viewed/edited with service like this:
141-
// https://dreampuf.github.io/GraphvizOnline/
142-
/*
143-
144-
digraph G {
145-
// Any fixed-width font will do
146-
node [fontname=Monaco];
147-
148-
// Solid lines are transitions on function enter
149-
// and dashed lines are second transitions in a function call.
150-
// (There are at most two transitions per function call).
151-
152-
// register thread state transitions are red
153-
"EMPTY" -> "REGISTERING" [color=red];
154-
"HOLDS" -> "REGISTERING" [color=red];
155-
"REGISTERING" -> "HOLDS" [color=red style=dashed];
156-
"REGISTERING|WAKING" -> "EMPTY" [color=red style=dashed];
157-
158-
// wake thread state transitions are blue
159-
"REGISTERING" -> "REGISTERING|WAKING" [color=blue];
160-
"EMPTY" -> "WAKING" [color=blue];
161-
"HOLDS" -> "WAKING|HOLDS" [color=blue];
162-
"WAKING|HOLDS" -> "EMPTY" [color=blue style=dashed];
163-
"WAKING" -> "EMPTY" [color=blue style=dashed];
164-
165-
// intermediate states (when at least one process is running)
166-
"REGISTERING|WAKING" [style=dashed];
167-
"WAKING|HOLDS" [style=dashed];
168-
"WAKING" [style=dashed];
169-
"REGISTERING" [style=dashed];
170-
}
171-
172-
*/
173-
134+
const WAKING: usize = 0b10;
174135

175136
impl AtomicWaker {
176137
/// Create an `AtomicWaker`.
@@ -180,7 +141,7 @@ impl AtomicWaker {
180141
impl AssertSync for Waker {}
181142

182143
AtomicWaker {
183-
state: AtomicUsize::new(EMPTY),
144+
state: AtomicUsize::new(WAITING),
184145
waker: UnsafeCell::new(None),
185146
}
186147
}
@@ -237,66 +198,63 @@ impl AtomicWaker {
237198
/// }
238199
/// ```
239200
pub fn register(&self, lw: &LocalWaker) {
240-
let state = self.state.load(SeqCst);
241-
if (state == EMPTY || state == HOLDS)
242-
&& self.state.compare_and_swap(state, REGISTERING, Acquire) == state
243-
{
244-
return unsafe {
245-
debug_assert_eq!(state == HOLDS, (*self.waker.get()).is_some());
246-
247-
// Locked acquired, update the waker cell
248-
*self.waker.get() = Some(lw.clone().into_waker());
249-
250-
// Release the lock. If the state transitioned to include
251-
// the `WAKING` bit, this means that a wake has been
252-
// called concurrently, so we have to remove the waker and
253-
// wake it.`
254-
//
255-
// Start by assuming that the state is `REGISTERING` as this
256-
// is what we jut set it to.
257-
let res = self.state.compare_exchange(
258-
REGISTERING, HOLDS, AcqRel, Acquire);
259-
260-
match res {
261-
Ok(_) => {}
262-
Err(actual) => {
263-
// This branch can only be reached if a
264-
// concurrent thread called `wake`. In this
265-
// case, `actual` **must** be `REGISTERING |
266-
// `WAKING`.
267-
debug_assert_eq!(actual, REGISTERING | WAKING);
268-
269-
// Take the waker to wake once the atomic operation has
270-
// completed.
271-
let waker = (*self.waker.get()).take().unwrap();
272-
273-
// Just swap, because no one could change state while state == `REGISTERING` | `WAKING`.
274-
self.state.swap(EMPTY, AcqRel);
275-
276-
// The atomic swap was complete, now
277-
// wake the task and return.
278-
waker.wake();
201+
match self.state.compare_and_swap(WAITING, REGISTERING, Acquire) {
202+
WAITING => {
203+
unsafe {
204+
// Locked acquired, update the waker cell
205+
*self.waker.get() = Some(lw.clone().into_waker());
206+
207+
// Release the lock. If the state transitioned to include
208+
// the `WAKING` bit, this means that a wake has been
209+
// called concurrently, so we have to remove the waker and
210+
// wake it.`
211+
//
212+
// Start by assuming that the state is `REGISTERING` as this
213+
// is what we jut set it to.
214+
let res = self.state.compare_exchange(
215+
REGISTERING, WAITING, AcqRel, Acquire);
216+
217+
match res {
218+
Ok(_) => {}
219+
Err(actual) => {
220+
// This branch can only be reached if a
221+
// concurrent thread called `wake`. In this
222+
// case, `actual` **must** be `REGISTERING |
223+
// `WAKING`.
224+
debug_assert_eq!(actual, REGISTERING | WAKING);
225+
226+
// Take the waker to wake once the atomic operation has
227+
// completed.
228+
let waker = (*self.waker.get()).take().unwrap();
229+
230+
// Just swap, because no one could change state while state == `REGISTERING` | `WAKING`.
231+
self.state.swap(WAITING, AcqRel);
232+
233+
// The atomic swap was complete, now
234+
// wake the task and return.
235+
waker.wake();
236+
}
279237
}
280238
}
281239
}
282-
}
283-
284-
if state == WAKING || state == WAKING | HOLDS || state == EMPTY || state == HOLDS {
285-
// Currently in the process of waking the task, i.e.,
286-
// `wake` is currently being called on the old task handle.
287-
// So, we call wake on the new waker
288-
lw.wake();
289-
} else {
290-
// In this case, a concurrent thread is holding the
291-
// "registering" lock. This probably indicates a bug in the
292-
// caller's code as racing to call `register` doesn't make much
293-
// sense.
294-
//
295-
// We just want to maintain memory safety. It is ok to drop the
296-
// call to `register`.
297-
debug_assert!(
298-
state == REGISTERING ||
299-
state == REGISTERING | WAKING);
240+
WAKING => {
241+
// Currently in the process of waking the task, i.e.,
242+
// `wake` is currently being called on the old task handle.
243+
// So, we call wake on the new waker
244+
lw.wake();
245+
}
246+
state => {
247+
// In this case, a concurrent thread is holding the
248+
// "registering" lock. This probably indicates a bug in the
249+
// caller's code as racing to call `register` doesn't make much
250+
// sense.
251+
//
252+
// We just want to maintain memory safety. It is ok to drop the
253+
// call to `register`.
254+
debug_assert!(
255+
state == REGISTERING ||
256+
state == REGISTERING | WAKING);
257+
}
300258
}
301259
}
302260

@@ -318,27 +276,16 @@ impl AtomicWaker {
318276
///
319277
/// If a waker has not been registered, this returns `None`.
320278
pub fn take(&self) -> Option<Waker> {
321-
let state = self.state.load(SeqCst);
322-
323-
if state == EMPTY || state & WAKING != 0 {
324-
// One of:
325-
// * no waker inside, nothing to take
326-
// * another process is calling wake now
327-
return None;
328-
}
329-
330279
// AcqRel ordering is used in order to acquire the value of the `task`
331280
// cell as well as to establish a `release` ordering with whatever
332281
// memory the `AtomicWaker` is associated with.
333-
let state = self.state.fetch_or(WAKING, AcqRel);
334-
match state {
335-
EMPTY | HOLDS => {
282+
match self.state.fetch_or(WAKING, AcqRel) {
283+
WAITING => {
336284
// The waking lock has been acquired.
337285
let waker = unsafe { (*self.waker.get()).take() };
338-
debug_assert_eq!(state == HOLDS, waker.is_some());
339286

340287
// Release the lock
341-
self.state.fetch_and(!(WAKING | HOLDS), Release);
288+
self.state.fetch_and(!WAKING, Release);
342289

343290
waker
344291
}
@@ -353,9 +300,7 @@ impl AtomicWaker {
353300
debug_assert!(
354301
state == REGISTERING ||
355302
state == REGISTERING | WAKING ||
356-
state == WAKING ||
357-
state == WAKING | HOLDS);
358-
303+
state == WAKING);
359304
None
360305
}
361306
}

0 commit comments

Comments
 (0)