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,49 @@ 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) ]
4953pub struct EagerStorage <T > {
54+ // This field must be first, for correctness of `#[rustc_align_static]`
5055 pub value: T ,
5156}
5257
5358// SAFETY: the target doesn't have threads.
5459unsafe impl < T > Sync for EagerStorage < T > { }
5560
61+ #[ derive( Clone , Copy , PartialEq , Eq ) ]
62+ enum State {
63+ Initial ,
64+ Alive ,
65+ Destroying ,
66+ }
67+
5668#[ allow( missing_debug_implementations) ]
69+ #[ repr( C ) ]
5770pub struct LazyStorage < T > {
58- value : UnsafeCell < Option < T > > ,
71+ // This field must be first, for correctness of `#[rustc_align_static]`
72+ value : UnsafeCell < MaybeUninit < T > > ,
73+ state : Cell < State > ,
5974}
6075
6176impl < T > LazyStorage < T > {
6277 pub const fn new ( ) -> LazyStorage < T > {
63- LazyStorage { value : UnsafeCell :: new ( None ) }
78+ LazyStorage {
79+ value : UnsafeCell :: new ( MaybeUninit :: uninit ( ) ) ,
80+ state : Cell :: new ( State :: Initial ) ,
81+ }
6482 }
6583
6684 /// Gets a pointer to the TLS value, potentially initializing it with the
@@ -70,24 +88,39 @@ impl<T> LazyStorage<T> {
7088 /// has occurred.
7189 #[ inline]
7290 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) ,
91+ if self . state . get ( ) == State :: Alive {
92+ self . value . get ( ) as * const T
93+ } else {
94+ self . initialize ( i, f)
7795 }
7896 }
7997
8098 #[ cold]
8199 fn initialize ( & ' static self , i : Option < & mut Option < T > > , f : impl FnOnce ( ) -> T ) -> * const T {
82100 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.
101+
102+ // Destroy the old value if it is initialized
85103 // FIXME(#110897): maybe panic on recursive initialization.
104+ if self . state . get ( ) == State :: Alive {
105+ self . state . set ( State :: Destroying ) ;
106+ // Safety: we check for no initialization during drop below
107+ unsafe {
108+ ptr:: drop_in_place ( self . value . get ( ) as * mut T ) ;
109+ }
110+ self . state . set ( State :: Initial ) ;
111+ }
112+
113+ // Guard against initialization during drop
114+ if self . state . get ( ) == State :: Destroying {
115+ panic ! ( "Attempted to initialize thread-local while it is being dropped" ) ;
116+ }
117+
86118 unsafe {
87- self . value . get ( ) . replace ( Some ( value) ) ;
119+ self . value . get ( ) . write ( MaybeUninit :: new ( value) ) ;
88120 }
89- // SAFETY: we just set this to `Some`.
90- unsafe { ( * self . value . get ( ) ) . as_ref ( ) . unwrap_unchecked ( ) }
121+ self . state . set ( State :: Alive ) ;
122+
123+ self . value . get ( ) as * const T
91124 }
92125}
93126
0 commit comments