2
2
3
3
#![ unstable( feature = "thread_local_internals" , issue = "0" ) ]
4
4
5
- use crate :: cell:: UnsafeCell ;
6
5
use crate :: fmt;
7
- use crate :: hint;
8
- use crate :: mem;
9
6
10
7
/// A thread local storage key which owns its contents.
11
8
///
@@ -92,10 +89,7 @@ pub struct LocalKey<T: 'static> {
92
89
// trivially devirtualizable by LLVM because the value of `inner` never
93
90
// changes and the constant should be readonly within a crate. This mainly
94
91
// only runs into problems when TLS statics are exported across crates.
95
- inner : unsafe fn ( ) -> Option < & ' static UnsafeCell < Option < T > > > ,
96
-
97
- // initialization routine to invoke to create a value
98
- init : fn ( ) -> T ,
92
+ inner : unsafe fn ( ) -> Option < & ' static T > ,
99
93
}
100
94
101
95
#[ stable( feature = "std_debug" , since = "1.16.0" ) ]
@@ -159,10 +153,7 @@ macro_rules! __thread_local_inner {
159
153
#[ inline]
160
154
fn __init( ) -> $t { $init }
161
155
162
- unsafe fn __getit( ) -> $crate:: option:: Option <
163
- & ' static $crate:: cell:: UnsafeCell <
164
- $crate:: option:: Option <$t>>>
165
- {
156
+ unsafe fn __getit( ) -> $crate:: option:: Option <& ' static $t> {
166
157
#[ cfg( all( target_arch = "wasm32" , not( target_feature = "atomics" ) ) ) ]
167
158
static __KEY: $crate:: thread:: __StaticLocalKeyInner<$t> =
168
159
$crate:: thread:: __StaticLocalKeyInner:: new( ) ;
@@ -182,11 +173,11 @@ macro_rules! __thread_local_inner {
182
173
static __KEY: $crate:: thread:: __OsLocalKeyInner<$t> =
183
174
$crate:: thread:: __OsLocalKeyInner:: new( ) ;
184
175
185
- __KEY. get( )
176
+ __KEY. get( __init )
186
177
}
187
178
188
179
unsafe {
189
- $crate:: thread:: LocalKey :: new( __getit, __init )
180
+ $crate:: thread:: LocalKey :: new( __getit)
190
181
}
191
182
}
192
183
} ;
@@ -221,11 +212,9 @@ impl<T: 'static> LocalKey<T> {
221
212
#[ unstable( feature = "thread_local_internals" ,
222
213
reason = "recently added to create a key" ,
223
214
issue = "0" ) ]
224
- pub const unsafe fn new ( inner : unsafe fn ( ) -> Option < & ' static UnsafeCell < Option < T > > > ,
225
- init : fn ( ) -> T ) -> LocalKey < T > {
215
+ pub const unsafe fn new ( inner : unsafe fn ( ) -> Option < & ' static T > ) -> LocalKey < T > {
226
216
LocalKey {
227
217
inner,
228
- init,
229
218
}
230
219
}
231
220
@@ -246,37 +235,6 @@ impl<T: 'static> LocalKey<T> {
246
235
after it is destroyed")
247
236
}
248
237
249
- unsafe fn init ( & self , slot : & UnsafeCell < Option < T > > ) -> & T {
250
- // Execute the initialization up front, *then* move it into our slot,
251
- // just in case initialization fails.
252
- let value = ( self . init ) ( ) ;
253
- let ptr = slot. get ( ) ;
254
-
255
- // note that this can in theory just be `*ptr = Some(value)`, but due to
256
- // the compiler will currently codegen that pattern with something like:
257
- //
258
- // ptr::drop_in_place(ptr)
259
- // ptr::write(ptr, Some(value))
260
- //
261
- // Due to this pattern it's possible for the destructor of the value in
262
- // `ptr` (e.g., if this is being recursively initialized) to re-access
263
- // TLS, in which case there will be a `&` and `&mut` pointer to the same
264
- // value (an aliasing violation). To avoid setting the "I'm running a
265
- // destructor" flag we just use `mem::replace` which should sequence the
266
- // operations a little differently and make this safe to call.
267
- mem:: replace ( & mut * ptr, Some ( value) ) ;
268
-
269
- // After storing `Some` we want to get a reference to the contents of
270
- // what we just stored. While we could use `unwrap` here and it should
271
- // always work it empirically doesn't seem to always get optimized away,
272
- // which means that using something like `try_with` can pull in
273
- // panicking code and cause a large size bloat.
274
- match * ptr {
275
- Some ( ref x) => x,
276
- None => hint:: unreachable_unchecked ( ) ,
277
- }
278
- }
279
-
280
238
/// Acquires a reference to the value in this TLS key.
281
239
///
282
240
/// This will lazily initialize the value if this thread has not referenced
@@ -293,13 +251,68 @@ impl<T: 'static> LocalKey<T> {
293
251
F : FnOnce ( & T ) -> R ,
294
252
{
295
253
unsafe {
296
- let slot = ( self . inner ) ( ) . ok_or ( AccessError {
254
+ let thread_local = ( self . inner ) ( ) . ok_or ( AccessError {
297
255
_private : ( ) ,
298
256
} ) ?;
299
- Ok ( f ( match * slot. get ( ) {
300
- Some ( ref inner) => inner,
301
- None => self . init ( slot) ,
302
- } ) )
257
+ Ok ( f ( thread_local) )
258
+ }
259
+ }
260
+ }
261
+
262
+ mod lazy {
263
+ use crate :: cell:: UnsafeCell ;
264
+ use crate :: mem;
265
+ use crate :: hint;
266
+
267
+ pub struct LazyKeyInner < T > {
268
+ inner : UnsafeCell < Option < T > > ,
269
+ }
270
+
271
+ impl < T > LazyKeyInner < T > {
272
+ pub const fn new ( ) -> LazyKeyInner < T > {
273
+ LazyKeyInner {
274
+ inner : UnsafeCell :: new ( None ) ,
275
+ }
276
+ }
277
+
278
+ pub unsafe fn get ( & self ) -> Option < & ' static T > {
279
+ ( * self . inner . get ( ) ) . as_ref ( )
280
+ }
281
+
282
+ pub unsafe fn initialize < F : FnOnce ( ) -> T > ( & self , init : F ) -> & ' static T {
283
+ // Execute the initialization up front, *then* move it into our slot,
284
+ // just in case initialization fails.
285
+ let value = init ( ) ;
286
+ let ptr = self . inner . get ( ) ;
287
+
288
+ // note that this can in theory just be `*ptr = Some(value)`, but due to
289
+ // the compiler will currently codegen that pattern with something like:
290
+ //
291
+ // ptr::drop_in_place(ptr)
292
+ // ptr::write(ptr, Some(value))
293
+ //
294
+ // Due to this pattern it's possible for the destructor of the value in
295
+ // `ptr` (e.g., if this is being recursively initialized) to re-access
296
+ // TLS, in which case there will be a `&` and `&mut` pointer to the same
297
+ // value (an aliasing violation). To avoid setting the "I'm running a
298
+ // destructor" flag we just use `mem::replace` which should sequence the
299
+ // operations a little differently and make this safe to call.
300
+ mem:: replace ( & mut * ptr, Some ( value) ) ;
301
+
302
+ // After storing `Some` we want to get a reference to the contents of
303
+ // what we just stored. While we could use `unwrap` here and it should
304
+ // always work it empirically doesn't seem to always get optimized away,
305
+ // which means that using something like `try_with` can pull in
306
+ // panicking code and cause a large size bloat.
307
+ match * ptr {
308
+ Some ( ref x) => x,
309
+ None => hint:: unreachable_unchecked ( ) ,
310
+ }
311
+ }
312
+
313
+ #[ allow( unused) ]
314
+ pub unsafe fn take ( & mut self ) -> Option < T > {
315
+ ( * self . inner . get ( ) ) . take ( )
303
316
}
304
317
}
305
318
}
@@ -309,11 +322,11 @@ impl<T: 'static> LocalKey<T> {
309
322
#[ doc( hidden) ]
310
323
#[ cfg( all( target_arch = "wasm32" , not( target_feature = "atomics" ) ) ) ]
311
324
pub mod statik {
312
- use crate :: cell :: UnsafeCell ;
325
+ use super :: lazy :: LazyKeyInner ;
313
326
use crate :: fmt;
314
327
315
328
pub struct Key < T > {
316
- inner : UnsafeCell < Option < T > > ,
329
+ inner : LazyKeyInner < T > ,
317
330
}
318
331
319
332
unsafe impl < T > Sync for Key < T > { }
@@ -327,32 +340,55 @@ pub mod statik {
327
340
impl < T > Key < T > {
328
341
pub const fn new ( ) -> Key < T > {
329
342
Key {
330
- inner : UnsafeCell :: new ( None ) ,
343
+ inner : LazyKeyInner :: new ( ) ,
331
344
}
332
345
}
333
346
334
- pub unsafe fn get ( & self ) -> Option < & ' static UnsafeCell < Option < T > > > {
335
- Some ( & * ( & self . inner as * const _ ) )
347
+ pub unsafe fn get ( & self , init : fn ( ) -> T ) -> Option < & ' static T > {
348
+ let value = match self . inner . get ( ) {
349
+ Some ( ref value) => value,
350
+ None => self . inner . initialize ( init) ,
351
+ } ;
352
+ Some ( value)
336
353
}
337
354
}
338
355
}
339
356
340
357
#[ doc( hidden) ]
341
358
#[ cfg( target_thread_local) ]
342
359
pub mod fast {
343
- use crate :: cell:: { Cell , UnsafeCell } ;
360
+ use super :: lazy:: LazyKeyInner ;
361
+ use crate :: cell:: Cell ;
344
362
use crate :: fmt;
345
363
use crate :: mem;
346
- use crate :: ptr;
347
- use crate :: sys:: fast_thread_local:: { register_dtor, requires_move_before_drop} ;
364
+ use crate :: sys:: fast_thread_local:: register_dtor;
348
365
366
+ #[ derive( Copy , Clone ) ]
367
+ enum DtorState {
368
+ Unregistered ,
369
+ Registered ,
370
+ RunningOrHasRun ,
371
+ }
372
+
373
+ // This data structure has been carefully constructed so that the fast path
374
+ // only contains one branch on x86. That optimization is necessary to avoid
375
+ // duplicated tls lookups on OSX.
376
+ //
377
+ // LLVM issue: https://bugs.llvm.org/show_bug.cgi?id=41722
349
378
pub struct Key < T > {
350
- inner : UnsafeCell < Option < T > > ,
379
+ // If `LazyKeyInner::get` returns `None`, that indicates either:
380
+ // * The value has never been initialized
381
+ // * The value is being recursively initialized
382
+ // * The value has already been destroyed or is being destroyed
383
+ // To determine which kind of `None`, check `dtor_state`.
384
+ //
385
+ // This is very optimizer friendly for the fast path - initialized but
386
+ // not yet dropped.
387
+ inner : LazyKeyInner < T > ,
351
388
352
389
// Metadata to keep track of the state of the destructor. Remember that
353
- // these variables are thread-local, not global.
354
- dtor_registered : Cell < bool > ,
355
- dtor_running : Cell < bool > ,
390
+ // this variable is thread-local, not global.
391
+ dtor_state : Cell < DtorState > ,
356
392
}
357
393
358
394
impl < T > fmt:: Debug for Key < T > {
@@ -364,54 +400,75 @@ pub mod fast {
364
400
impl < T > Key < T > {
365
401
pub const fn new ( ) -> Key < T > {
366
402
Key {
367
- inner : UnsafeCell :: new ( None ) ,
368
- dtor_registered : Cell :: new ( false ) ,
369
- dtor_running : Cell :: new ( false )
403
+ inner : LazyKeyInner :: new ( ) ,
404
+ dtor_state : Cell :: new ( DtorState :: Unregistered ) ,
370
405
}
371
406
}
372
407
373
- pub unsafe fn get ( & self ) -> Option < & ' static UnsafeCell < Option < T > > > {
374
- if mem:: needs_drop :: < T > ( ) && self . dtor_running . get ( ) {
375
- return None
408
+ pub unsafe fn get < F : FnOnce ( ) -> T > ( & self , init : F ) -> Option < & ' static T > {
409
+ match self . inner . get ( ) {
410
+ Some ( val) => Some ( val) ,
411
+ None => self . try_initialize ( init) ,
376
412
}
377
- self . register_dtor ( ) ;
378
- Some ( & * ( & self . inner as * const _ ) )
379
413
}
380
414
381
- unsafe fn register_dtor ( & self ) {
382
- if !mem:: needs_drop :: < T > ( ) || self . dtor_registered . get ( ) {
383
- return
415
+ // `try_initialize` is only called once per fast thread local variable,
416
+ // except in corner cases where thread_local dtors reference other
417
+ // thread_local's, or it is being recursively initialized.
418
+ //
419
+ // Macos: Inlining this function can cause two `tlv_get_addr` calls to
420
+ // be performed for every call to `Key::get`. The #[cold] hint makes
421
+ // that less likely.
422
+ // LLVM issue: https://bugs.llvm.org/show_bug.cgi?id=41722
423
+ #[ cold]
424
+ unsafe fn try_initialize < F : FnOnce ( ) -> T > ( & self , init : F ) -> Option < & ' static T > {
425
+ if !mem:: needs_drop :: < T > ( ) || self . try_register_dtor ( ) {
426
+ Some ( self . inner . initialize ( init) )
427
+ } else {
428
+ None
384
429
}
430
+ }
385
431
386
- register_dtor ( self as * const _ as * mut u8 ,
387
- destroy_value :: < T > ) ;
388
- self . dtor_registered . set ( true ) ;
432
+ // `try_register_dtor` is only called once per fast thread local
433
+ // variable, except in corner cases where thread_local dtors reference
434
+ // other thread_local's, or it is being recursively initialized.
435
+ unsafe fn try_register_dtor ( & self ) -> bool {
436
+ match self . dtor_state . get ( ) {
437
+ DtorState :: Unregistered => {
438
+ // dtor registration happens before initialization.
439
+ register_dtor ( self as * const _ as * mut u8 ,
440
+ destroy_value :: < T > ) ;
441
+ self . dtor_state . set ( DtorState :: Registered ) ;
442
+ true
443
+ }
444
+ DtorState :: Registered => {
445
+ // recursively initialized
446
+ true
447
+ }
448
+ DtorState :: RunningOrHasRun => {
449
+ false
450
+ }
451
+ }
389
452
}
390
453
}
391
454
392
455
unsafe extern fn destroy_value < T > ( ptr : * mut u8 ) {
393
456
let ptr = ptr as * mut Key < T > ;
394
- // Right before we run the user destructor be sure to flag the
395
- // destructor as running for this thread so calls to `get` will return
396
- // `None`.
397
- ( * ptr) . dtor_running . set ( true ) ;
398
457
399
- // Some implementations may require us to move the value before we drop
400
- // it as it could get re-initialized in-place during destruction.
401
- //
402
- // Hence, we use `ptr::read` on those platforms (to move to a "safe"
403
- // location) instead of drop_in_place.
404
- if requires_move_before_drop ( ) {
405
- ptr:: read ( ( * ptr) . inner . get ( ) ) ;
406
- } else {
407
- ptr:: drop_in_place ( ( * ptr) . inner . get ( ) ) ;
408
- }
458
+ // Right before we run the user destructor be sure to set the
459
+ // `Option<T>` to `None`, and `dtor_state` to `RunningOrHasRun`. This
460
+ // causes future calls to `get` to run `try_initialize_drop` again,
461
+ // which will now fail, and return `None`.
462
+ let value = ( * ptr) . inner . take ( ) ;
463
+ ( * ptr) . dtor_state . set ( DtorState :: RunningOrHasRun ) ;
464
+ drop ( value) ;
409
465
}
410
466
}
411
467
412
468
#[ doc( hidden) ]
413
469
pub mod os {
414
- use crate :: cell:: { Cell , UnsafeCell } ;
470
+ use super :: lazy:: LazyKeyInner ;
471
+ use crate :: cell:: Cell ;
415
472
use crate :: fmt;
416
473
use crate :: marker;
417
474
use crate :: ptr;
@@ -432,8 +489,8 @@ pub mod os {
432
489
unsafe impl < T > Sync for Key < T > { }
433
490
434
491
struct Value < T : ' static > {
492
+ inner : LazyKeyInner < T > ,
435
493
key : & ' static Key < T > ,
436
- value : UnsafeCell < Option < T > > ,
437
494
}
438
495
439
496
impl < T : ' static > Key < T > {
@@ -444,24 +501,43 @@ pub mod os {
444
501
}
445
502
}
446
503
447
- pub unsafe fn get ( & ' static self ) -> Option < & ' static UnsafeCell < Option < T > > > {
504
+ pub unsafe fn get ( & ' static self , init : fn ( ) -> T ) -> Option < & ' static T > {
448
505
let ptr = self . os . get ( ) as * mut Value < T > ;
449
- if !ptr. is_null ( ) {
450
- if ptr as usize == 1 {
451
- return None
506
+ if ptr as usize > 1 {
507
+ match ( * ptr) . inner . get ( ) {
508
+ Some ( ref value) => return Some ( value) ,
509
+ None => { } ,
452
510
}
453
- return Some ( & ( * ptr) . value ) ;
511
+ }
512
+ self . try_initialize ( init)
513
+ }
514
+
515
+ // `try_initialize` is only called once per os thread local variable,
516
+ // except in corner cases where thread_local dtors reference other
517
+ // thread_local's, or it is being recursively initialized.
518
+ unsafe fn try_initialize ( & ' static self , init : fn ( ) -> T ) -> Option < & ' static T > {
519
+ let ptr = self . os . get ( ) as * mut Value < T > ;
520
+ if ptr as usize == 1 {
521
+ // destructor is running
522
+ return None
454
523
}
455
524
456
- // If the lookup returned null, we haven't initialized our own
457
- // local copy, so do that now.
458
- let ptr: Box < Value < T > > = box Value {
459
- key : self ,
460
- value : UnsafeCell :: new ( None ) ,
525
+ let ptr = if ptr. is_null ( ) {
526
+ // If the lookup returned null, we haven't initialized our own
527
+ // local copy, so do that now.
528
+ let ptr: Box < Value < T > > = box Value {
529
+ inner : LazyKeyInner :: new ( ) ,
530
+ key : self ,
531
+ } ;
532
+ let ptr = Box :: into_raw ( ptr) ;
533
+ self . os . set ( ptr as * mut u8 ) ;
534
+ ptr
535
+ } else {
536
+ // recursive initialization
537
+ ptr
461
538
} ;
462
- let ptr = Box :: into_raw ( ptr) ;
463
- self . os . set ( ptr as * mut u8 ) ;
464
- Some ( & ( * ptr) . value )
539
+
540
+ Some ( ( * ptr) . inner . initialize ( init) )
465
541
}
466
542
}
467
543
0 commit comments