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,7 +27,7 @@ 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
@@ -34,33 +36,48 @@ pub macro thread_local_inner {
3436 use $crate:: thread:: local_impl:: LazyStorage ;
3537
3638 LocalKey :: new ( |init| {
39+ $( #[ $align_attr] ) *
3740 static VAL : LazyStorage <$t> = LazyStorage :: new ( ) ;
3841 VAL . get ( init, __init)
3942 } )
4043 }
4144 } } ,
42- ( $( #[ $attr: meta] ) * $vis: vis $name: ident, $t: ty, $( $init: tt) * ) => {
45+ ( $( #[ $attr: meta] ) * $vis: vis $name: ident, $t: ty, $( # [ $align_attr : meta ] ) * , $ ( $init: tt) * ) => {
4346 $( #[ $attr] ) * $vis const $name: $crate:: thread:: LocalKey <$t> =
44- $crate:: thread:: local_impl:: thread_local_inner!( @key $t, $( $init) * ) ;
47+ $crate:: thread:: local_impl:: thread_local_inner!( @key $t, $( # [ $align_attr ] ) * , $ ( $init) * ) ;
4548 } ,
4649}
4750
4851#[ allow( missing_debug_implementations) ]
52+ #[ repr( transparent) ] // Required for correctness of `#[rustc_align_static]`
4953pub struct EagerStorage <T > {
5054 pub value: T ,
5155}
5256
5357// SAFETY: the target doesn't have threads.
5458unsafe impl < T > Sync for EagerStorage < T > { }
5559
60+ #[ derive( Clone , Copy , PartialEq , Eq ) ]
61+ enum State {
62+ Initial ,
63+ Alive ,
64+ Destroying ,
65+ }
66+
5667#[ allow( missing_debug_implementations) ]
68+ #[ repr( C ) ]
5769pub struct LazyStorage < T > {
58- value : UnsafeCell < Option < T > > ,
70+ // This field must be first, for correctness of `#[rustc_align_static]`
71+ value : UnsafeCell < MaybeUninit < T > > ,
72+ state : Cell < State > ,
5973}
6074
6175impl < T > LazyStorage < T > {
6276 pub const fn new ( ) -> LazyStorage < T > {
63- LazyStorage { value : UnsafeCell :: new ( None ) }
77+ LazyStorage {
78+ value : UnsafeCell :: new ( MaybeUninit :: uninit ( ) ) ,
79+ state : Cell :: new ( State :: Initial ) ,
80+ }
6481 }
6582
6683 /// Gets a pointer to the TLS value, potentially initializing it with the
@@ -70,24 +87,39 @@ impl<T> LazyStorage<T> {
7087 /// has occurred.
7188 #[ inline]
7289 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) ,
90+ if self . state . get ( ) == State :: Alive {
91+ self . value . get ( ) as * const T
92+ } else {
93+ self . initialize ( i, f)
7794 }
7895 }
7996
8097 #[ cold]
8198 fn initialize ( & ' static self , i : Option < & mut Option < T > > , f : impl FnOnce ( ) -> T ) -> * const T {
8299 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.
100+
101+ // Destroy the old value if it is initialized
85102 // FIXME(#110897): maybe panic on recursive initialization.
103+ if self . state . get ( ) == State :: Alive {
104+ self . state . set ( State :: Destroying ) ;
105+ // Safety: we check for no initialization during drop below
106+ unsafe {
107+ ptr:: drop_in_place ( self . value . get ( ) as * mut T ) ;
108+ }
109+ self . state . set ( State :: Initial ) ;
110+ }
111+
112+ // Guard against initialization during drop
113+ if self . state . get ( ) == State :: Destroying {
114+ panic ! ( "Attempted to initialize thread-local while it is being dropped" ) ;
115+ }
116+
86117 unsafe {
87- self . value . get ( ) . replace ( Some ( value) ) ;
118+ self . value . get ( ) . write ( MaybeUninit :: new ( value) ) ;
88119 }
89- // SAFETY: we just set this to `Some`.
90- unsafe { ( * self . value . get ( ) ) . as_ref ( ) . unwrap_unchecked ( ) }
120+ self . state . set ( State :: Alive ) ;
121+
122+ self . value . get ( ) as * const T
91123 }
92124}
93125
0 commit comments