Skip to content

Commit 73628fa

Browse files
committed
std: begin unifying TLS destructor lists
1 parent ddef56d commit 73628fa

File tree

3 files changed

+76
-42
lines changed

3 files changed

+76
-42
lines changed

library/std/src/sys/common/thread_local/fast_local.rs

+74-19
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use super::lazy::LazyKeyInner;
2-
use crate::cell::Cell;
3-
use crate::sys::thread_local_dtor::register_dtor;
4-
use crate::{fmt, mem, panic};
2+
use crate::cell::{Cell, RefCell};
3+
use crate::fmt;
4+
use crate::mem::{self, forget};
55

66
#[doc(hidden)]
77
#[allow_internal_unstable(thread_local_internals, cfg_target_thread_local, thread_local)]
@@ -37,13 +37,11 @@ pub macro thread_local_inner {
3737

3838
// Safety: Performs `drop_in_place(ptr as *mut $t)`, and requires
3939
// all that comes with it.
40-
unsafe extern "C" fn destroy(ptr: *mut $crate::primitive::u8) {
41-
$crate::thread::local_impl::abort_on_dtor_unwind(|| {
42-
let old_state = STATE.replace(2);
43-
$crate::debug_assert_eq!(old_state, 1);
44-
// Safety: safety requirement is passed on to caller.
45-
unsafe { $crate::ptr::drop_in_place(ptr.cast::<$t>()); }
46-
});
40+
unsafe fn destroy(ptr: *mut $crate::primitive::u8) {
41+
let old_state = STATE.replace(2);
42+
$crate::debug_assert_eq!(old_state, 1);
43+
// Safety: safety requirement is passed on to caller.
44+
unsafe { $crate::ptr::drop_in_place(ptr.cast::<$t>()); }
4745
}
4846

4947
unsafe {
@@ -152,8 +150,8 @@ impl<T> Key<T> {
152150

153151
// note that this is just a publicly-callable function only for the
154152
// const-initialized form of thread locals, basically a way to call the
155-
// free `register_dtor` function defined elsewhere in std.
156-
pub unsafe fn register_dtor(a: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
153+
// free `register_dtor` function.
154+
pub unsafe fn register_dtor(a: *mut u8, dtor: unsafe fn(*mut u8)) {
157155
unsafe {
158156
register_dtor(a, dtor);
159157
}
@@ -217,7 +215,7 @@ impl<T> Key<T> {
217215
}
218216
}
219217

220-
unsafe extern "C" fn destroy_value<T>(ptr: *mut u8) {
218+
unsafe fn destroy_value<T>(ptr: *mut u8) {
221219
let ptr = ptr as *mut Key<T>;
222220

223221
// SAFETY:
@@ -230,14 +228,71 @@ unsafe extern "C" fn destroy_value<T>(ptr: *mut u8) {
230228
// `Option<T>` to `None`, and `dtor_state` to `RunningOrHasRun`. This
231229
// causes future calls to `get` to run `try_initialize_drop` again,
232230
// which will now fail, and return `None`.
233-
//
234-
// Wrap the call in a catch to ensure unwinding is caught in the event
235-
// a panic takes place in a destructor.
236-
if let Err(_) = panic::catch_unwind(panic::AssertUnwindSafe(|| unsafe {
231+
unsafe {
237232
let value = (*ptr).inner.take();
238233
(*ptr).dtor_state.set(DtorState::RunningOrHasRun);
239234
drop(value);
240-
})) {
241-
rtabort!("thread local panicked on drop");
242235
}
243236
}
237+
238+
#[thread_local]
239+
static DTORS: RefCell<Vec<(*mut u8, unsafe fn(*mut u8))>> = RefCell::new(Vec::new());
240+
241+
// Ensure this can never be inlined on Windows because otherwise this may break
242+
// in dylibs. See #44391.
243+
#[cfg_attr(windows, inline(never))]
244+
unsafe fn register_dtor(t: *mut u8, dtor: unsafe fn(*mut u8)) {
245+
// Ensure that destructors are run on thread exit.
246+
crate::sys::thread_local_guard::activate();
247+
248+
let mut dtors = match DTORS.try_borrow_mut() {
249+
Ok(dtors) => dtors,
250+
// The only place this function can be called reentrantly is inside the
251+
// heap allocator. This is currently forbidden.
252+
Err(_) => rtabort!("the global allocator may not register TLS destructors"),
253+
};
254+
dtors.push((t, dtor));
255+
}
256+
257+
/// Called by the platform on thread exit to run all registered destructors.
258+
/// The signature was chosen so that this function may be passed as a callback
259+
/// to platform functions. The argument is ignored.
260+
///
261+
/// # Safety
262+
/// May only be called on thread exit. In particular, no thread locals may
263+
/// currently be referenced.
264+
pub unsafe extern "C" fn run_dtors(_unused: *mut u8) {
265+
struct Guard;
266+
impl Drop for Guard {
267+
fn drop(&mut self) {
268+
rtabort!("thread local panicked on drop");
269+
}
270+
}
271+
272+
// This function must not unwind. This is ensured by the `extern "C"` ABI
273+
// regardless, but by using a guard that aborts on drop, we can give a
274+
// nicer abort reason.
275+
let guard = Guard;
276+
let dtors = &DTORS;
277+
278+
loop {
279+
// Ensure that the `RefMut` guard is not held while the destructor is
280+
// executed to allow initializing TLS variables in destructors.
281+
let (t, dtor) = {
282+
let mut dtors = dtors.borrow_mut();
283+
match dtors.pop() {
284+
Some(entry) => entry,
285+
None => break,
286+
}
287+
};
288+
289+
unsafe {
290+
(dtor)(t);
291+
}
292+
}
293+
294+
// All destructors were run, deallocate the list.
295+
drop(dtors.replace(Vec::new()));
296+
// Disarm the guard.
297+
forget(guard);
298+
}

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

+1-22
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ cfg_if::cfg_if! {
1515
#[doc(hidden)]
1616
mod fast_local;
1717
#[doc(hidden)]
18-
pub use fast_local::{Key, thread_local_inner};
18+
pub use fast_local::{Key, thread_local_inner, run_dtors};
1919
} else {
2020
#[doc(hidden)]
2121
mod os_local;
@@ -101,24 +101,3 @@ mod lazy {
101101
}
102102
}
103103
}
104-
105-
/// Run a callback in a scenario which must not unwind (such as a `extern "C"
106-
/// fn` declared in a user crate). If the callback unwinds anyway, then
107-
/// `rtabort` with a message about thread local panicking on drop.
108-
#[inline]
109-
pub fn abort_on_dtor_unwind(f: impl FnOnce()) {
110-
// Using a guard like this is lower cost.
111-
let guard = DtorUnwindGuard;
112-
f();
113-
core::mem::forget(guard);
114-
115-
struct DtorUnwindGuard;
116-
impl Drop for DtorUnwindGuard {
117-
#[inline]
118-
fn drop(&mut self) {
119-
// This is not terribly descriptive, but it doesn't need to be as we'll
120-
// already have printed a panic message at this point.
121-
rtabort!("thread local panicked on drop");
122-
}
123-
}
124-
}

library/std/src/thread/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ cfg_if::cfg_if! {
206206
#[doc(hidden)]
207207
#[unstable(feature = "thread_local_internals", issue = "none")]
208208
pub mod local_impl {
209-
pub use crate::sys::common::thread_local::{thread_local_inner, Key, abort_on_dtor_unwind};
209+
pub use crate::sys::common::thread_local::{thread_local_inner, Key};
210210
}
211211
}
212212
}

0 commit comments

Comments
 (0)