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,55 @@ 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) * ) => {
42+
43+ ( $( #[ $attr: meta] ) * $vis: vis $name: ident, $t: ty, $( #[ $align_attr: meta] ) * , $( $init: tt) * ) => {
4344 $( #[ $attr] ) * $vis const $name: $crate:: thread:: LocalKey <$t> =
44- $crate:: thread:: local_impl:: thread_local_inner!( @key $t, $( $init) * ) ;
45+ $crate:: thread:: local_impl:: thread_local_inner!( @key $t, $( # [ $align_attr ] ) * , $ ( $init) * ) ;
4546 } ,
4647}
4748
4849#[ allow( missing_debug_implementations) ]
50+ #[ repr( transparent) ] // Required for correctness of `#[rustc_align_static]`
4951pub struct EagerStorage <T > {
5052 pub value: T ,
5153}
5254
5355// SAFETY: the target doesn't have threads.
5456unsafe impl < T > Sync for EagerStorage < T > { }
5557
58+ #[ derive( Clone , Copy , PartialEq , Eq ) ]
59+ enum State {
60+ Initial ,
61+ Alive ,
62+ Destroying ,
63+ }
64+
5665#[ allow( missing_debug_implementations) ]
66+ #[ repr( C ) ]
5767pub struct LazyStorage < T > {
58- value : UnsafeCell < Option < T > > ,
68+ // This field must be first, for correctness of `#[rustc_align_static]`
69+ value : UnsafeCell < MaybeUninit < T > > ,
70+ state : Cell < State > ,
5971}
6072
6173impl < T > LazyStorage < T > {
6274 pub const fn new ( ) -> LazyStorage < T > {
63- LazyStorage { value : UnsafeCell :: new ( None ) }
75+ LazyStorage {
76+ value : UnsafeCell :: new ( MaybeUninit :: uninit ( ) ) ,
77+ state : Cell :: new ( State :: Initial ) ,
78+ }
6479 }
6580
6681 /// Gets a pointer to the TLS value, potentially initializing it with the
@@ -70,24 +85,39 @@ impl<T> LazyStorage<T> {
7085 /// has occurred.
7186 #[ inline]
7287 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) ,
88+ if self . state . get ( ) == State :: Alive {
89+ self . value . get ( ) as * const T
90+ } else {
91+ self . initialize ( i, f)
7792 }
7893 }
7994
8095 #[ cold]
8196 fn initialize ( & ' static self , i : Option < & mut Option < T > > , f : impl FnOnce ( ) -> T ) -> * const T {
8297 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.
98+
99+ // Destroy the old value if it is initialized
85100 // FIXME(#110897): maybe panic on recursive initialization.
101+ if self . state . get ( ) == State :: Alive {
102+ self . state . set ( State :: Destroying ) ;
103+ // Safety: we check for no initialization during drop below
104+ unsafe {
105+ ptr:: drop_in_place ( self . value . get ( ) as * mut T ) ;
106+ }
107+ self . state . set ( State :: Initial ) ;
108+ }
109+
110+ // Guard against initialization during drop
111+ if self . state . get ( ) == State :: Destroying {
112+ panic ! ( "Attempted to initialize thread-local while it is being dropped" ) ;
113+ }
114+
86115 unsafe {
87- self . value . get ( ) . replace ( Some ( value) ) ;
116+ self . value . get ( ) . write ( MaybeUninit :: new ( value) ) ;
88117 }
89- // SAFETY: we just set this to `Some`.
90- unsafe { ( * self . value . get ( ) ) . as_ref ( ) . unwrap_unchecked ( ) }
118+ self . state . set ( State :: Alive ) ;
119+
120+ self . value . get ( ) as * const T
91121 }
92122}
93123
0 commit comments