Skip to content

Commit 65db279

Browse files
committed
std: move UNIX to new destructor list implementation
1 parent 08b6a47 commit 65db279

File tree

5 files changed

+163
-111
lines changed

5 files changed

+163
-111
lines changed

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ pub mod rand;
3434
pub mod stack_overflow;
3535
pub mod stdio;
3636
pub mod thread;
37-
pub mod thread_local_dtor;
37+
pub mod thread_local_guard;
3838
pub mod thread_local_key;
3939
pub mod thread_parking;
4040
pub mod time;
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
@@ -25,9 +27,10 @@
2527
// FIXME: The Rust compiler currently omits weakly function definitions (i.e.,
2628
// __cxa_thread_atexit_impl) and its metadata from LLVM IR.
2729
#[no_sanitize(cfi, kcfi)]
28-
pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
30+
pub fn activate() {
31+
use crate::cell::Cell;
2932
use crate::mem;
30-
use crate::sys_common::thread_local_dtor::register_dtor_fallback;
33+
use crate::sys_common::thread_local_key::StaticKey;
3134

3235
/// This is necessary because the __cxa_thread_atexit_impl implementation
3336
/// std links to by default may be a C or C++ implementation that was not
@@ -52,64 +55,47 @@ pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
5255
>;
5356
}
5457

55-
if let Some(f) = __cxa_thread_atexit_impl {
56-
unsafe {
57-
f(
58-
mem::transmute::<
59-
unsafe extern "C" fn(*mut u8),
60-
unsafe extern "C" fn(*mut libc::c_void),
61-
>(dtor),
62-
t.cast(),
63-
&__dso_handle as *const _ as *mut _,
64-
);
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));
6577
}
66-
return;
6778
}
68-
register_dtor_fallback(t, dtor);
6979
}
7080

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

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

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

106-
unsafe extern "C" fn run_dtors(_: *mut u8) {
107-
let mut list = DTORS.take();
108-
while !list.is_empty() {
109-
for (ptr, dtor) in list {
110-
dtor(ptr);
111-
}
112-
list = DTORS.take();
95+
if !REGISTERED.get() {
96+
unsafe {
97+
_tlv_atexit(run_dtors, ptr::null_mut());
98+
REGISTERED.set(true);
11399
}
114100
}
115101
}
@@ -121,7 +107,12 @@ pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
121107
target_os = "aix"
122108
))]
123109
#[cfg_attr(target_family = "wasm", allow(unused))] // might remain unused depending on target details (e.g. wasm32-unknown-emscripten)
124-
pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
125-
use crate::sys_common::thread_local_dtor::register_dtor_fallback;
126-
register_dtor_fallback(t, dtor);
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+
}
127118
}
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

-1
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

-56
This file was deleted.

0 commit comments

Comments
 (0)