1
- use super :: lazy:: LazyKeyInner ;
2
- use crate :: fmt;
1
+ //! On some targets like wasm there's no threads, so no need to generate
2
+ //! thread locals and we can instead just use plain statics!
3
+
4
+ use crate :: cell:: UnsafeCell ;
3
5
4
6
#[ doc( hidden) ]
5
7
#[ allow_internal_unstable( thread_local_internals) ]
@@ -9,22 +11,17 @@ use crate::fmt;
9
11
pub macro thread_local_inner {
10
12
// used to generate the `LocalKey` value for const-initialized thread locals
11
13
( @key $t: ty, const $init: expr) => { {
12
- #[ inline] // see comments below
14
+ const __INIT: $t = $init;
15
+
16
+ #[ inline]
13
17
#[ deny( unsafe_op_in_unsafe_fn) ]
14
18
unsafe fn __getit (
15
19
_init : $crate:: option:: Option < & mut $crate:: option:: Option < $t> > ,
16
20
) -> $crate:: option:: Option < & ' static $t> {
17
- const INIT_EXPR : $t = $init;
18
-
19
- // wasm without atomics maps directly to `static mut`, and dtors
20
- // aren't implemented because thread dtors aren't really a thing
21
- // on wasm right now
22
- //
23
- // FIXME(#84224) this should come after the `target_thread_local`
24
- // block.
25
- static mut VAL : $t = INIT_EXPR ;
26
- // SAFETY: we only ever create shared references, so there's no mutable aliasing.
27
- unsafe { $crate:: option:: Option :: Some ( & * $crate:: ptr:: addr_of!( VAL ) ) }
21
+ use $crate:: thread:: local_impl:: EagerStorage ;
22
+
23
+ static VAL : EagerStorage < $t> = EagerStorage { value : __INIT } ;
24
+ $crate:: option:: Option :: Some ( & VAL . value )
28
25
}
29
26
30
27
unsafe {
@@ -33,74 +30,83 @@ pub macro thread_local_inner {
33
30
} } ,
34
31
35
32
// used to generate the `LocalKey` value for `thread_local!`
36
- ( @key $t: ty, $init: expr) => {
37
- {
38
- #[ inline]
39
- fn __init( ) -> $t { $init }
40
- #[ inline]
41
- unsafe fn __getit (
42
- init : $crate:: option:: Option < & mut $crate:: option:: Option < $t> > ,
43
- ) -> $crate:: option:: Option < & ' static $t> {
44
- static __KEY: $crate:: thread:: local_impl:: Key < $t> =
45
- $crate:: thread:: local_impl:: Key :: new ( ) ;
46
-
47
- unsafe {
48
- __KEY. get ( move || {
49
- if let $crate:: option:: Option :: Some ( init) = init {
50
- if let $crate:: option:: Option :: Some ( value) = init. take ( ) {
51
- return value;
52
- } else if $crate:: cfg!( debug_assertions) {
53
- $crate:: unreachable!( "missing default value" ) ;
54
- }
55
- }
56
- __init ( )
57
- } )
58
- }
59
- }
60
-
61
- unsafe {
62
- $crate:: thread:: LocalKey :: new ( __getit)
63
- }
33
+ ( @key $t: ty, $init: expr) => { {
34
+ #[ inline]
35
+ fn __init( ) -> $t { $init }
36
+
37
+ #[ inline]
38
+ #[ deny( unsafe_op_in_unsafe_fn) ]
39
+ unsafe fn __getit (
40
+ init : $crate:: option:: Option < & mut $crate:: option:: Option < $t> > ,
41
+ ) -> $crate:: option:: Option < & ' static $t> {
42
+ use $crate:: thread:: local_impl:: LazyStorage ;
43
+
44
+ static VAL : LazyStorage < $t> = LazyStorage :: new ( ) ;
45
+ unsafe { $crate:: option:: Option :: Some ( VAL . get ( init, __init) ) }
64
46
}
65
- } ,
47
+
48
+ unsafe {
49
+ $crate:: thread:: LocalKey :: new ( __getit)
50
+ }
51
+ } } ,
66
52
( $( #[ $attr: meta] ) * $vis: vis $name: ident, $t: ty, $( $init: tt) * ) => {
67
53
$( #[ $attr] ) * $vis const $name: $crate:: thread:: LocalKey <$t> =
68
54
$crate:: thread:: local_impl:: thread_local_inner!( @key $t, $( $init) * ) ;
69
55
} ,
70
56
}
71
57
72
- /// On some targets like wasm there's no threads, so no need to generate
73
- /// thread locals and we can instead just use plain statics!
74
-
75
- pub struct Key <T > {
76
- inner: LazyKeyInner <T >,
58
+ #[ allow ( missing_debug_implementations) ]
59
+ pub struct EagerStorage <T > {
60
+ pub value: T ,
77
61
}
78
62
79
- unsafe impl < T > Sync for Key < T > { }
63
+ // SAFETY: the target doesn't have threads.
64
+ unsafe impl < T > Sync for EagerStorage < T > { }
80
65
81
- impl < T > fmt:: Debug for Key < T > {
82
- fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
83
- f. debug_struct ( "Key" ) . finish_non_exhaustive ( )
84
- }
66
+ #[ allow( missing_debug_implementations) ]
67
+ pub struct LazyStorage < T > {
68
+ value : UnsafeCell < Option < T > > ,
85
69
}
86
70
87
- impl < T > Key < T > {
88
- pub const fn new ( ) -> Key < T > {
89
- Key { inner : LazyKeyInner :: new ( ) }
71
+ impl < T > LazyStorage < T > {
72
+ pub const fn new ( ) -> LazyStorage < T > {
73
+ LazyStorage { value : UnsafeCell :: new ( None ) }
90
74
}
91
75
92
- pub unsafe fn get ( & self , init : impl FnOnce ( ) -> T ) -> Option < & ' static T > {
93
- // SAFETY: The caller must ensure no reference is ever handed out to
94
- // the inner cell nor mutable reference to the Option<T> inside said
95
- // cell. This make it safe to hand a reference, though the lifetime
96
- // of 'static is itself unsafe, making the get method unsafe.
97
- let value = unsafe {
98
- match self . inner . get ( ) {
99
- Some ( ref value) => value,
100
- None => self . inner . initialize ( init) ,
101
- }
102
- } ;
103
-
104
- Some ( value)
76
+ /// Gets a reference to the contained value, initializing it if necessary.
77
+ ///
78
+ /// # Safety
79
+ /// The returned reference may not be used after reentrant initialization has occurred.
80
+ #[ inline]
81
+ pub unsafe fn get (
82
+ & ' static self ,
83
+ i : Option < & mut Option < T > > ,
84
+ f : impl FnOnce ( ) -> T ,
85
+ ) -> & ' static T {
86
+ let value = unsafe { & * self . value . get ( ) } ;
87
+ match value {
88
+ Some ( v) => v,
89
+ None => self . initialize ( i, f) ,
90
+ }
91
+ }
92
+
93
+ #[ cold]
94
+ unsafe fn initialize (
95
+ & ' static self ,
96
+ i : Option < & mut Option < T > > ,
97
+ f : impl FnOnce ( ) -> T ,
98
+ ) -> & ' static T {
99
+ let value = i. and_then ( Option :: take) . unwrap_or_else ( f) ;
100
+ // Destroy the old value, after updating the TLS variable as the
101
+ // destructor might reference it.
102
+ // FIXME(#110897): maybe panic on recursive initialization.
103
+ unsafe {
104
+ self . value . get ( ) . replace ( Some ( value) ) ;
105
+ }
106
+ // SAFETY: we just set this to `Some`.
107
+ unsafe { ( * self . value . get ( ) ) . as_ref ( ) . unwrap_unchecked ( ) }
105
108
}
106
109
}
110
+
111
+ // SAFETY: the target doesn't have threads.
112
+ unsafe impl < T > Sync for LazyStorage < T > { }
0 commit comments