22//! thread locals and we can instead just use plain statics!
33
44use crate :: cell:: { Cell , UnsafeCell } ;
5+ use crate :: mem:: MaybeUninit ;
56use crate :: ptr;
67
78#[ doc( hidden) ]
@@ -11,12 +12,13 @@ use crate::ptr;
1112#[ rustc_macro_transparency = "semitransparent" ]
1213pub macro thread_local_inner {
1314 // used to generate the `LocalKey` value for const-initialized thread locals
14- ( @key $t: ty, const $init: expr) => { {
15+ ( @key $t: ty, $ ( # [ $align_attr : meta ] ) * , const $init: expr) => { {
1516 const __INIT: $t = $init;
1617
1718 // NOTE: Please update the shadowing test in `tests/thread.rs` if these types are renamed.
1819 unsafe {
1920 $crate:: thread:: LocalKey :: new ( |_| {
21+ $( #[ $align_attr] ) *
2022 static VAL : $crate:: thread:: local_impl:: EagerStorage <$t> =
2123 $crate:: thread:: local_impl:: EagerStorage { value : __INIT } ;
2224 & VAL . value
@@ -25,42 +27,50 @@ pub macro thread_local_inner {
2527 } } ,
2628
2729 // used to generate the `LocalKey` value for `thread_local!`
28- ( @key $t: ty, $init: expr) => { {
30+ ( @key $t: ty, $( # [ $align_attr : meta ] ) * , $ init: expr) => { {
2931 #[ inline]
3032 fn __init( ) -> $t { $init }
3133
3234 unsafe {
33- use $crate:: thread:: LocalKey ;
34- use $crate:: thread:: local_impl:: LazyStorage ;
35-
36- LocalKey :: new ( |init| {
37- static VAL : LazyStorage < $t> = LazyStorage :: new ( ) ;
35+ $crate:: thread:: LocalKey :: new ( |init| {
36+ $( #[ $align_attr] ) *
37+ static VAL : $crate:: thread:: local_impl:: LazyStorage <$t> = $crate:: thread:: local_impl:: LazyStorage :: new ( ) ;
3838 VAL . get ( init, __init)
3939 } )
4040 }
4141 } } ,
42- ( $( #[ $attr: meta] ) * $vis: vis $name: ident, $t: ty, $( $init: tt) * ) => {
43- $( #[ $attr] ) * $vis const $name: $crate:: thread:: LocalKey <$t> =
44- $crate:: thread:: local_impl:: thread_local_inner!( @key $t, $( $init) * ) ;
45- } ,
4642}
4743
4844#[ allow( missing_debug_implementations) ]
45+ #[ repr( transparent) ] // Required for correctness of `#[rustc_align_static]`
4946pub struct EagerStorage < T > {
5047 pub value : T ,
5148}
5249
5350// SAFETY: the target doesn't have threads.
5451unsafe impl < T > Sync for EagerStorage < T > { }
5552
53+ #[ derive( Clone , Copy , PartialEq , Eq ) ]
54+ enum State {
55+ Initial ,
56+ Alive ,
57+ Destroying ,
58+ }
59+
5660#[ allow( missing_debug_implementations) ]
61+ #[ repr( C ) ]
5762pub struct LazyStorage < T > {
58- value : UnsafeCell < Option < T > > ,
63+ // This field must be first, for correctness of `#[rustc_align_static]`
64+ value : UnsafeCell < MaybeUninit < T > > ,
65+ state : Cell < State > ,
5966}
6067
6168impl < T > LazyStorage < T > {
6269 pub const fn new ( ) -> LazyStorage < T > {
63- LazyStorage { value : UnsafeCell :: new ( None ) }
70+ LazyStorage {
71+ value : UnsafeCell :: new ( MaybeUninit :: uninit ( ) ) ,
72+ state : Cell :: new ( State :: Initial ) ,
73+ }
6474 }
6575
6676 /// Gets a pointer to the TLS value, potentially initializing it with the
@@ -70,24 +80,39 @@ impl<T> LazyStorage<T> {
7080 /// has occurred.
7181 #[ inline]
7282 pub fn get ( & ' static self , i : Option < & mut Option < T > > , f : impl FnOnce ( ) -> T ) -> * const T {
73- let value = unsafe { & * self . value . get ( ) } ;
74- match value {
75- Some ( v ) => v ,
76- None => self . initialize ( i, f) ,
83+ if self . state . get ( ) == State :: Alive {
84+ self . value . get ( ) as * const T
85+ } else {
86+ self . initialize ( i, f)
7787 }
7888 }
7989
8090 #[ cold]
8191 fn initialize ( & ' static self , i : Option < & mut Option < T > > , f : impl FnOnce ( ) -> T ) -> * const T {
8292 let value = i. and_then ( Option :: take) . unwrap_or_else ( f) ;
83- // Destroy the old value, after updating the TLS variable as the
84- // destructor might reference it.
93+
94+ // Destroy the old value if it is initialized
8595 // FIXME(#110897): maybe panic on recursive initialization.
96+ if self . state . get ( ) == State :: Alive {
97+ self . state . set ( State :: Destroying ) ;
98+ // Safety: we check for no initialization during drop below
99+ unsafe {
100+ ptr:: drop_in_place ( self . value . get ( ) as * mut T ) ;
101+ }
102+ self . state . set ( State :: Initial ) ;
103+ }
104+
105+ // Guard against initialization during drop
106+ if self . state . get ( ) == State :: Destroying {
107+ panic ! ( "Attempted to initialize thread-local while it is being dropped" ) ;
108+ }
109+
86110 unsafe {
87- self . value . get ( ) . replace ( Some ( value) ) ;
111+ self . value . get ( ) . write ( MaybeUninit :: new ( value) ) ;
88112 }
89- // SAFETY: we just set this to `Some`.
90- unsafe { ( * self . value . get ( ) ) . as_ref ( ) . unwrap_unchecked ( ) }
113+ self . state . set ( State :: Alive ) ;
114+
115+ self . value . get ( ) as * const T
91116 }
92117}
93118
0 commit comments