diff --git a/src/libstd/sync/once.rs b/src/libstd/sync/once.rs index 01cb7582d38..2c09fb3318b 100644 --- a/src/libstd/sync/once.rs +++ b/src/libstd/sync/once.rs @@ -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 {