]> git.lizzy.rs Git - rust.git/commitdiff
Turn Finish into WaiterQueue
authorPaul Dicker <pitdicker@gmail.com>
Wed, 23 Oct 2019 09:44:31 +0000 (11:44 +0200)
committerPaul Dicker <pitdicker@gmail.com>
Thu, 24 Oct 2019 15:28:04 +0000 (17:28 +0200)
src/libstd/sync/once.rs

index d8565e55ab2fcff24114083174a0a8d1d25371a7..01cb7582d38dc6d5dbfc44b5467b6572bdbbb1ad 100644 (file)
@@ -139,13 +139,15 @@ struct Waiter {
     next: *const Waiter,
 }
 
-// Helper struct used to clean up after a closure call with a `Drop`
-// implementation to also run on panic.
-struct Finish<'a> {
-    panicked: bool,
-    me: &'a Once,
+// Head of a linked list of waiters.
+// Every node is a struct on the stack of a waiting thread.
+// Will wake up the waiters when it gets dropped, i.e. also on panic.
+struct WaiterQueue<'a> {
+    state_and_queue: &'a AtomicUsize,
+    set_state_on_drop_to: usize,
 }
 
+
 impl Once {
     /// Creates a new `Once` value.
     #[stable(feature = "once_new", since = "1.2.0")]
@@ -379,18 +381,16 @@ fn call_inner(&self,
                         state_and_queue = old;
                         continue
                     }
-
-                    // Run the initialization routine, letting it know if we're
-                    // poisoned or not. The `Finish` struct is then dropped, and
-                    // the `Drop` implementation here is responsible for waking
-                    // up other waiters both in the normal return and panicking
-                    // case.
-                    let mut complete = Finish {
-                        panicked: true,
-                        me: self,
+                    // `waiter_queue` will manage other waiting threads, and
+                    // wake them up on drop.
+                    let mut waiter_queue = WaiterQueue {
+                        state_and_queue: &self.state_and_queue,
+                        set_state_on_drop_to: POISONED,
                     };
+                    // Run the initialization function, letting it know if we're
+                    // poisoned or not.
                     init(state_and_queue == POISONED);
-                    complete.panicked = false;
+                    waiter_queue.set_state_on_drop_to = COMPLETE;
                     return
                 }
 
@@ -453,15 +453,13 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
     }
 }
 
-impl Drop for Finish<'_> {
+impl Drop for WaiterQueue<'_> {
     fn drop(&mut self) {
-        // Swap out our state with however we finished. We should only ever see
-        // an old state which was RUNNING.
-        let state_and_queue = if self.panicked {
-            self.me.state_and_queue.swap(POISONED, Ordering::SeqCst)
-        } else {
-            self.me.state_and_queue.swap(COMPLETE, Ordering::SeqCst)
-        };
+        // Swap out our state with however we finished.
+        let state_and_queue = self.state_and_queue.swap(self.set_state_on_drop_to,
+                                                        Ordering::SeqCst);
+
+        // We should only ever see an old state which was RUNNING.
         assert_eq!(state_and_queue & STATE_MASK, RUNNING);
 
         // Decode the RUNNING to a list of waiters, then walk that entire list