Skip to content

Commit 9cdfc77

Browse files
Support #[rustc_align_static] inside thread_local!
1 parent 565a9ca commit 9cdfc77

File tree

10 files changed

+660
-63
lines changed

10 files changed

+660
-63
lines changed

library/std/src/sys/thread_local/native/eager.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@ enum State {
1010
}
1111

1212
#[allow(missing_debug_implementations)]
13+
#[repr(C)]
1314
pub struct Storage<T> {
14-
state: Cell<State>,
15+
// This field must be first, for correctness of `#[rustc_align_static]`
1516
val: UnsafeCell<T>,
17+
state: Cell<State>,
1618
}
1719

1820
impl<T> Storage<T> {

library/std/src/sys/thread_local/native/lazy.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,11 @@ enum State<D> {
2727
}
2828

2929
#[allow(missing_debug_implementations)]
30+
#[repr(C)]
3031
pub struct Storage<T, D> {
31-
state: Cell<State<D>>,
32+
// This field must be first, for correctness of `#[rustc_align_static]`
3233
value: UnsafeCell<MaybeUninit<T>>,
34+
state: Cell<State<D>>,
3335
}
3436

3537
impl<T, D> Storage<T, D>

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

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,21 +54,23 @@ pub macro thread_local_inner {
5454
// test in `tests/thread.rs` if these types are renamed.
5555

5656
// Used to generate the `LocalKey` value for const-initialized thread locals.
57-
(@key $t:ty, const $init:expr) => {{
57+
(@key $t:ty, $(#[$align_attr:meta])*, const $init:expr) => {{
5858
const __INIT: $t = $init;
5959

6060
unsafe {
6161
$crate::thread::LocalKey::new(const {
6262
if $crate::mem::needs_drop::<$t>() {
6363
|_| {
6464
#[thread_local]
65+
$(#[$align_attr])*
6566
static VAL: $crate::thread::local_impl::EagerStorage<$t>
6667
= $crate::thread::local_impl::EagerStorage::new(__INIT);
6768
VAL.get()
6869
}
6970
} else {
7071
|_| {
7172
#[thread_local]
73+
$(#[$align_attr])*
7274
static VAL: $t = __INIT;
7375
&VAL
7476
}
@@ -78,7 +80,7 @@ pub macro thread_local_inner {
7880
}},
7981

8082
// used to generate the `LocalKey` value for `thread_local!`
81-
(@key $t:ty, $init:expr) => {{
83+
(@key $t:ty, $(#[$align_attr:meta])*, $init:expr) => {{
8284
#[inline]
8385
fn __init() -> $t {
8486
$init
@@ -89,13 +91,15 @@ pub macro thread_local_inner {
8991
if $crate::mem::needs_drop::<$t>() {
9092
|init| {
9193
#[thread_local]
94+
$(#[$align_attr])*
9295
static VAL: $crate::thread::local_impl::LazyStorage<$t, ()>
9396
= $crate::thread::local_impl::LazyStorage::new();
9497
VAL.get_or_init(init, __init)
9598
}
9699
} else {
97100
|init| {
98101
#[thread_local]
102+
$(#[$align_attr])*
99103
static VAL: $crate::thread::local_impl::LazyStorage<$t, !>
100104
= $crate::thread::local_impl::LazyStorage::new();
101105
VAL.get_or_init(init, __init)
@@ -104,9 +108,9 @@ pub macro thread_local_inner {
104108
})
105109
}
106110
}},
107-
($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => {
111+
($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $(#[$align_attr:meta])*, $($init:tt)*) => {
108112
$(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> =
109-
$crate::thread::local_impl::thread_local_inner!(@key $t, $($init)*);
113+
$crate::thread::local_impl::thread_local_inner!(@key $t, $(#[$align_attr])*, $($init)*);
110114
},
111115
}
112116

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

Lines changed: 47 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
//! thread locals and we can instead just use plain statics!
33
44
use crate::cell::{Cell, UnsafeCell};
5+
use crate::mem::MaybeUninit;
56
use crate::ptr;
67

78
#[doc(hidden)]
@@ -11,12 +12,13 @@ use crate::ptr;
1112
#[rustc_macro_transparency = "semitransparent"]
1213
pub macro thread_local_inner {
1314
// used to generate the `LocalKey` value for const-initialized thread locals
14-
(@key $t:ty, const $init:expr) => {{
15+
(@key $t:ty, $(#[$align_attr:meta])*, const $init:expr) => {{
1516
const __INIT: $t = $init;
1617

1718
// NOTE: Please update the shadowing test in `tests/thread.rs` if these types are renamed.
1819
unsafe {
1920
$crate::thread::LocalKey::new(|_| {
21+
$(#[$align_attr])*
2022
static VAL: $crate::thread::local_impl::EagerStorage<$t> =
2123
$crate::thread::local_impl::EagerStorage { value: __INIT };
2224
&VAL.value
@@ -25,7 +27,7 @@ pub macro thread_local_inner {
2527
}},
2628

2729
// used to generate the `LocalKey` value for `thread_local!`
28-
(@key $t:ty, $init:expr) => {{
30+
(@key $t:ty, $(#[$align_attr:meta])*, $init:expr) => {{
2931
#[inline]
3032
fn __init() -> $t { $init }
3133

@@ -34,33 +36,48 @@ pub macro thread_local_inner {
3436
use $crate::thread::local_impl::LazyStorage;
3537

3638
LocalKey::new(|init| {
39+
$(#[$align_attr])*
3740
static VAL: LazyStorage<$t> = LazyStorage::new();
3841
VAL.get(init, __init)
3942
})
4043
}
4144
}},
42-
($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => {
45+
($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $(#[$align_attr:meta])*, $($init:tt)*) => {
4346
$(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> =
44-
$crate::thread::local_impl::thread_local_inner!(@key $t, $($init)*);
47+
$crate::thread::local_impl::thread_local_inner!(@key $t, $(#[$align_attr])*, $($init)*);
4548
},
4649
}
4750

4851
#[allow(missing_debug_implementations)]
52+
#[repr(transparent)] // Required for correctness of `#[rustc_align_static]`
4953
pub struct EagerStorage<T> {
5054
pub value: T,
5155
}
5256

5357
// SAFETY: the target doesn't have threads.
5458
unsafe impl<T> Sync for EagerStorage<T> {}
5559

60+
#[derive(Clone, Copy, PartialEq, Eq)]
61+
enum State {
62+
Initial,
63+
Alive,
64+
Destroying,
65+
}
66+
5667
#[allow(missing_debug_implementations)]
68+
#[repr(C)]
5769
pub struct LazyStorage<T> {
58-
value: UnsafeCell<Option<T>>,
70+
// This field must be first, for correctness of `#[rustc_align_static]`
71+
value: UnsafeCell<MaybeUninit<T>>,
72+
state: Cell<State>,
5973
}
6074

6175
impl<T> LazyStorage<T> {
6276
pub const fn new() -> LazyStorage<T> {
63-
LazyStorage { value: UnsafeCell::new(None) }
77+
LazyStorage {
78+
value: UnsafeCell::new(MaybeUninit::uninit()),
79+
state: Cell::new(State::Initial),
80+
}
6481
}
6582

6683
/// Gets a pointer to the TLS value, potentially initializing it with the
@@ -70,24 +87,39 @@ impl<T> LazyStorage<T> {
7087
/// has occurred.
7188
#[inline]
7289
pub fn get(&'static self, i: Option<&mut Option<T>>, f: impl FnOnce() -> T) -> *const T {
73-
let value = unsafe { &*self.value.get() };
74-
match value {
75-
Some(v) => v,
76-
None => self.initialize(i, f),
90+
if self.state.get() == State::Alive {
91+
self.value.get() as *const T
92+
} else {
93+
self.initialize(i, f)
7794
}
7895
}
7996

8097
#[cold]
8198
fn initialize(&'static self, i: Option<&mut Option<T>>, f: impl FnOnce() -> T) -> *const T {
8299
let value = i.and_then(Option::take).unwrap_or_else(f);
83-
// Destroy the old value, after updating the TLS variable as the
84-
// destructor might reference it.
100+
101+
// Destroy the old value if it is initialized
85102
// FIXME(#110897): maybe panic on recursive initialization.
103+
if self.state.get() == State::Alive {
104+
self.state.set(State::Destroying);
105+
// Safety: we check for no initialization during drop below
106+
unsafe {
107+
ptr::drop_in_place(self.value.get() as *mut T);
108+
}
109+
self.state.set(State::Initial);
110+
}
111+
112+
// Guard against initialization during drop
113+
if self.state.get() == State::Destroying {
114+
panic!("Attempted to initialize thread-local while it is being dropped");
115+
}
116+
86117
unsafe {
87-
self.value.get().replace(Some(value));
118+
self.value.get().write(MaybeUninit::new(value));
88119
}
89-
// SAFETY: we just set this to `Some`.
90-
unsafe { (*self.value.get()).as_ref().unwrap_unchecked() }
120+
self.state.set(State::Alive);
121+
122+
self.value.get() as *const T
91123
}
92124
}
93125

0 commit comments

Comments
 (0)