Skip to content

Commit 13cc6af

Browse files
committed
std: move UNIX to new destructor list implementation
1 parent ca68d4d commit 13cc6af

File tree

5 files changed

+163
-111
lines changed

5 files changed

+163
-111
lines changed

library/std/src/sys/pal/unix/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ pub mod rand;
3333
pub mod stack_overflow;
3434
pub mod stdio;
3535
pub mod thread;
36-
pub mod thread_local_dtor;
36+
pub mod thread_local_guard;
3737
pub mod thread_local_key;
3838
pub mod thread_parking;
3939
pub mod time;
Lines changed: 44 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
//! Ensures that thread-local destructors are run on thread exit.
2+
13
#![cfg(target_thread_local)]
24
#![unstable(feature = "thread_local_internals", issue = "none")]
35

4-
//! Provides thread-local destructors without an associated "key", which
5-
//! can be more efficient.
6+
use crate::ptr;
7+
use crate::sys::common::thread_local::run_dtors;
68

79
// Since what appears to be glibc 2.18 this symbol has been shipped which
810
// GCC and clang both use to invoke destructors in thread_local globals, so
@@ -23,9 +25,10 @@
2325
// FIXME: The Rust compiler currently omits weakly function definitions (i.e.,
2426
// __cxa_thread_atexit_impl) and its metadata from LLVM IR.
2527
#[no_sanitize(cfi, kcfi)]
26-
pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
28+
pub fn activate() {
29+
use crate::cell::Cell;
2730
use crate::mem;
28-
use crate::sys_common::thread_local_dtor::register_dtor_fallback;
31+
use crate::sys_common::thread_local_key::StaticKey;
2932

3033
/// This is necessary because the __cxa_thread_atexit_impl implementation
3134
/// std links to by default may be a C or C++ implementation that was not
@@ -50,64 +53,47 @@ pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
5053
>;
5154
}
5255

53-
if let Some(f) = __cxa_thread_atexit_impl {
54-
unsafe {
55-
f(
56-
mem::transmute::<
57-
unsafe extern "C" fn(*mut u8),
58-
unsafe extern "C" fn(*mut libc::c_void),
59-
>(dtor),
60-
t.cast(),
61-
&__dso_handle as *const _ as *mut _,
62-
);
56+
unsafe {
57+
if let Some(atexit) = __cxa_thread_atexit_impl {
58+
#[thread_local]
59+
static REGISTERED: Cell<bool> = Cell::new(false);
60+
if !REGISTERED.get() {
61+
atexit(
62+
mem::transmute::<
63+
unsafe extern "C" fn(*mut u8),
64+
unsafe extern "C" fn(*mut libc::c_void),
65+
>(run_dtors),
66+
ptr::null_mut(),
67+
&__dso_handle as *const _ as *mut _,
68+
);
69+
REGISTERED.set(true);
70+
}
71+
} else {
72+
static KEY: StaticKey = StaticKey::new(Some(run_dtors));
73+
74+
KEY.set(ptr::invalid_mut(1));
6375
}
64-
return;
6576
}
66-
register_dtor_fallback(t, dtor);
6777
}
6878

69-
// This implementation is very similar to register_dtor_fallback in
70-
// sys_common/thread_local.rs. The main difference is that we want to hook into
71-
// macOS's analog of the above linux function, _tlv_atexit. OSX will run the
72-
// registered dtors before any TLS slots get freed, and when the main thread
79+
// We hook into macOS's analog of the above linux function, _tlv_atexit. OSX
80+
// will run `run_dtors` before any TLS slots get freed, and when the main thread
7381
// exits.
74-
//
75-
// Unfortunately, calling _tlv_atexit while tls dtors are running is UB. The
76-
// workaround below is to register, via _tlv_atexit, a custom DTOR list once per
77-
// thread. thread_local dtors are pushed to the DTOR list without calling
78-
// _tlv_atexit.
7982
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos", target_os = "tvos"))]
80-
pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
81-
use crate::cell::{Cell, RefCell};
82-
use crate::ptr;
83-
84-
#[thread_local]
85-
static REGISTERED: Cell<bool> = Cell::new(false);
86-
87-
#[thread_local]
88-
static DTORS: RefCell<Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>> = RefCell::new(Vec::new());
89-
90-
if !REGISTERED.get() {
91-
_tlv_atexit(run_dtors, ptr::null_mut());
92-
REGISTERED.set(true);
93-
}
83+
pub fn activate() {
84+
use crate::cell::Cell;
9485

9586
extern "C" {
9687
fn _tlv_atexit(dtor: unsafe extern "C" fn(*mut u8), arg: *mut u8);
9788
}
9889

99-
match DTORS.try_borrow_mut() {
100-
Ok(mut dtors) => dtors.push((t, dtor)),
101-
Err(_) => rtabort!("global allocator may not use TLS"),
102-
}
90+
#[thread_local]
91+
static REGISTERED: Cell<bool> = Cell::new(false);
10392

104-
unsafe extern "C" fn run_dtors(_: *mut u8) {
105-
let mut list = DTORS.take();
106-
while !list.is_empty() {
107-
for (ptr, dtor) in list {
108-
dtor(ptr);
109-
}
110-
list = DTORS.take();
93+
if !REGISTERED.get() {
94+
unsafe {
95+
_tlv_atexit(run_dtors, ptr::null_mut());
96+
REGISTERED.set(true);
11197
}
11298
}
11399
}
@@ -120,7 +106,12 @@ pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
120106
target_os = "freebsd",
121107
))]
122108
#[cfg_attr(target_family = "wasm", allow(unused))] // might remain unused depending on target details (e.g. wasm32-unknown-emscripten)
123-
pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
124-
use crate::sys_common::thread_local_dtor::register_dtor_fallback;
125-
register_dtor_fallback(t, dtor);
109+
pub fn activate() {
110+
use crate::sys_common::thread_local_key::StaticKey;
111+
112+
static KEY: StaticKey = StaticKey::new(Some(run_dtors));
113+
114+
unsafe {
115+
KEY.set(ptr::invalid_mut(1));
116+
}
126117
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
//! Ensures that thread-local destructors are run on thread exit.
2+
3+
#![cfg(target_thread_local)]
4+
#![unstable(feature = "thread_local_internals", issue = "none")]
5+
6+
use crate::ptr;
7+
use crate::sys::common::thread_local::run_dtors;
8+
9+
// Since what appears to be glibc 2.18 this symbol has been shipped which
10+
// GCC and clang both use to invoke destructors in thread_local globals, so
11+
// let's do the same!
12+
//
13+
// Note, however, that we run on lots older linuxes, as well as cross
14+
// compiling from a newer linux to an older linux, so we also have a
15+
// fallback implementation to use as well.
16+
#[cfg_attr(bootstrap, allow(unexpected_cfgs))]
17+
#[cfg(any(
18+
target_os = "linux",
19+
target_os = "android",
20+
target_os = "fuchsia",
21+
target_os = "redox",
22+
target_os = "hurd",
23+
target_os = "freebsd",
24+
target_os = "netbsd",
25+
target_os = "dragonfly"
26+
))]
27+
// FIXME: The Rust compiler currently omits weakly function definitions (i.e.,
28+
// __cxa_thread_atexit_impl) and its metadata from LLVM IR.
29+
#[no_sanitize(cfi, kcfi)]
30+
pub fn activate() {
31+
use crate::cell::Cell;
32+
use crate::mem;
33+
use crate::sys_common::thread_local_key::StaticKey;
34+
35+
/// This is necessary because the __cxa_thread_atexit_impl implementation
36+
/// std links to by default may be a C or C++ implementation that was not
37+
/// compiled using the Clang integer normalization option.
38+
#[cfg(sanitizer_cfi_normalize_integers)]
39+
use core::ffi::c_int;
40+
#[cfg(not(sanitizer_cfi_normalize_integers))]
41+
#[cfi_encoding = "i"]
42+
#[repr(transparent)]
43+
pub struct c_int(pub libc::c_int);
44+
45+
extern "C" {
46+
#[linkage = "extern_weak"]
47+
static __dso_handle: *mut u8;
48+
#[linkage = "extern_weak"]
49+
static __cxa_thread_atexit_impl: Option<
50+
extern "C" fn(
51+
unsafe extern "C" fn(*mut libc::c_void),
52+
*mut libc::c_void,
53+
*mut libc::c_void,
54+
) -> c_int,
55+
>;
56+
}
57+
58+
unsafe {
59+
if let Some(atexit) = __cxa_thread_atexit_impl {
60+
#[thread_local]
61+
static REGISTERED: Cell<bool> = Cell::new(false);
62+
if !REGISTERED.get() {
63+
atexit(
64+
mem::transmute::<
65+
unsafe extern "C" fn(*mut u8),
66+
unsafe extern "C" fn(*mut libc::c_void),
67+
>(run_dtors),
68+
ptr::null_mut(),
69+
&__dso_handle as *const _ as *mut _,
70+
);
71+
REGISTERED.set(true);
72+
}
73+
} else {
74+
static KEY: StaticKey = StaticKey::new(Some(run_dtors));
75+
76+
KEY.set(ptr::invalid_mut(1));
77+
}
78+
}
79+
}
80+
81+
// We hook into macOS's analog of the above linux function, _tlv_atexit. OSX
82+
// will run `run_dtors` before any TLS slots get freed, and when the main thread
83+
// exits.
84+
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos", target_os = "tvos"))]
85+
pub fn activate() {
86+
use crate::cell::Cell;
87+
88+
extern "C" {
89+
fn _tlv_atexit(dtor: unsafe extern "C" fn(*mut u8), arg: *mut u8);
90+
}
91+
92+
#[thread_local]
93+
static REGISTERED: Cell<bool> = Cell::new(false);
94+
95+
if !REGISTERED.get() {
96+
unsafe {
97+
_tlv_atexit(run_dtors, ptr::null_mut());
98+
REGISTERED.set(true);
99+
}
100+
}
101+
}
102+
103+
#[cfg(any(
104+
target_os = "vxworks",
105+
target_os = "horizon",
106+
target_os = "emscripten",
107+
target_os = "aix"
108+
))]
109+
#[cfg_attr(target_family = "wasm", allow(unused))] // might remain unused depending on target details (e.g. wasm32-unknown-emscripten)
110+
pub fn activate() {
111+
use crate::sys_common::thread_local_key::StaticKey;
112+
113+
static KEY: StaticKey = StaticKey::new(Some(run_dtors));
114+
115+
unsafe {
116+
KEY.set(ptr::invalid_mut(1));
117+
}
118+
}

library/std/src/sys_common/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ pub mod once;
2929
pub mod process;
3030
pub mod thread;
3131
pub mod thread_info;
32-
pub mod thread_local_dtor;
3332
pub mod thread_parking;
3433
pub mod wstr;
3534
pub mod wtf8;

library/std/src/sys_common/thread_local_dtor.rs

Lines changed: 0 additions & 56 deletions
This file was deleted.

0 commit comments

Comments
 (0)