2
2
//! thread locals and we can instead just use plain statics!
3
3
4
4
use crate :: cell:: { Cell , UnsafeCell } ;
5
+ use crate :: mem:: MaybeUninit ;
5
6
use crate :: ptr;
6
7
7
8
#[ doc( hidden) ]
@@ -11,12 +12,13 @@ use crate::ptr;
11
12
#[ rustc_macro_transparency = "semitransparent" ]
12
13
pub macro thread_local_inner {
13
14
// 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) => { {
15
16
const __INIT: $t = $init;
16
17
17
18
// NOTE: Please update the shadowing test in `tests/thread.rs` if these types are renamed.
18
19
unsafe {
19
20
$crate:: thread:: LocalKey :: new ( |_| {
21
+ $( #[ $align_attr] ) *
20
22
static VAL : $crate:: thread:: local_impl:: EagerStorage <$t> =
21
23
$crate:: thread:: local_impl:: EagerStorage { value : __INIT } ;
22
24
& VAL . value
@@ -25,7 +27,7 @@ pub macro thread_local_inner {
25
27
} } ,
26
28
27
29
// used to generate the `LocalKey` value for `thread_local!`
28
- ( @key $t: ty, $init: expr) => { {
30
+ ( @key $t: ty, $( # [ $align_attr : meta ] ) * , $ init: expr) => { {
29
31
#[ inline]
30
32
fn __init( ) -> $t { $init }
31
33
@@ -34,33 +36,48 @@ pub macro thread_local_inner {
34
36
use $crate:: thread:: local_impl:: LazyStorage ;
35
37
36
38
LocalKey :: new ( |init| {
39
+ $( #[ $align_attr] ) *
37
40
static VAL : LazyStorage <$t> = LazyStorage :: new ( ) ;
38
41
VAL . get ( init, __init)
39
42
} )
40
43
}
41
44
} } ,
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) * ) => {
43
46
$( #[ $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) * ) ;
45
48
} ,
46
49
}
47
50
48
51
#[ allow( missing_debug_implementations) ]
52
+ #[ repr( transparent) ] // Required for correctness of `#[rustc_align_static]`
49
53
pub struct EagerStorage <T > {
50
54
pub value: T ,
51
55
}
52
56
53
57
// SAFETY: the target doesn't have threads.
54
58
unsafe impl < T > Sync for EagerStorage < T > { }
55
59
60
+ #[ derive( Clone , Copy , PartialEq , Eq ) ]
61
+ enum State {
62
+ Initial ,
63
+ Alive ,
64
+ Destroying ,
65
+ }
66
+
56
67
#[ allow( missing_debug_implementations) ]
68
+ #[ repr( C ) ]
57
69
pub 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 > ,
59
73
}
60
74
61
75
impl < T > LazyStorage < T > {
62
76
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
+ }
64
81
}
65
82
66
83
/// Gets a pointer to the TLS value, potentially initializing it with the
@@ -70,24 +87,39 @@ impl<T> LazyStorage<T> {
70
87
/// has occurred.
71
88
#[ inline]
72
89
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)
77
94
}
78
95
}
79
96
80
97
#[ cold]
81
98
fn initialize ( & ' static self , i : Option < & mut Option < T > > , f : impl FnOnce ( ) -> T ) -> * const T {
82
99
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
85
102
// 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
+
86
117
unsafe {
87
- self . value . get ( ) . replace ( Some ( value) ) ;
118
+ self . value . get ( ) . write ( MaybeUninit :: new ( value) ) ;
88
119
}
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
91
123
}
92
124
}
93
125
0 commit comments