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,50 @@ 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) * ) => {
43
- $( #[ $attr] ) * $vis const $name: $crate:: thread:: LocalKey <$t> =
44
- $crate:: thread:: local_impl:: thread_local_inner!( @key $t, $( $init) * ) ;
45
- } ,
46
42
}
47
43
48
44
#[ allow( missing_debug_implementations) ]
45
+ #[ repr( transparent) ] // Required for correctness of `#[rustc_align_static]`
49
46
pub struct EagerStorage < T > {
50
47
pub value : T ,
51
48
}
52
49
53
50
// SAFETY: the target doesn't have threads.
54
51
unsafe impl < T > Sync for EagerStorage < T > { }
55
52
53
+ #[ derive( Clone , Copy , PartialEq , Eq ) ]
54
+ enum State {
55
+ Initial ,
56
+ Alive ,
57
+ Destroying ,
58
+ }
59
+
56
60
#[ allow( missing_debug_implementations) ]
61
+ #[ repr( C ) ]
57
62
pub struct LazyStorage < T > {
58
- value : UnsafeCell < Option < T > > ,
63
+ // This field must be first, for correctness of `#[rustc_align_static]`
64
+ value : UnsafeCell < MaybeUninit < T > > ,
65
+ state : Cell < State > ,
59
66
}
60
67
61
68
impl < T > LazyStorage < T > {
62
69
pub const fn new ( ) -> LazyStorage < T > {
63
- LazyStorage { value : UnsafeCell :: new ( None ) }
70
+ LazyStorage {
71
+ value : UnsafeCell :: new ( MaybeUninit :: uninit ( ) ) ,
72
+ state : Cell :: new ( State :: Initial ) ,
73
+ }
64
74
}
65
75
66
76
/// Gets a pointer to the TLS value, potentially initializing it with the
@@ -70,24 +80,39 @@ impl<T> LazyStorage<T> {
70
80
/// has occurred.
71
81
#[ inline]
72
82
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) ,
83
+ if self . state . get ( ) == State :: Alive {
84
+ self . value . get ( ) as * const T
85
+ } else {
86
+ self . initialize ( i, f)
77
87
}
78
88
}
79
89
80
90
#[ cold]
81
91
fn initialize ( & ' static self , i : Option < & mut Option < T > > , f : impl FnOnce ( ) -> T ) -> * const T {
82
92
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.
93
+
94
+ // Destroy the old value if it is initialized
85
95
// FIXME(#110897): maybe panic on recursive initialization.
96
+ if self . state . get ( ) == State :: Alive {
97
+ self . state . set ( State :: Destroying ) ;
98
+ // Safety: we check for no initialization during drop below
99
+ unsafe {
100
+ ptr:: drop_in_place ( self . value . get ( ) as * mut T ) ;
101
+ }
102
+ self . state . set ( State :: Initial ) ;
103
+ }
104
+
105
+ // Guard against initialization during drop
106
+ if self . state . get ( ) == State :: Destroying {
107
+ panic ! ( "Attempted to initialize thread-local while it is being dropped" ) ;
108
+ }
109
+
86
110
unsafe {
87
- self . value . get ( ) . replace ( Some ( value) ) ;
111
+ self . value . get ( ) . write ( MaybeUninit :: new ( value) ) ;
88
112
}
89
- // SAFETY: we just set this to `Some`.
90
- unsafe { ( * self . value . get ( ) ) . as_ref ( ) . unwrap_unchecked ( ) }
113
+ self . state . set ( State :: Alive ) ;
114
+
115
+ self . value . get ( ) as * const T
91
116
}
92
117
}
93
118
0 commit comments