Skip to content

Commit b0f6b69

Browse files
committed
Do not move thread-locals before dropping
1 parent cb678b9 commit b0f6b69

File tree

1 file changed

+52
-32
lines changed
  • library/std/src/sys/thread_local/native

1 file changed

+52
-32
lines changed
Lines changed: 52 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
use crate::cell::UnsafeCell;
2-
use crate::hint::unreachable_unchecked;
1+
use crate::cell::{Cell, UnsafeCell};
2+
use crate::mem::MaybeUninit;
33
use crate::ptr;
44
use crate::sys::thread_local::{abort_on_dtor_unwind, destructors};
55

6-
pub unsafe trait DestroyedState: Sized {
6+
pub unsafe trait DestroyedState: Sized + Copy {
77
fn register_dtor<T>(s: &Storage<T, Self>);
88
}
99

@@ -19,23 +19,29 @@ unsafe impl DestroyedState for () {
1919
}
2020
}
2121

22-
enum State<T, D> {
23-
Initial,
24-
Alive(T),
22+
#[derive(Copy, Clone)]
23+
enum State<D> {
24+
Uninitialized,
25+
Initializing,
26+
Alive,
2527
Destroyed(D),
2628
}
2729

2830
#[allow(missing_debug_implementations)]
2931
pub struct Storage<T, D> {
30-
state: UnsafeCell<State<T, D>>,
32+
state: Cell<State<D>>,
33+
value: UnsafeCell<MaybeUninit<T>>,
3134
}
3235

3336
impl<T, D> Storage<T, D>
3437
where
3538
D: DestroyedState,
3639
{
3740
pub const fn new() -> Storage<T, D> {
38-
Storage { state: UnsafeCell::new(State::Initial) }
41+
Storage {
42+
state: Cell::new(State::Uninitialized),
43+
value: UnsafeCell::new(MaybeUninit::uninit()),
44+
}
3945
}
4046

4147
/// Gets a pointer to the TLS value, potentially initializing it with the
@@ -49,35 +55,45 @@ where
4955
/// The `self` reference must remain valid until the TLS destructor is run.
5056
#[inline]
5157
pub unsafe fn get_or_init(&self, i: Option<&mut Option<T>>, f: impl FnOnce() -> T) -> *const T {
52-
let state = unsafe { &*self.state.get() };
53-
match state {
54-
State::Alive(v) => v,
55-
State::Destroyed(_) => ptr::null(),
56-
State::Initial => unsafe { self.initialize(i, f) },
58+
if let State::Alive = self.state.get() {
59+
self.value.get().cast()
60+
} else {
61+
self.get_or_init_slow(i, f)
5762
}
5863
}
5964

6065
#[cold]
61-
unsafe fn initialize(&self, i: Option<&mut Option<T>>, f: impl FnOnce() -> T) -> *const T {
62-
// Perform initialization
63-
64-
let v = i.and_then(Option::take).unwrap_or_else(f);
66+
fn get_or_init_slow(&self, i: Option<&mut Option<T>>, f: impl FnOnce() -> T) -> *const T {
67+
// Ensure we have unique access to an uninitialized value.
68+
match self.state.get() {
69+
State::Uninitialized => self.state.set(State::Initializing),
70+
State::Initializing => panic!("thread_local initializer recursively depends on itself"),
71+
State::Alive => return self.value.get().cast(),
72+
State::Destroyed(_) => return ptr::null(),
73+
}
6574

66-
let old = unsafe { self.state.get().replace(State::Alive(v)) };
67-
match old {
68-
// If the variable is not being recursively initialized, register
69-
// the destructor. This might be a noop if the value does not need
70-
// destruction.
71-
State::Initial => D::register_dtor(self),
72-
// Else, drop the old value. This might be changed to a panic.
73-
val => drop(val),
75+
struct BackToUninitOnPanic<'a, D>(&'a Cell<State<D>>);
76+
impl<'a, D> Drop for BackToUninitOnPanic<'a, D> {
77+
fn drop(&mut self) {
78+
self.0.set(State::Uninitialized);
79+
}
7480
}
7581

76-
// SAFETY: the state was just set to `Alive`
82+
// Get the initial value, making sure that we restore the state to uninitialized
83+
// should f panic.
84+
let on_panic = BackToUninitOnPanic(&self.state);
85+
let v = i.and_then(Option::take).unwrap_or_else(f);
86+
crate::mem::forget(on_panic);
87+
88+
// SAFETY: we are !Sync so we have exclusive access to self.value. We also ensured
89+
// that the state was uninitialized so we aren't replacing a value we must keep alive.
7790
unsafe {
78-
let State::Alive(v) = &*self.state.get() else { unreachable_unchecked() };
79-
v
91+
self.value.get().write(MaybeUninit::new(v));
8092
}
93+
94+
self.state.set(State::Alive);
95+
D::register_dtor(self);
96+
self.value.get().cast()
8197
}
8298
}
8399

@@ -92,9 +108,13 @@ unsafe extern "C" fn destroy<T>(ptr: *mut u8) {
92108
// Print a nice abort message if a panic occurs.
93109
abort_on_dtor_unwind(|| {
94110
let storage = unsafe { &*(ptr as *const Storage<T, ()>) };
95-
// Update the state before running the destructor as it may attempt to
96-
// access the variable.
97-
let val = unsafe { storage.state.get().replace(State::Destroyed(())) };
98-
drop(val);
111+
if let State::Alive = storage.state.replace(State::Destroyed(())) {
112+
// SAFETY: we ensured the state was Alive, and prevented running the destructor
113+
// twice by updating the state to Destroyed. This is necessary as the destructor
114+
// may attempt to access the variable.
115+
unsafe {
116+
crate::ptr::drop_in_place(storage.value.get().cast::<T>());
117+
}
118+
}
99119
})
100120
}

0 commit comments

Comments
 (0)