Skip to content

Commit 45d5021

Browse files
committed
Split __thread_local_inner macro
Split the __thread_local_inner macro to make it more readable. Also move everything to crate::sys::common::thread_local. Signed-off-by: Ayush Singh <[email protected]>
1 parent ffa9019 commit 45d5021

File tree

3 files changed

+316
-194
lines changed

3 files changed

+316
-194
lines changed

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

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
pub mod alloc;
1414
pub mod small_c_string;
15+
pub mod thread_local;
1516

1617
#[cfg(test)]
1718
mod tests;
+315
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,315 @@
1+
#[doc(hidden)]
2+
#[unstable(feature = "thread_local_internals", reason = "should not be necessary", issue = "none")]
3+
#[macro_export]
4+
#[allow_internal_unstable(thread_local_internals, cfg_target_thread_local, thread_local)]
5+
#[allow_internal_unsafe]
6+
#[cfg(all(
7+
not(target_thread_local),
8+
not(all(target_family = "wasm", not(target_feature = "atomics")))
9+
))]
10+
macro_rules! __thread_local_inner {
11+
// used to generate the `LocalKey` value for const-initialized thread locals
12+
(@key $t:ty, const $init:expr) => {{
13+
#[cfg_attr(not(windows), inline)] // see comments below
14+
#[deny(unsafe_op_in_unsafe_fn)]
15+
unsafe fn __getit(
16+
_init: $crate::option::Option<&mut $crate::option::Option<$t>>,
17+
) -> $crate::option::Option<&'static $t> {
18+
const INIT_EXPR: $t = $init;
19+
20+
// On platforms without `#[thread_local]` we fall back to the
21+
// same implementation as below for os thread locals.
22+
#[inline]
23+
const fn __init() -> $t { INIT_EXPR }
24+
static __KEY: $crate::thread::__OsLocalKeyInner<$t> =
25+
$crate::thread::__OsLocalKeyInner::new();
26+
#[allow(unused_unsafe)]
27+
unsafe {
28+
__KEY.get(move || {
29+
if let $crate::option::Option::Some(init) = _init {
30+
if let $crate::option::Option::Some(value) = init.take() {
31+
return value;
32+
} else if $crate::cfg!(debug_assertions) {
33+
$crate::unreachable!("missing initial value");
34+
}
35+
}
36+
__init()
37+
})
38+
}
39+
}
40+
41+
unsafe {
42+
$crate::thread::LocalKey::new(__getit)
43+
}
44+
}};
45+
46+
// used to generate the `LocalKey` value for `thread_local!`
47+
(@key $t:ty, $init:expr) => {
48+
{
49+
#[inline]
50+
fn __init() -> $t { $init }
51+
52+
// When reading this function you might ask "why is this inlined
53+
// everywhere other than Windows?", and that's a very reasonable
54+
// question to ask. The short story is that it segfaults rustc if
55+
// this function is inlined. The longer story is that Windows looks
56+
// to not support `extern` references to thread locals across DLL
57+
// boundaries. This appears to at least not be supported in the ABI
58+
// that LLVM implements.
59+
//
60+
// Because of this we never inline on Windows, but we do inline on
61+
// other platforms (where external references to thread locals
62+
// across DLLs are supported). A better fix for this would be to
63+
// inline this function on Windows, but only for "statically linked"
64+
// components. For example if two separately compiled rlibs end up
65+
// getting linked into a DLL then it's fine to inline this function
66+
// across that boundary. It's only not fine to inline this function
67+
// across a DLL boundary. Unfortunately rustc doesn't currently
68+
// have this sort of logic available in an attribute, and it's not
69+
// clear that rustc is even equipped to answer this (it's more of a
70+
// Cargo question kinda). This means that, unfortunately, Windows
71+
// gets the pessimistic path for now where it's never inlined.
72+
//
73+
// The issue of "should enable on Windows sometimes" is #84933
74+
#[cfg_attr(not(windows), inline)]
75+
unsafe fn __getit(
76+
init: $crate::option::Option<&mut $crate::option::Option<$t>>,
77+
) -> $crate::option::Option<&'static $t> {
78+
static __KEY: $crate::thread::__OsLocalKeyInner<$t> =
79+
$crate::thread::__OsLocalKeyInner::new();
80+
81+
// FIXME: remove the #[allow(...)] marker when macros don't
82+
// raise warning for missing/extraneous unsafe blocks anymore.
83+
// See https://github.com/rust-lang/rust/issues/74838.
84+
#[allow(unused_unsafe)]
85+
unsafe {
86+
__KEY.get(move || {
87+
if let $crate::option::Option::Some(init) = init {
88+
if let $crate::option::Option::Some(value) = init.take() {
89+
return value;
90+
} else if $crate::cfg!(debug_assertions) {
91+
$crate::unreachable!("missing default value");
92+
}
93+
}
94+
__init()
95+
})
96+
}
97+
}
98+
99+
unsafe {
100+
$crate::thread::LocalKey::new(__getit)
101+
}
102+
}
103+
};
104+
($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => {
105+
$(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> =
106+
$crate::__thread_local_inner!(@key $t, $($init)*);
107+
}
108+
}
109+
110+
#[doc(hidden)]
111+
#[unstable(feature = "thread_local_internals", reason = "should not be necessary", issue = "none")]
112+
#[macro_export]
113+
#[allow_internal_unstable(thread_local_internals, cfg_target_thread_local, thread_local)]
114+
#[allow_internal_unsafe]
115+
#[cfg(all(target_thread_local, not(all(target_family = "wasm", not(target_feature = "atomics")))))]
116+
macro_rules! __thread_local_inner {
117+
// used to generate the `LocalKey` value for const-initialized thread locals
118+
(@key $t:ty, const $init:expr) => {{
119+
#[cfg_attr(not(windows), inline)] // see comments below
120+
#[deny(unsafe_op_in_unsafe_fn)]
121+
unsafe fn __getit(
122+
_init: $crate::option::Option<&mut $crate::option::Option<$t>>,
123+
) -> $crate::option::Option<&'static $t> {
124+
const INIT_EXPR: $t = $init;
125+
// If the platform has support for `#[thread_local]`, use it.
126+
#[thread_local]
127+
static mut VAL: $t = INIT_EXPR;
128+
129+
// If a dtor isn't needed we can do something "very raw" and
130+
// just get going.
131+
if !$crate::mem::needs_drop::<$t>() {
132+
unsafe {
133+
return $crate::option::Option::Some(&VAL)
134+
}
135+
}
136+
137+
// 0 == dtor not registered
138+
// 1 == dtor registered, dtor not run
139+
// 2 == dtor registered and is running or has run
140+
#[thread_local]
141+
static mut STATE: $crate::primitive::u8 = 0;
142+
143+
unsafe extern "C" fn destroy(ptr: *mut $crate::primitive::u8) {
144+
let ptr = ptr as *mut $t;
145+
146+
unsafe {
147+
$crate::debug_assert_eq!(STATE, 1);
148+
STATE = 2;
149+
$crate::ptr::drop_in_place(ptr);
150+
}
151+
}
152+
153+
unsafe {
154+
match STATE {
155+
// 0 == we haven't registered a destructor, so do
156+
// so now.
157+
0 => {
158+
$crate::thread::__FastLocalKeyInner::<$t>::register_dtor(
159+
$crate::ptr::addr_of_mut!(VAL) as *mut $crate::primitive::u8,
160+
destroy,
161+
);
162+
STATE = 1;
163+
$crate::option::Option::Some(&VAL)
164+
}
165+
// 1 == the destructor is registered and the value
166+
// is valid, so return the pointer.
167+
1 => $crate::option::Option::Some(&VAL),
168+
// otherwise the destructor has already run, so we
169+
// can't give access.
170+
_ => $crate::option::Option::None,
171+
}
172+
}
173+
}
174+
175+
unsafe {
176+
$crate::thread::LocalKey::new(__getit)
177+
}
178+
}};
179+
180+
// used to generate the `LocalKey` value for `thread_local!`
181+
(@key $t:ty, $init:expr) => {
182+
{
183+
#[inline]
184+
fn __init() -> $t { $init }
185+
186+
// When reading this function you might ask "why is this inlined
187+
// everywhere other than Windows?", and that's a very reasonable
188+
// question to ask. The short story is that it segfaults rustc if
189+
// this function is inlined. The longer story is that Windows looks
190+
// to not support `extern` references to thread locals across DLL
191+
// boundaries. This appears to at least not be supported in the ABI
192+
// that LLVM implements.
193+
//
194+
// Because of this we never inline on Windows, but we do inline on
195+
// other platforms (where external references to thread locals
196+
// across DLLs are supported). A better fix for this would be to
197+
// inline this function on Windows, but only for "statically linked"
198+
// components. For example if two separately compiled rlibs end up
199+
// getting linked into a DLL then it's fine to inline this function
200+
// across that boundary. It's only not fine to inline this function
201+
// across a DLL boundary. Unfortunately rustc doesn't currently
202+
// have this sort of logic available in an attribute, and it's not
203+
// clear that rustc is even equipped to answer this (it's more of a
204+
// Cargo question kinda). This means that, unfortunately, Windows
205+
// gets the pessimistic path for now where it's never inlined.
206+
//
207+
// The issue of "should enable on Windows sometimes" is #84933
208+
#[cfg_attr(not(windows), inline)]
209+
unsafe fn __getit(
210+
init: $crate::option::Option<&mut $crate::option::Option<$t>>,
211+
) -> $crate::option::Option<&'static $t> {
212+
#[thread_local]
213+
static __KEY: $crate::thread::__FastLocalKeyInner<$t> =
214+
$crate::thread::__FastLocalKeyInner::new();
215+
216+
// FIXME: remove the #[allow(...)] marker when macros don't
217+
// raise warning for missing/extraneous unsafe blocks anymore.
218+
// See https://github.com/rust-lang/rust/issues/74838.
219+
#[allow(unused_unsafe)]
220+
unsafe {
221+
__KEY.get(move || {
222+
if let $crate::option::Option::Some(init) = init {
223+
if let $crate::option::Option::Some(value) = init.take() {
224+
return value;
225+
} else if $crate::cfg!(debug_assertions) {
226+
$crate::unreachable!("missing default value");
227+
}
228+
}
229+
__init()
230+
})
231+
}
232+
}
233+
234+
unsafe {
235+
$crate::thread::LocalKey::new(__getit)
236+
}
237+
}
238+
};
239+
($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => {
240+
$(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> =
241+
$crate::__thread_local_inner!(@key $t, $($init)*);
242+
}
243+
}
244+
245+
#[doc(hidden)]
246+
#[unstable(feature = "thread_local_internals", reason = "should not be necessary", issue = "none")]
247+
#[macro_export]
248+
#[allow_internal_unstable(thread_local_internals, cfg_target_thread_local, thread_local)]
249+
#[allow_internal_unsafe]
250+
#[cfg(all(target_family = "wasm", not(target_feature = "atomics")))]
251+
macro_rules! __thread_local_inner {
252+
// used to generate the `LocalKey` value for const-initialized thread locals
253+
(@key $t:ty, const $init:expr) => {{
254+
#[inline] // see comments below
255+
#[deny(unsafe_op_in_unsafe_fn)]
256+
unsafe fn __getit(
257+
_init: $crate::option::Option<&mut $crate::option::Option<$t>>,
258+
) -> $crate::option::Option<&'static $t> {
259+
const INIT_EXPR: $t = $init;
260+
261+
// wasm without atomics maps directly to `static mut`, and dtors
262+
// aren't implemented because thread dtors aren't really a thing
263+
// on wasm right now
264+
//
265+
// FIXME(#84224) this should come after the `target_thread_local`
266+
// block.
267+
static mut VAL: $t = INIT_EXPR;
268+
unsafe { $crate::option::Option::Some(&VAL) }
269+
}
270+
271+
unsafe {
272+
$crate::thread::LocalKey::new(__getit)
273+
}
274+
}};
275+
276+
// used to generate the `LocalKey` value for `thread_local!`
277+
(@key $t:ty, $init:expr) => {
278+
{
279+
#[inline]
280+
fn __init() -> $t { $init }
281+
#[inline]
282+
unsafe fn __getit(
283+
init: $crate::option::Option<&mut $crate::option::Option<$t>>,
284+
) -> $crate::option::Option<&'static $t> {
285+
static __KEY: $crate::thread::__StaticLocalKeyInner<$t> =
286+
$crate::thread::__StaticLocalKeyInner::new();
287+
288+
// FIXME: remove the #[allow(...)] marker when macros don't
289+
// raise warning for missing/extraneous unsafe blocks anymore.
290+
// See https://github.com/rust-lang/rust/issues/74838.
291+
#[allow(unused_unsafe)]
292+
unsafe {
293+
__KEY.get(move || {
294+
if let $crate::option::Option::Some(init) = init {
295+
if let $crate::option::Option::Some(value) = init.take() {
296+
return value;
297+
} else if $crate::cfg!(debug_assertions) {
298+
$crate::unreachable!("missing default value");
299+
}
300+
}
301+
__init()
302+
})
303+
}
304+
}
305+
306+
unsafe {
307+
$crate::thread::LocalKey::new(__getit)
308+
}
309+
}
310+
};
311+
($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => {
312+
$(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> =
313+
$crate::__thread_local_inner!(@key $t, $($init)*);
314+
}
315+
}

0 commit comments

Comments
 (0)