Simplify loop conditions in RUNNING and add comments
This commit is contained in:
parent
2ab812c181
commit
7f1e166899
1 changed files with 30 additions and 18 deletions
|
@ -354,7 +354,7 @@ impl Once {
|
||||||
// SeqCst minimizes the chances of something going wrong.
|
// SeqCst minimizes the chances of something going wrong.
|
||||||
let mut state_and_queue = self.state_and_queue.load(Ordering::SeqCst);
|
let mut state_and_queue = self.state_and_queue.load(Ordering::SeqCst);
|
||||||
|
|
||||||
'outer: loop {
|
loop {
|
||||||
match state_and_queue {
|
match state_and_queue {
|
||||||
// If we're complete, then there's nothing to do, we just
|
// If we're complete, then there's nothing to do, we just
|
||||||
// jettison out as we shouldn't run the closure.
|
// jettison out as we shouldn't run the closure.
|
||||||
|
@ -401,33 +401,45 @@ impl Once {
|
||||||
// not RUNNING.
|
// not RUNNING.
|
||||||
_ => {
|
_ => {
|
||||||
assert!(state_and_queue & STATE_MASK == RUNNING);
|
assert!(state_and_queue & STATE_MASK == RUNNING);
|
||||||
|
// Create the node for our current thread that we are going to try to slot
|
||||||
|
// in at the head of the linked list.
|
||||||
let mut node = Waiter {
|
let mut node = Waiter {
|
||||||
thread: Some(thread::current()),
|
thread: Some(thread::current()),
|
||||||
signaled: AtomicBool::new(false),
|
signaled: AtomicBool::new(false),
|
||||||
next: ptr::null_mut(),
|
next: ptr::null_mut(),
|
||||||
};
|
};
|
||||||
let me = &mut node as *mut Waiter as usize;
|
let me = &mut node as *mut Waiter as usize;
|
||||||
assert!(me & STATE_MASK == 0);
|
assert!(me & STATE_MASK == 0); // We assume pointers have 2 free bits that
|
||||||
|
// we can use for state.
|
||||||
|
|
||||||
while state_and_queue & STATE_MASK == RUNNING {
|
// Try to slide in the node at the head of the linked list.
|
||||||
node.next = (state_and_queue & !STATE_MASK) as *mut Waiter;
|
// Run in a loop where we make sure the status is still RUNNING, and that
|
||||||
let old = self.state_and_queue.compare_and_swap(state_and_queue,
|
// another thread did not just replace the head of the linked list.
|
||||||
|
let mut old_head_and_status = state_and_queue;
|
||||||
|
loop {
|
||||||
|
if old_head_and_status & STATE_MASK != RUNNING {
|
||||||
|
return; // No need anymore to enqueue ourselves.
|
||||||
|
}
|
||||||
|
|
||||||
|
node.next = (old_head_and_status & !STATE_MASK) as *mut Waiter;
|
||||||
|
let old = self.state_and_queue.compare_and_swap(old_head_and_status,
|
||||||
me | RUNNING,
|
me | RUNNING,
|
||||||
Ordering::SeqCst);
|
Ordering::Release);
|
||||||
if old != state_and_queue {
|
if old == old_head_and_status {
|
||||||
state_and_queue = old;
|
break; // Success!
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
old_head_and_status = old;
|
||||||
// Once we've enqueued ourselves, wait in a loop.
|
|
||||||
// Afterwards reload the state and continue with what we
|
|
||||||
// were doing from before.
|
|
||||||
while !node.signaled.load(Ordering::SeqCst) {
|
|
||||||
thread::park();
|
|
||||||
}
|
|
||||||
state_and_queue = self.state_and_queue.load(Ordering::SeqCst);
|
|
||||||
continue 'outer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We have enqueued ourselves, now lets wait.
|
||||||
|
// It is important not to return before being signaled, otherwise we would
|
||||||
|
// drop our `Waiter` node and leave a hole in the linked list (and a
|
||||||
|
// dangling reference). Guard against spurious wakeups by reparking
|
||||||
|
// ourselves until we are signaled.
|
||||||
|
while !node.signaled.load(Ordering::SeqCst) {
|
||||||
|
thread::park();
|
||||||
|
}
|
||||||
|
state_and_queue = self.state_and_queue.load(Ordering::SeqCst);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue