@@ -141,9 +141,15 @@ impl StaticKey {
141
141
panic ! ( "out of TLS indexes" ) ;
142
142
}
143
143
144
- self . key . store ( key + 1 , Release ) ;
145
144
register_dtor ( self ) ;
146
145
146
+ // Release-storing the key needs to be the last thing we do.
147
+ // This is because in `fn key()`, other threads will do an acquire load of the key,
148
+ // and if that sees this write then it will entirely bypass the `InitOnce`. We thus
149
+ // need to establish synchronization through `key`. In particular that acquire load
150
+ // must happen-after the register_dtor above, to ensure the dtor actually runs!
151
+ self . key . store ( key + 1 , Release ) ;
152
+
147
153
let r = c:: InitOnceComplete ( self . once . get ( ) , 0 , ptr:: null_mut ( ) ) ;
148
154
debug_assert_eq ! ( r, c:: TRUE ) ;
149
155
@@ -313,17 +319,25 @@ unsafe fn run_dtors() {
313
319
// Use acquire ordering to observe key initialization.
314
320
let mut cur = DTORS . load ( Acquire ) ;
315
321
while !cur. is_null ( ) {
316
- let key = ( * cur) . key . load ( Relaxed ) - 1 ;
322
+ let pre_key = ( * cur) . key . load ( Relaxed ) ;
317
323
let dtor = ( * cur) . dtor . unwrap ( ) ;
324
+ cur = ( * cur) . next . load ( Relaxed ) ;
325
+
326
+ // In StaticKey::init, we register the dtor before setting `key`.
327
+ // So if one thread's `run_dtors` races with another thread executing `init` on the same `StaticKey`,
328
+ // we can encounter a key of 0 here. That means this key was never initialized in this thread
329
+ // so we can safely skip it.
330
+ if pre_key == 0 {
331
+ continue ;
332
+ }
333
+ let key = pre_key - 1 ;
318
334
319
335
let ptr = c:: TlsGetValue ( key) ;
320
336
if !ptr. is_null ( ) {
321
337
c:: TlsSetValue ( key, ptr:: null_mut ( ) ) ;
322
338
dtor ( ptr as * mut _ ) ;
323
339
any_run = true ;
324
340
}
325
-
326
- cur = ( * cur) . next . load ( Relaxed ) ;
327
341
}
328
342
329
343
if !any_run {
0 commit comments