@@ -139,13 +139,15 @@ struct Waiter {
139
139
next : * const Waiter ,
140
140
}
141
141
142
- // Helper struct used to clean up after a closure call with a `Drop`
143
- // implementation to also run on panic.
144
- struct Finish < ' a > {
145
- panicked : bool ,
146
- me : & ' a Once ,
142
+ // Head of a linked list of waiters.
143
+ // Every node is a struct on the stack of a waiting thread.
144
+ // Will wake up the waiters when it gets dropped, i.e. also on panic.
145
+ struct WaiterQueue < ' a > {
146
+ state_and_queue : & ' a AtomicUsize ,
147
+ set_state_on_drop_to : usize ,
147
148
}
148
149
150
+
149
151
impl Once {
150
152
/// Creates a new `Once` value.
151
153
#[ stable( feature = "once_new" , since = "1.2.0" ) ]
@@ -379,18 +381,16 @@ impl Once {
379
381
state_and_queue = old;
380
382
continue
381
383
}
382
-
383
- // Run the initialization routine, letting it know if we're
384
- // poisoned or not. The `Finish` struct is then dropped, and
385
- // the `Drop` implementation here is responsible for waking
386
- // up other waiters both in the normal return and panicking
387
- // case.
388
- let mut complete = Finish {
389
- panicked : true ,
390
- me : self ,
384
+ // `waiter_queue` will manage other waiting threads, and
385
+ // wake them up on drop.
386
+ let mut waiter_queue = WaiterQueue {
387
+ state_and_queue : & self . state_and_queue ,
388
+ set_state_on_drop_to : POISONED ,
391
389
} ;
390
+ // Run the initialization function, letting it know if we're
391
+ // poisoned or not.
392
392
init ( state_and_queue == POISONED ) ;
393
- complete . panicked = false ;
393
+ waiter_queue . set_state_on_drop_to = COMPLETE ;
394
394
return
395
395
}
396
396
@@ -453,15 +453,13 @@ impl fmt::Debug for Once {
453
453
}
454
454
}
455
455
456
- impl Drop for Finish < ' _ > {
456
+ impl Drop for WaiterQueue < ' _ > {
457
457
fn drop ( & mut self ) {
458
- // Swap out our state with however we finished. We should only ever see
459
- // an old state which was RUNNING.
460
- let state_and_queue = if self . panicked {
461
- self . me . state_and_queue . swap ( POISONED , Ordering :: SeqCst )
462
- } else {
463
- self . me . state_and_queue . swap ( COMPLETE , Ordering :: SeqCst )
464
- } ;
458
+ // Swap out our state with however we finished.
459
+ let state_and_queue = self . state_and_queue . swap ( self . set_state_on_drop_to ,
460
+ Ordering :: SeqCst ) ;
461
+
462
+ // We should only ever see an old state which was RUNNING.
465
463
assert_eq ! ( state_and_queue & STATE_MASK , RUNNING ) ;
466
464
467
465
// Decode the RUNNING to a list of waiters, then walk that entire list
0 commit comments