Move thread parking to a seperate function
This commit is contained in:
parent
fbc242f1ef
commit
2e8eb5f33d
1 changed files with 42 additions and 38 deletions
|
@ -401,44 +401,7 @@ impl Once {
|
|||
// not 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 {
|
||||
thread: thread::current(),
|
||||
signaled: AtomicBool::new(false),
|
||||
next: ptr::null(),
|
||||
};
|
||||
let me = &node as *const Waiter as usize;
|
||||
assert!(me & STATE_MASK == 0); // We assume pointers have 2 free bits that
|
||||
// we can use for state.
|
||||
|
||||
// Try to slide in the node at the head of the linked list.
|
||||
// Run in a loop where we make sure the status is still RUNNING, and that
|
||||
// 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 *const Waiter;
|
||||
let old = self.state_and_queue.compare_and_swap(old_head_and_status,
|
||||
me | RUNNING,
|
||||
Ordering::Release);
|
||||
if old == old_head_and_status {
|
||||
break; // Success!
|
||||
}
|
||||
old_head_and_status = old;
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
wait(&self.state_and_queue, state_and_queue);
|
||||
state_and_queue = self.state_and_queue.load(Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
|
@ -446,6 +409,47 @@ impl Once {
|
|||
}
|
||||
}
|
||||
|
||||
fn wait(state_and_queue: &AtomicUsize, current_state: usize) {
|
||||
// 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 {
|
||||
thread: thread::current(),
|
||||
signaled: AtomicBool::new(false),
|
||||
next: ptr::null(),
|
||||
};
|
||||
let me = &node as *const Waiter as usize;
|
||||
assert!(me & STATE_MASK == 0); // We assume pointers have 2 free bits that
|
||||
// we can use for state.
|
||||
|
||||
// Try to slide in the node at the head of the linked list.
|
||||
// Run in a loop where we make sure the status is still RUNNING, and that
|
||||
// another thread did not just replace the head of the linked list.
|
||||
let mut old_head_and_status = current_state;
|
||||
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 *const Waiter;
|
||||
let old = state_and_queue.compare_and_swap(old_head_and_status,
|
||||
me | RUNNING,
|
||||
Ordering::Release);
|
||||
if old == old_head_and_status {
|
||||
break; // Success!
|
||||
}
|
||||
old_head_and_status = old;
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "std_debug", since = "1.16.0")]
|
||||
impl fmt::Debug for Once {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue