Skip to content

Commit b8c6be6

Browse files
committed
std: rewrite TLS on platforms without threads
1 parent 7a495cc commit b8c6be6

File tree

3 files changed

+77
-72
lines changed

3 files changed

+77
-72
lines changed

library/std/src/sys/thread_local/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ cfg_if::cfg_if! {
1010
#[doc(hidden)]
1111
mod static_local;
1212
#[doc(hidden)]
13-
pub use static_local::{Key, thread_local_inner};
13+
pub use static_local::{EagerStorage, LazyStorage, thread_local_inner};
1414
} else if #[cfg(target_thread_local)] {
1515
#[doc(hidden)]
1616
mod fast_local;
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
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;
35

46
#[doc(hidden)]
57
#[allow_internal_unstable(thread_local_internals)]
@@ -9,23 +11,17 @@ use crate::fmt;
911
pub macro thread_local_inner {
1012
// used to generate the `LocalKey` value for const-initialized thread locals
1113
(@key $t:ty, const $init:expr) => {{
12-
#[inline] // see comments below
14+
const __INIT: $t = $init;
15+
16+
#[inline]
1317
#[deny(unsafe_op_in_unsafe_fn)]
14-
// FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_refs` lint
15-
#[allow(static_mut_refs)]
1618
unsafe fn __getit(
1719
_init: $crate::option::Option<&mut $crate::option::Option<$t>>,
1820
) -> $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)
2925
}
3026

3127
unsafe {
@@ -34,74 +30,83 @@ pub macro thread_local_inner {
3430
}},
3531

3632
// 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)) }
6546
}
66-
},
47+
48+
unsafe {
49+
$crate::thread::LocalKey::new(__getit)
50+
}
51+
}},
6752
($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => {
6853
$(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> =
6954
$crate::thread::local_impl::thread_local_inner!(@key $t, $($init)*);
7055
},
7156
}
7257

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,
7861
}
7962

80-
unsafe impl<T> Sync for Key<T> {}
63+
// SAFETY: the target doesn't have threads.
64+
unsafe impl<T> Sync for EagerStorage<T> {}
8165

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>>,
8669
}
8770

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) }
9174
}
9275

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() }
106108
}
107109
}
110+
111+
// SAFETY: the target doesn't have threads.
112+
unsafe impl<T> Sync for LazyStorage<T> {}

library/std/src/thread/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ cfg_if::cfg_if! {
204204
#[doc(hidden)]
205205
#[unstable(feature = "thread_local_internals", issue = "none")]
206206
pub mod local_impl {
207-
pub use crate::sys::thread_local::{thread_local_inner, Key, abort_on_dtor_unwind};
207+
pub use crate::sys::thread_local::*;
208208
}
209209
}
210210
}

0 commit comments

Comments
 (0)