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,42 +27,55 @@ 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
32
34
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 ( ) ;
38
38
VAL . get ( init, __init)
39
39
} )
40
40
}
41
41
} } ,
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) * ) => {
43
44
$( #[ $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) * ) ;
45
46
} ,
46
47
}
47
48
48
49
#[ allow( missing_debug_implementations) ]
50
+ #[ repr( transparent) ] // Required for correctness of `#[rustc_align_static]`
49
51
pub struct EagerStorage <T > {
50
52
pub value: T ,
51
53
}
52
54
53
55
// SAFETY: the target doesn't have threads.
54
56
unsafe impl < T > Sync for EagerStorage < T > { }
55
57
58
+ #[ derive( Clone , Copy , PartialEq , Eq ) ]
59
+ enum State {
60
+ Initial ,
61
+ Alive ,
62
+ Destroying ,
63
+ }
64
+
56
65
#[ allow( missing_debug_implementations) ]
66
+ #[ repr( C ) ]
57
67
pub 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 > ,
59
71
}
60
72
61
73
impl < T > LazyStorage < T > {
62
74
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
+ }
64
79
}
65
80
66
81
/// Gets a pointer to the TLS value, potentially initializing it with the
@@ -70,24 +85,39 @@ impl<T> LazyStorage<T> {
70
85
/// has occurred.
71
86
#[ inline]
72
87
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)
77
92
}
78
93
}
79
94
80
95
#[ cold]
81
96
fn initialize ( & ' static self , i : Option < & mut Option < T > > , f : impl FnOnce ( ) -> T ) -> * const T {
82
97
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
85
100
// 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
+
86
115
unsafe {
87
- self . value . get ( ) . replace ( Some ( value) ) ;
116
+ self . value . get ( ) . write ( MaybeUninit :: new ( value) ) ;
88
117
}
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
91
121
}
92
122
}
93
123
0 commit comments