Skip to content

Commit ca68d4d

Browse files
committed
std: begin unifying TLS destructor lists
1 parent 1be4688 commit ca68d4d

File tree

3 files changed

+69
-41
lines changed

3 files changed

+69
-41
lines changed

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

Lines changed: 67 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use super::lazy::LazyKeyInner;
2-
use crate::cell::Cell;
3-
use crate::sys::thread_local_dtor::register_dtor;
2+
use crate::cell::{Cell, RefCell};
43
use crate::{fmt, mem, panic};
54

65
#[doc(hidden)]
@@ -39,13 +38,11 @@ pub macro thread_local_inner {
3938

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

5148
unsafe {
@@ -155,8 +152,8 @@ impl<T> Key<T> {
155152

156153
// note that this is just a publicly-callable function only for the
157154
// const-initialized form of thread locals, basically a way to call the
158-
// free `register_dtor` function defined elsewhere in std.
159-
pub unsafe fn register_dtor(a: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
155+
// free `register_dtor` function.
156+
pub unsafe fn register_dtor(a: *mut u8, dtor: unsafe fn(*mut u8)) {
160157
unsafe {
161158
register_dtor(a, dtor);
162159
}
@@ -220,7 +217,7 @@ impl<T> Key<T> {
220217
}
221218
}
222219

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

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

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

Lines changed: 1 addition & 22 deletions
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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ cfg_if::cfg_if! {
205205
#[doc(hidden)]
206206
#[unstable(feature = "thread_local_internals", issue = "none")]
207207
pub mod local_impl {
208-
pub use crate::sys::common::thread_local::{thread_local_inner, Key, abort_on_dtor_unwind};
208+
pub use crate::sys::common::thread_local::{thread_local_inner, Key};
209209
}
210210
}
211211
}

0 commit comments

Comments
 (0)