Skip to content

Commit 78dd504

Browse files
committed
Auto merge of rust-lang#123724 - joboet:static_tls, r=m-ou-se
Rewrite TLS on platforms without threads The saga of rust-lang#110897 continues! r? `@m-ou-se` if you have time
2 parents 8679004 + 8e4a6af commit 78dd504

File tree

2 files changed

+76
-70
lines changed

2 files changed

+76
-70
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,22 +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)]
1418
unsafe fn __getit(
1519
_init: $crate::option::Option<&mut $crate::option::Option<$t>>,
1620
) -> $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)
2825
}
2926

3027
unsafe {
@@ -33,74 +30,83 @@ pub macro thread_local_inner {
3330
}},
3431

3532
// 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)) }
6446
}
65-
},
47+
48+
unsafe {
49+
$crate::thread::LocalKey::new(__getit)
50+
}
51+
}},
6652
($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => {
6753
$(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> =
6854
$crate::thread::local_impl::thread_local_inner!(@key $t, $($init)*);
6955
},
7056
}
7157

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,
7761
}
7862

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

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>>,
8569
}
8670

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) }
9074
}
9175

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

0 commit comments

Comments
 (0)