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,23 +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
- // FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_refs` lint
15
- #[ allow( static_mut_refs) ]
16
18
unsafe fn __getit (
17
19
_init : $crate:: option:: Option < & mut $crate:: option:: Option < $t> > ,
18
20
) -> $crate:: option:: Option < & ' static $t> {
19
- const INIT_EXPR : $t = $init;
20
-
21
- // wasm without atomics maps directly to `static mut`, and dtors
22
- // aren't implemented because thread dtors aren't really a thing
23
- // on wasm right now
24
- //
25
- // FIXME(#84224) this should come after the `target_thread_local`
26
- // block.
27
- static mut VAL : $t = INIT_EXPR ;
28
- unsafe { $crate:: option:: Option :: Some ( & VAL ) }
21
+ use $crate:: thread:: local_impl:: EagerStorage ;
22
+
23
+ static VAL : EagerStorage < $t> = EagerStorage { value : __INIT } ;
24
+ $crate:: option:: Option :: Some ( & VAL . value )
29
25
}
30
26
31
27
unsafe {
@@ -34,74 +30,83 @@ pub macro thread_local_inner {
34
30
} } ,
35
31
36
32
// used to generate the `LocalKey` value for `thread_local!`
37
- ( @key $t: ty, $init: expr) => {
38
- {
39
- #[ inline]
40
- fn __init( ) -> $t { $init }
41
- #[ inline]
42
- unsafe fn __getit (
43
- init : $crate:: option:: Option < & mut $crate:: option:: Option < $t> > ,
44
- ) -> $crate:: option:: Option < & ' static $t> {
45
- static __KEY: $crate:: thread:: local_impl:: Key < $t> =
46
- $crate:: thread:: local_impl:: Key :: new ( ) ;
47
-
48
- unsafe {
49
- __KEY. get ( move || {
50
- if let $crate:: option:: Option :: Some ( init) = init {
51
- if let $crate:: option:: Option :: Some ( value) = init. take ( ) {
52
- return value;
53
- } else if $crate:: cfg!( debug_assertions) {
54
- $crate:: unreachable!( "missing default value" ) ;
55
- }
56
- }
57
- __init ( )
58
- } )
59
- }
60
- }
61
-
62
- unsafe {
63
- $crate:: thread:: LocalKey :: new ( __getit)
64
- }
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) ) }
65
46
}
66
- } ,
47
+
48
+ unsafe {
49
+ $crate:: thread:: LocalKey :: new ( __getit)
50
+ }
51
+ } } ,
67
52
( $( #[ $attr: meta] ) * $vis: vis $name: ident, $t: ty, $( $init: tt) * ) => {
68
53
$( #[ $attr] ) * $vis const $name: $crate:: thread:: LocalKey <$t> =
69
54
$crate:: thread:: local_impl:: thread_local_inner!( @key $t, $( $init) * ) ;
70
55
} ,
71
56
}
72
57
73
- /// On some targets like wasm there's no threads, so no need to generate
74
- /// thread locals and we can instead just use plain statics!
75
-
76
- pub struct Key <T > {
77
- inner: LazyKeyInner <T >,
58
+ #[ allow ( missing_debug_implementations) ]
59
+ pub struct EagerStorage <T > {
60
+ pub value: T ,
78
61
}
79
62
80
- unsafe impl < T > Sync for Key < T > { }
63
+ // SAFETY: the target doesn't have threads.
64
+ unsafe impl < T > Sync for EagerStorage < T > { }
81
65
82
- impl < T > fmt:: Debug for Key < T > {
83
- fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
84
- f. debug_struct ( "Key" ) . finish_non_exhaustive ( )
85
- }
66
+ #[ allow( missing_debug_implementations) ]
67
+ pub struct LazyStorage < T > {
68
+ value : UnsafeCell < Option < T > > ,
86
69
}
87
70
88
- impl < T > Key < T > {
89
- pub const fn new ( ) -> Key < T > {
90
- Key { inner : LazyKeyInner :: new ( ) }
71
+ impl < T > LazyStorage < T > {
72
+ pub const fn new ( ) -> LazyStorage < T > {
73
+ LazyStorage { value : UnsafeCell :: new ( None ) }
91
74
}
92
75
93
- pub unsafe fn get ( & self , init : impl FnOnce ( ) -> T ) -> Option < & ' static T > {
94
- // SAFETY: The caller must ensure no reference is ever handed out to
95
- // the inner cell nor mutable reference to the Option<T> inside said
96
- // cell. This make it safe to hand a reference, though the lifetime
97
- // of 'static is itself unsafe, making the get method unsafe.
98
- let value = unsafe {
99
- match self . inner . get ( ) {
100
- Some ( ref value) => value,
101
- None => self . inner . initialize ( init) ,
102
- }
103
- } ;
104
-
105
- 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 ( ) }
106
108
}
107
109
}
110
+
111
+ // SAFETY: the target doesn't have threads.
112
+ unsafe impl < T > Sync for LazyStorage < T > { }
0 commit comments