Skip to content

Commit ff49b1c

Browse files
committed
fix slow tls on apple targets
1 parent 7c4a55c commit ff49b1c

File tree

6 files changed

+67
-1
lines changed

6 files changed

+67
-1
lines changed

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

+1
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ pub(crate) mod key {
136136
not(target_family = "wasm"),
137137
target_family = "unix",
138138
),
139+
all(not(target_thread_local), target_vendor = "apple"),
139140
target_os = "teeos",
140141
all(target_os = "wasi", target_env = "p1", target_feature = "atomics"),
141142
))] {

library/std/tests/thread_local/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#![feature(cfg_target_thread_local)]
2+
13
#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))]
24
mod tests;
35

library/std/tests/thread_local/tests.rs

+20-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::cell::{Cell, UnsafeCell};
22
use std::sync::atomic::{AtomicU8, Ordering};
33
use std::sync::{Arc, Condvar, Mutex};
4-
use std::thread::{self, Builder, LocalKey};
4+
use std::thread::{self, LocalKey};
55
use std::thread_local;
66

77
#[derive(Clone, Default)]
@@ -345,8 +345,27 @@ fn join_orders_after_tls_destructors() {
345345
}
346346

347347
// Test that thread::current is still available in TLS destructors.
348+
//
349+
// The test won't currently work without target_thread_local, aka with slow tls.
350+
// The runtime tries very hard to drop last the TLS variable that keeps the information about the
351+
// current thread, by using several tricks like deffering the drop to a later round of TLS destruction.
352+
// However, this only seems to work with fast tls.
353+
//
354+
// With slow TLS, it seems that multiple libc implementations will just set the value to null the first
355+
// time they encounter it, regardless of it having a destructor or not. This means that trying to
356+
// retrieve it later in a drop impl of another TLS variable will not work.
357+
//
358+
// ** Apple libc: https://github.com/apple-oss-distributions/libpthread/blob/c032e0b076700a0a47db75528a282b8d3a06531a/src/pthread_tsd.c#L293
359+
// Sets the variable to null if it has a destructor and the value is not null. However, all variables
360+
// created with pthread_key_create are marked as having a destructor, even if the fn ptr called with
361+
// it is null.
362+
// ** glibc: https://github.com/bminor/glibc/blob/e5893e6349541d871e8a25120bca014551d13ff5/nptl/nptl_deallocate_tsd.c#L59
363+
// ** musl: https://github.com/kraj/musl/blob/1880359b54ff7dd9f5016002bfdae4b136007dde/src/thread/pthread_key_create.c#L87
364+
#[cfg(target_thread_local)]
348365
#[test]
349366
fn thread_current_in_dtor() {
367+
use std::thread::Builder;
368+
350369
// Go through one round of TLS destruction first.
351370
struct Defer;
352371
impl Drop for Defer {
+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//! Test if compilation with has-thread-local=false works, and if the output
2+
//! has indeed no fast TLS variables.
3+
4+
//@ only-apple
5+
6+
use run_make_support::run::cmd;
7+
use run_make_support::{cargo, rfs, rustc};
8+
9+
fn main() {
10+
let output =
11+
rustc().print("target-spec-json").args(["-Z", "unstable-options"]).run().stdout_utf8();
12+
let to_search = r#""has-thread-local": true"#;
13+
assert!(output.contains(to_search));
14+
let output = output.replace(to_search, r#""has-thread-local": false"#);
15+
16+
let out_path = "t.json";
17+
rfs::write(out_path, output);
18+
19+
cargo()
20+
.current_dir("tls_test")
21+
.args(["b", "--target", "../t.json", "-Zbuild-std=std,core,panic_abort"])
22+
.run();
23+
24+
let output = cmd("nm").arg("tls_test/target/t/debug/tls_test").run().stdout_utf8();
25+
// If a binary has any fast TLS variables, it should also contain the symbols
26+
// __tlv_bootstrap and __tlv_atexit. We don't want them.
27+
assert!(!output.contains("__tlv_bootstrap"));
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[package]
2+
name = "tls_test"
3+
version = "0.1.0"
4+
edition = "2024"
5+
6+
[dependencies]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
use std::cell::RefCell;
2+
3+
fn main() {
4+
thread_local! {
5+
static S: RefCell<String> = RefCell::default();
6+
}
7+
8+
S.with(|x| *x.borrow_mut() = "pika pika".to_string());
9+
S.with(|x| println!("{}", x.borrow()));
10+
}

0 commit comments

Comments
 (0)