Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 45366f5

Browse files
committedOct 2, 2024·
Auto merge of rust-lang#127912 - joboet:tls_dtor_thread_current, r=cupiver
std: make `thread::current` available in all `thread_local!` destructors ... and thereby allow the panic runtime to always print the right thread name. This works by modifying the TLS destructor system to schedule a runtime cleanup function after all other TLS destructors registered by `std` have run. Unfortunately, this doesn't affect foreign TLS destructors, `thread::current` will still panic there. Additionally, the thread ID returned by `current_id` will now always be available, even inside the global allocator, and will not change during the lifetime of one thread (this was previously the case with key-based TLS). The mechanisms I added for this (`local_pointer` and `thread_cleanup`) will also allow finally fixing rust-lang#111272 by moving the signal stack to a similar runtime-cleanup TLS variable.
2 parents 360f7d7 + b1e383b commit 45366f5

File tree

21 files changed

+533
-139
lines changed

21 files changed

+533
-139
lines changed
 

‎library/std/src/rt.rs

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@ pub use crate::panicking::{begin_panic, panic_count};
2121
pub use core::panicking::{panic_display, panic_fmt};
2222

2323
#[rustfmt::skip]
24+
use crate::any::Any;
2425
use crate::sync::Once;
25-
use crate::sys;
2626
use crate::thread::{self, Thread};
27+
use crate::{mem, panic, sys};
2728

2829
// Prints to the "panic output", depending on the platform this may be:
2930
// - the standard error output
@@ -66,6 +67,11 @@ macro_rules! rtunwrap {
6667
};
6768
}
6869

70+
fn handle_rt_panic(e: Box<dyn Any + Send>) {
71+
mem::forget(e);
72+
rtabort!("initialization or cleanup bug");
73+
}
74+
6975
// One-time runtime initialization.
7076
// Runs before `main`.
7177
// SAFETY: must be called only once during runtime initialization.
@@ -101,6 +107,20 @@ unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) {
101107
thread::set_current(thread);
102108
}
103109

110+
/// Clean up the thread-local runtime state. This *should* be run after all other
111+
/// code managed by the Rust runtime, but will not cause UB if that condition is
112+
/// not fulfilled. Also note that this function is not guaranteed to be run, but
113+
/// skipping it will cause leaks and therefore is to be avoided.
114+
pub(crate) fn thread_cleanup() {
115+
// This function is run in situations where unwinding leads to an abort
116+
// (think `extern "C"` functions). Abort here instead so that we can
117+
// print a nice message.
118+
panic::catch_unwind(|| {
119+
crate::thread::drop_current();
120+
})
121+
.unwrap_or_else(handle_rt_panic);
122+
}
123+
104124
// One-time runtime cleanup.
105125
// Runs after `main` or at program exit.
106126
// NOTE: this is not guaranteed to run, for example when the program aborts.
@@ -123,11 +143,6 @@ fn lang_start_internal(
123143
argv: *const *const u8,
124144
sigpipe: u8,
125145
) -> Result<isize, !> {
126-
use crate::{mem, panic};
127-
let rt_abort = move |e| {
128-
mem::forget(e);
129-
rtabort!("initialization or cleanup bug");
130-
};
131146
// Guard against the code called by this function from unwinding outside of the Rust-controlled
132147
// code, which is UB. This is a requirement imposed by a combination of how the
133148
// `#[lang="start"]` attribute is implemented as well as by the implementation of the panicking
@@ -139,16 +154,17 @@ fn lang_start_internal(
139154
// prevent std from accidentally introducing a panic to these functions. Another is from
140155
// user code from `main` or, more nefariously, as described in e.g. issue #86030.
141156
// SAFETY: Only called once during runtime initialization.
142-
panic::catch_unwind(move || unsafe { init(argc, argv, sigpipe) }).map_err(rt_abort)?;
157+
panic::catch_unwind(move || unsafe { init(argc, argv, sigpipe) })
158+
.unwrap_or_else(handle_rt_panic);
143159
let ret_code = panic::catch_unwind(move || panic::catch_unwind(main).unwrap_or(101) as isize)
144160
.map_err(move |e| {
145161
mem::forget(e);
146162
rtabort!("drop of the panic payload panicked");
147163
});
148-
panic::catch_unwind(cleanup).map_err(rt_abort)?;
164+
panic::catch_unwind(cleanup).unwrap_or_else(handle_rt_panic);
149165
// Guard against multiple threads calling `libc::exit` concurrently.
150166
// See the documentation for `unique_thread_exit` for more information.
151-
panic::catch_unwind(|| crate::sys::exit_guard::unique_thread_exit()).map_err(rt_abort)?;
167+
panic::catch_unwind(crate::sys::exit_guard::unique_thread_exit).unwrap_or_else(handle_rt_panic);
152168
ret_code
153169
}
154170

‎library/std/src/sys/pal/hermit/mod.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,11 @@ pub unsafe extern "C" fn runtime_entry(
9292
unsafe {
9393
crate::sys::thread_local::destructors::run();
9494
}
95-
unsafe { hermit_abi::exit(result) }
95+
crate::rt::thread_cleanup();
96+
97+
unsafe {
98+
hermit_abi::exit(result);
99+
}
96100
}
97101

98102
#[inline]

‎library/std/src/sys/pal/hermit/thread.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ impl Thread {
5353

5454
// run all destructors
5555
crate::sys::thread_local::destructors::run();
56+
crate::rt::thread_cleanup();
5657
}
5758
}
5859
}

‎library/std/src/sys/sync/rwlock/queue.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ use crate::mem;
113113
use crate::ptr::{self, NonNull, null_mut, without_provenance_mut};
114114
use crate::sync::atomic::Ordering::{AcqRel, Acquire, Relaxed, Release};
115115
use crate::sync::atomic::{AtomicBool, AtomicPtr};
116-
use crate::thread::{self, Thread};
116+
use crate::thread::{self, Thread, ThreadId};
117117

118118
// Locking uses exponential backoff. `SPIN_COUNT` indicates how many times the
119119
// locking operation will be retried.
@@ -200,7 +200,9 @@ impl Node {
200200
fn prepare(&mut self) {
201201
// Fall back to creating an unnamed `Thread` handle to allow locking in
202202
// TLS destructors.
203-
self.thread.get_or_init(|| thread::try_current().unwrap_or_else(Thread::new_unnamed));
203+
self.thread.get_or_init(|| {
204+
thread::try_current().unwrap_or_else(|| Thread::new_unnamed(ThreadId::new()))
205+
});
204206
self.completed = AtomicBool::new(false);
205207
}
206208

‎library/std/src/sys/thread_local/guard/apple.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ pub fn enable() {
2626
unsafe extern "C" fn run_dtors(_: *mut u8) {
2727
unsafe {
2828
destructors::run();
29+
crate::rt::thread_cleanup();
2930
}
3031
}
3132
}

‎library/std/src/sys/thread_local/guard/key.rs

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
//! that will run all native TLS destructors in the destructor list.
44
55
use crate::ptr;
6-
use crate::sys::thread_local::destructors;
76
use crate::sys::thread_local::key::{LazyKey, set};
87

8+
#[cfg(target_thread_local)]
99
pub fn enable() {
10+
use crate::sys::thread_local::destructors;
11+
1012
static DTORS: LazyKey = LazyKey::new(Some(run));
1113

1214
// Setting the key value to something other than NULL will result in the
@@ -18,6 +20,41 @@ pub fn enable() {
1820
unsafe extern "C" fn run(_: *mut u8) {
1921
unsafe {
2022
destructors::run();
23+
// On platforms with `__cxa_thread_atexit_impl`, `destructors::run`
24+
// does nothing on newer systems as the TLS destructors are
25+
// registered with the system. But because all of those platforms
26+
// call the destructors of TLS keys after the registered ones, this
27+
// function will still be run last (at the time of writing).
28+
crate::rt::thread_cleanup();
29+
}
30+
}
31+
}
32+
33+
/// On platforms with key-based TLS, the system runs the destructors for us.
34+
/// We still have to make sure that [`crate::rt::thread_cleanup`] is called,
35+
/// however. This is done by defering the execution of a TLS destructor to
36+
/// the next round of destruction inside the TLS destructors.
37+
#[cfg(not(target_thread_local))]
38+
pub fn enable() {
39+
const DEFER: *mut u8 = ptr::without_provenance_mut(1);
40+
const RUN: *mut u8 = ptr::without_provenance_mut(2);
41+
42+
static CLEANUP: LazyKey = LazyKey::new(Some(run));
43+
44+
unsafe { set(CLEANUP.force(), DEFER) }
45+
46+
unsafe extern "C" fn run(state: *mut u8) {
47+
if state == DEFER {
48+
// Make sure that this function is run again in the next round of
49+
// TLS destruction. If there is no futher round, there will be leaks,
50+
// but that's okay, `thread_cleanup` is not guaranteed to be called.
51+
unsafe { set(CLEANUP.force(), RUN) }
52+
} else {
53+
debug_assert_eq!(state, RUN);
54+
// If the state is still RUN in the next round of TLS destruction,
55+
// it means that no other TLS destructors defined by this runtime
56+
// have been run, as they would have set the state to DEFER.
57+
crate::rt::thread_cleanup();
2158
}
2259
}
2360
}

‎library/std/src/sys/thread_local/guard/solid.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ pub fn enable() {
1919
}
2020

2121
unsafe extern "C" fn tls_dtor(_unused: *mut u8) {
22-
unsafe { destructors::run() };
22+
unsafe {
23+
destructors::run();
24+
crate::rt::thread_cleanup();
25+
}
2326
}
2427
}

‎library/std/src/sys/thread_local/guard/windows.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,13 +80,13 @@ pub static CALLBACK: unsafe extern "system" fn(*mut c_void, u32, *mut c_void) =
8080

8181
unsafe extern "system" fn tls_callback(_h: *mut c_void, dw_reason: u32, _pv: *mut c_void) {
8282
if dw_reason == c::DLL_THREAD_DETACH || dw_reason == c::DLL_PROCESS_DETACH {
83-
#[cfg(target_thread_local)]
8483
unsafe {
84+
#[cfg(target_thread_local)]
8585
super::super::destructors::run();
86-
}
87-
#[cfg(not(target_thread_local))]
88-
unsafe {
86+
#[cfg(not(target_thread_local))]
8987
super::super::key::run_dtors();
88+
89+
crate::rt::thread_cleanup();
9090
}
9191
}
9292
}

‎library/std/src/sys/thread_local/key/xous.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,4 +212,6 @@ unsafe fn run_dtors() {
212212
unsafe { cur = (*cur).next };
213213
}
214214
}
215+
216+
crate::rt::thread_cleanup();
215217
}

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

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,15 @@ cfg_if::cfg_if! {
3131
))] {
3232
mod statik;
3333
pub use statik::{EagerStorage, LazyStorage, thread_local_inner};
34+
pub(crate) use statik::{LocalPointer, local_pointer};
3435
} else if #[cfg(target_thread_local)] {
3536
mod native;
3637
pub use native::{EagerStorage, LazyStorage, thread_local_inner};
38+
pub(crate) use native::{LocalPointer, local_pointer};
3739
} else {
3840
mod os;
3941
pub use os::{Storage, thread_local_inner};
42+
pub(crate) use os::{LocalPointer, local_pointer};
4043
}
4144
}
4245

@@ -72,36 +75,44 @@ pub(crate) mod destructors {
7275
}
7376

7477
/// This module provides a way to schedule the execution of the destructor list
75-
/// on systems without a per-variable destructor system.
76-
mod guard {
78+
/// and the [runtime cleanup](crate::rt::thread_cleanup) function. Calling `enable`
79+
/// should ensure that these functions are called at the right times.
80+
pub(crate) mod guard {
7781
cfg_if::cfg_if! {
7882
if #[cfg(all(target_thread_local, target_vendor = "apple"))] {
7983
mod apple;
80-
pub(super) use apple::enable;
84+
pub(crate) use apple::enable;
8185
} else if #[cfg(target_os = "windows")] {
8286
mod windows;
83-
pub(super) use windows::enable;
87+
pub(crate) use windows::enable;
8488
} else if #[cfg(any(
85-
all(target_family = "wasm", target_feature = "atomics"),
89+
target_family = "wasm",
90+
target_os = "uefi",
91+
target_os = "zkvm",
8692
))] {
87-
pub(super) fn enable() {
88-
// FIXME: Right now there is no concept of "thread exit", but
89-
// this is likely going to show up at some point in the form of
90-
// an exported symbol that the wasm runtime is going to be
91-
// expected to call. For now we just leak everything, but if
92-
// such a function starts to exist it will probably need to
93+
pub(crate) fn enable() {
94+
// FIXME: Right now there is no concept of "thread exit" on
95+
// wasm, but this is likely going to show up at some point in
96+
// the form of an exported symbol that the wasm runtime is going
97+
// to be expected to call. For now we just leak everything, but
98+
// if such a function starts to exist it will probably need to
9399
// iterate the destructor list with this function:
94100
#[allow(unused)]
95-
use super::destructors::run;
101+
use crate::rt::thread_cleanup;
96102
}
97-
} else if #[cfg(target_os = "hermit")] {
98-
pub(super) fn enable() {}
103+
} else if #[cfg(any(
104+
target_os = "hermit",
105+
target_os = "xous",
106+
))] {
107+
// `std` is the only runtime, so it just calls the destructor functions
108+
// itself when the time comes.
109+
pub(crate) fn enable() {}
99110
} else if #[cfg(target_os = "solid_asp3")] {
100111
mod solid;
101-
pub(super) use solid::enable;
102-
} else if #[cfg(all(target_thread_local, not(target_family = "wasm")))] {
112+
pub(crate) use solid::enable;
113+
} else {
103114
mod key;
104-
pub(super) use key::enable;
115+
pub(crate) use key::enable;
105116
}
106117
}
107118
}

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

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@
2929
//! eliminates the `Destroyed` state for these values, which can allow more niche
3030
//! optimizations to occur for the `State` enum. For `Drop` types, `()` is used.
3131
32+
use crate::cell::Cell;
33+
use crate::ptr;
34+
3235
mod eager;
3336
mod lazy;
3437

@@ -107,3 +110,31 @@ pub macro thread_local_inner {
107110
$crate::thread::local_impl::thread_local_inner!(@key $t, $($init)*);
108111
},
109112
}
113+
114+
#[rustc_macro_transparency = "semitransparent"]
115+
pub(crate) macro local_pointer {
116+
() => {},
117+
($vis:vis static $name:ident; $($rest:tt)*) => {
118+
#[thread_local]
119+
$vis static $name: $crate::sys::thread_local::LocalPointer = $crate::sys::thread_local::LocalPointer::__new();
120+
$crate::sys::thread_local::local_pointer! { $($rest)* }
121+
},
122+
}
123+
124+
pub(crate) struct LocalPointer {
125+
p: Cell<*mut ()>,
126+
}
127+
128+
impl LocalPointer {
129+
pub const fn __new() -> LocalPointer {
130+
LocalPointer { p: Cell::new(ptr::null_mut()) }
131+
}
132+
133+
pub fn get(&self) -> *mut () {
134+
self.p.get()
135+
}
136+
137+
pub fn set(&self, p: *mut ()) {
138+
self.p.set(p)
139+
}
140+
}

‎library/std/src/sys/thread_local/os.rs

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
use super::abort_on_dtor_unwind;
1+
use super::key::{Key, LazyKey, get, set};
2+
use super::{abort_on_dtor_unwind, guard};
23
use crate::cell::Cell;
34
use crate::marker::PhantomData;
45
use crate::ptr;
5-
use crate::sys::thread_local::key::{Key, LazyKey, get, set};
66

77
#[doc(hidden)]
88
#[allow_internal_unstable(thread_local_internals)]
@@ -138,5 +138,35 @@ unsafe extern "C" fn destroy_value<T: 'static>(ptr: *mut u8) {
138138
drop(ptr);
139139
// SAFETY: `key` is the TLS key `ptr` was stored under.
140140
unsafe { set(key, ptr::null_mut()) };
141+
// Make sure that the runtime cleanup will be performed
142+
// after the next round of TLS destruction.
143+
guard::enable();
141144
});
142145
}
146+
147+
#[rustc_macro_transparency = "semitransparent"]
148+
pub(crate) macro local_pointer {
149+
() => {},
150+
($vis:vis static $name:ident; $($rest:tt)*) => {
151+
$vis static $name: $crate::sys::thread_local::LocalPointer = $crate::sys::thread_local::LocalPointer::__new();
152+
$crate::sys::thread_local::local_pointer! { $($rest)* }
153+
},
154+
}
155+
156+
pub(crate) struct LocalPointer {
157+
key: LazyKey,
158+
}
159+
160+
impl LocalPointer {
161+
pub const fn __new() -> LocalPointer {
162+
LocalPointer { key: LazyKey::new(None) }
163+
}
164+
165+
pub fn get(&'static self) -> *mut () {
166+
unsafe { get(self.key.force()) as *mut () }
167+
}
168+
169+
pub fn set(&'static self, p: *mut ()) {
170+
unsafe { set(self.key.force(), p as *mut u8) }
171+
}
172+
}

‎library/std/src/sys/thread_local/statik.rs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
//! On some targets like wasm there's no threads, so no need to generate
22
//! thread locals and we can instead just use plain statics!
33
4-
use crate::cell::UnsafeCell;
4+
use crate::cell::{Cell, UnsafeCell};
5+
use crate::ptr;
56

67
#[doc(hidden)]
78
#[allow_internal_unstable(thread_local_internals)]
@@ -93,3 +94,33 @@ impl<T> LazyStorage<T> {
9394

9495
// SAFETY: the target doesn't have threads.
9596
unsafe impl<T> Sync for LazyStorage<T> {}
97+
98+
#[rustc_macro_transparency = "semitransparent"]
99+
pub(crate) macro local_pointer {
100+
() => {},
101+
($vis:vis static $name:ident; $($rest:tt)*) => {
102+
$vis static $name: $crate::sys::thread_local::LocalPointer = $crate::sys::thread_local::LocalPointer::__new();
103+
$crate::sys::thread_local::local_pointer! { $($rest)* }
104+
},
105+
}
106+
107+
pub(crate) struct LocalPointer {
108+
p: Cell<*mut ()>,
109+
}
110+
111+
impl LocalPointer {
112+
pub const fn __new() -> LocalPointer {
113+
LocalPointer { p: Cell::new(ptr::null_mut()) }
114+
}
115+
116+
pub fn get(&self) -> *mut () {
117+
self.p.get()
118+
}
119+
120+
pub fn set(&self, p: *mut ()) {
121+
self.p.set(p)
122+
}
123+
}
124+
125+
// SAFETY: the target doesn't have threads.
126+
unsafe impl Sync for LocalPointer {}

‎library/std/src/thread/current.rs

Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
use super::{Thread, ThreadId};
2+
use crate::mem::ManuallyDrop;
3+
use crate::ptr;
4+
use crate::sys::thread_local::local_pointer;
5+
6+
const NONE: *mut () = ptr::null_mut();
7+
const BUSY: *mut () = ptr::without_provenance_mut(1);
8+
const DESTROYED: *mut () = ptr::without_provenance_mut(2);
9+
10+
local_pointer! {
11+
static CURRENT;
12+
}
13+
14+
/// Persistent storage for the thread ID.
15+
///
16+
/// We store the thread ID so that it never gets destroyed during the lifetime
17+
/// of a thread, either using `#[thread_local]` or multiple `local_pointer!`s.
18+
mod id {
19+
use super::*;
20+
21+
cfg_if::cfg_if! {
22+
if #[cfg(target_thread_local)] {
23+
use crate::cell::Cell;
24+
25+
#[thread_local]
26+
static ID: Cell<Option<ThreadId>> = Cell::new(None);
27+
28+
pub(super) const CHEAP: bool = true;
29+
30+
pub(super) fn get() -> Option<ThreadId> {
31+
ID.get()
32+
}
33+
34+
pub(super) fn set(id: ThreadId) {
35+
ID.set(Some(id))
36+
}
37+
} else if #[cfg(target_pointer_width = "16")] {
38+
local_pointer! {
39+
static ID0;
40+
static ID16;
41+
static ID32;
42+
static ID48;
43+
}
44+
45+
pub(super) const CHEAP: bool = false;
46+
47+
pub(super) fn get() -> Option<ThreadId> {
48+
let id0 = ID0.get().addr() as u64;
49+
let id16 = ID16.get().addr() as u64;
50+
let id32 = ID32.get().addr() as u64;
51+
let id48 = ID48.get().addr() as u64;
52+
ThreadId::from_u64((id48 << 48) + (id32 << 32) + (id16 << 16) + id0)
53+
}
54+
55+
pub(super) fn set(id: ThreadId) {
56+
let val = id.as_u64().get();
57+
ID0.set(ptr::without_provenance_mut(val as usize));
58+
ID16.set(ptr::without_provenance_mut((val >> 16) as usize));
59+
ID32.set(ptr::without_provenance_mut((val >> 32) as usize));
60+
ID48.set(ptr::without_provenance_mut((val >> 48) as usize));
61+
}
62+
} else if #[cfg(target_pointer_width = "32")] {
63+
local_pointer! {
64+
static ID0;
65+
static ID32;
66+
}
67+
68+
pub(super) const CHEAP: bool = false;
69+
70+
pub(super) fn get() -> Option<ThreadId> {
71+
let id0 = ID0.get().addr() as u64;
72+
let id32 = ID32.get().addr() as u64;
73+
ThreadId::from_u64((id32 << 32) + id0)
74+
}
75+
76+
pub(super) fn set(id: ThreadId) {
77+
let val = id.as_u64().get();
78+
ID0.set(ptr::without_provenance_mut(val as usize));
79+
ID32.set(ptr::without_provenance_mut((val >> 32) as usize));
80+
}
81+
} else {
82+
local_pointer! {
83+
static ID;
84+
}
85+
86+
pub(super) const CHEAP: bool = true;
87+
88+
pub(super) fn get() -> Option<ThreadId> {
89+
let id = ID.get().addr() as u64;
90+
ThreadId::from_u64(id)
91+
}
92+
93+
pub(super) fn set(id: ThreadId) {
94+
let val = id.as_u64().get();
95+
ID.set(ptr::without_provenance_mut(val as usize));
96+
}
97+
}
98+
}
99+
100+
#[inline]
101+
pub(super) fn get_or_init() -> ThreadId {
102+
get().unwrap_or_else(
103+
#[cold]
104+
|| {
105+
let id = ThreadId::new();
106+
id::set(id);
107+
id
108+
},
109+
)
110+
}
111+
}
112+
113+
/// Sets the thread handle for the current thread.
114+
///
115+
/// Aborts if the handle or the ID has been set already.
116+
pub(crate) fn set_current(thread: Thread) {
117+
if CURRENT.get() != NONE || id::get().is_some() {
118+
// Using `panic` here can add ~3kB to the binary size. We have complete
119+
// control over where this is called, so just abort if there is a bug.
120+
rtabort!("thread::set_current should only be called once per thread");
121+
}
122+
123+
id::set(thread.id());
124+
125+
// Make sure that `crate::rt::thread_cleanup` will be run, which will
126+
// call `drop_current`.
127+
crate::sys::thread_local::guard::enable();
128+
CURRENT.set(thread.into_raw().cast_mut());
129+
}
130+
131+
/// Gets the id of the thread that invokes it.
132+
///
133+
/// This function will always succeed, will always return the same value for
134+
/// one thread and is guaranteed not to call the global allocator.
135+
#[inline]
136+
pub(crate) fn current_id() -> ThreadId {
137+
// If accessing the persistant thread ID takes multiple TLS accesses, try
138+
// to retrieve it from the current thread handle, which will only take one
139+
// TLS access.
140+
if !id::CHEAP {
141+
let current = CURRENT.get();
142+
if current > DESTROYED {
143+
unsafe {
144+
let current = ManuallyDrop::new(Thread::from_raw(current));
145+
return current.id();
146+
}
147+
}
148+
}
149+
150+
id::get_or_init()
151+
}
152+
153+
/// Gets a handle to the thread that invokes it, if the handle has been initialized.
154+
pub(crate) fn try_current() -> Option<Thread> {
155+
let current = CURRENT.get();
156+
if current > DESTROYED {
157+
unsafe {
158+
let current = ManuallyDrop::new(Thread::from_raw(current));
159+
Some((*current).clone())
160+
}
161+
} else {
162+
None
163+
}
164+
}
165+
166+
/// Gets a handle to the thread that invokes it.
167+
///
168+
/// # Examples
169+
///
170+
/// Getting a handle to the current thread with `thread::current()`:
171+
///
172+
/// ```
173+
/// use std::thread;
174+
///
175+
/// let handler = thread::Builder::new()
176+
/// .name("named thread".into())
177+
/// .spawn(|| {
178+
/// let handle = thread::current();
179+
/// assert_eq!(handle.name(), Some("named thread"));
180+
/// })
181+
/// .unwrap();
182+
///
183+
/// handler.join().unwrap();
184+
/// ```
185+
#[must_use]
186+
#[stable(feature = "rust1", since = "1.0.0")]
187+
pub fn current() -> Thread {
188+
let current = CURRENT.get();
189+
if current > DESTROYED {
190+
unsafe {
191+
let current = ManuallyDrop::new(Thread::from_raw(current));
192+
(*current).clone()
193+
}
194+
} else {
195+
init_current(current)
196+
}
197+
}
198+
199+
#[cold]
200+
fn init_current(current: *mut ()) -> Thread {
201+
if current == NONE {
202+
CURRENT.set(BUSY);
203+
// If the thread ID was initialized already, use it.
204+
let id = id::get_or_init();
205+
let thread = Thread::new_unnamed(id);
206+
207+
// Make sure that `crate::rt::thread_cleanup` will be run, which will
208+
// call `drop_current`.
209+
crate::sys::thread_local::guard::enable();
210+
CURRENT.set(thread.clone().into_raw().cast_mut());
211+
thread
212+
} else if current == BUSY {
213+
// BUSY exists solely for this check, but as it is in the slow path, the
214+
// extra TLS write above shouldn't matter. The alternative is nearly always
215+
// a stack overflow.
216+
217+
// If you came across this message, contact the author of your allocator.
218+
// If you are said author: A surprising amount of functions inside the
219+
// standard library (e.g. `Mutex`, `thread_local!`, `File` when using long
220+
// paths, even `panic!` when using unwinding), need memory allocation, so
221+
// you'll get circular dependencies all over the place when using them.
222+
// I (joboet) highly recommend using only APIs from core in your allocator
223+
// and implementing your own system abstractions. Still, if you feel that
224+
// a particular API should be entirely allocation-free, feel free to open
225+
// an issue on the Rust repository, we'll see what we can do.
226+
rtabort!(
227+
"\n
228+
Attempted to access thread-local data while allocating said data.\n
229+
Do not access functions that allocate in the global allocator!\n
230+
This is a bug in the global allocator.\n
231+
"
232+
)
233+
} else {
234+
debug_assert_eq!(current, DESTROYED);
235+
panic!(
236+
"use of std::thread::current() is not possible after the thread's
237+
local data has been destroyed"
238+
)
239+
}
240+
}
241+
242+
/// This should be run in [`crate::rt::thread_cleanup`] to reset the thread
243+
/// handle.
244+
pub(crate) fn drop_current() {
245+
let current = CURRENT.get();
246+
if current > DESTROYED {
247+
unsafe {
248+
CURRENT.set(DESTROYED);
249+
drop(Thread::from_raw(current));
250+
}
251+
}
252+
}

‎library/std/src/thread/local/tests.rs

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

77
#[derive(Clone, Default)]
@@ -343,3 +343,34 @@ fn join_orders_after_tls_destructors() {
343343
jh2.join().unwrap();
344344
}
345345
}
346+
347+
// Test that thread::current is still available in TLS destructors.
348+
#[test]
349+
fn thread_current_in_dtor() {
350+
// Go through one round of TLS destruction first.
351+
struct Defer;
352+
impl Drop for Defer {
353+
fn drop(&mut self) {
354+
RETRIEVE.with(|_| {});
355+
}
356+
}
357+
358+
struct RetrieveName;
359+
impl Drop for RetrieveName {
360+
fn drop(&mut self) {
361+
*NAME.lock().unwrap() = Some(thread::current().name().unwrap().to_owned());
362+
}
363+
}
364+
365+
static NAME: Mutex<Option<String>> = Mutex::new(None);
366+
367+
thread_local! {
368+
static DEFER: Defer = const { Defer };
369+
static RETRIEVE: RetrieveName = const { RetrieveName };
370+
}
371+
372+
Builder::new().name("test".to_owned()).spawn(|| DEFER.with(|_| {})).unwrap().join().unwrap();
373+
let name = NAME.lock().unwrap();
374+
let name = name.as_ref().unwrap();
375+
assert_eq!(name, "test");
376+
}

‎library/std/src/thread/mod.rs

Lines changed: 35 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@
141141
//! [`Result`]: crate::result::Result
142142
//! [`Ok`]: crate::result::Result::Ok
143143
//! [`Err`]: crate::result::Result::Err
144-
//! [`thread::current`]: current
144+
//! [`thread::current`]: current::current
145145
//! [`thread::Result`]: Result
146146
//! [`unpark`]: Thread::unpark
147147
//! [`thread::park_timeout`]: park_timeout
@@ -159,7 +159,7 @@
159159
mod tests;
160160

161161
use crate::any::Any;
162-
use crate::cell::{Cell, OnceCell, UnsafeCell};
162+
use crate::cell::UnsafeCell;
163163
use crate::ffi::CStr;
164164
use crate::marker::PhantomData;
165165
use crate::mem::{self, ManuallyDrop, forget};
@@ -179,6 +179,12 @@ mod scoped;
179179
#[stable(feature = "scoped_threads", since = "1.63.0")]
180180
pub use scoped::{Scope, ScopedJoinHandle, scope};
181181

182+
mod current;
183+
184+
#[stable(feature = "rust1", since = "1.0.0")]
185+
pub use current::current;
186+
pub(crate) use current::{current_id, drop_current, set_current, try_current};
187+
182188
////////////////////////////////////////////////////////////////////////////////
183189
// Thread-local storage
184190
////////////////////////////////////////////////////////////////////////////////
@@ -471,7 +477,11 @@ impl Builder {
471477
amt
472478
});
473479

474-
let my_thread = name.map_or_else(Thread::new_unnamed, Thread::new);
480+
let id = ThreadId::new();
481+
let my_thread = match name {
482+
Some(name) => Thread::new(id, name.into()),
483+
None => Thread::new_unnamed(id),
484+
};
475485
let their_thread = my_thread.clone();
476486

477487
let my_packet: Arc<Packet<'scope, T>> = Arc::new(Packet {
@@ -509,14 +519,16 @@ impl Builder {
509519

510520
let f = MaybeDangling::new(f);
511521
let main = move || {
522+
// Immediately store the thread handle to avoid setting it or its ID
523+
// twice, which would cause an abort.
524+
set_current(their_thread.clone());
512525
if let Some(name) = their_thread.cname() {
513526
imp::Thread::set_name(name);
514527
}
515528

516529
crate::io::set_output_capture(output_capture);
517530

518531
let f = f.into_inner();
519-
set_current(their_thread);
520532
let try_result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
521533
crate::sys::backtrace::__rust_begin_short_backtrace(f)
522534
}));
@@ -690,84 +702,6 @@ where
690702
Builder::new().spawn(f).expect("failed to spawn thread")
691703
}
692704

693-
thread_local! {
694-
// Invariant: `CURRENT` and `CURRENT_ID` will always be initialized together.
695-
// If `CURRENT` is initialized, then `CURRENT_ID` will hold the same value
696-
// as `CURRENT.id()`.
697-
static CURRENT: OnceCell<Thread> = const { OnceCell::new() };
698-
static CURRENT_ID: Cell<Option<ThreadId>> = const { Cell::new(None) };
699-
}
700-
701-
/// Sets the thread handle for the current thread.
702-
///
703-
/// Aborts if the handle has been set already to reduce code size.
704-
pub(crate) fn set_current(thread: Thread) {
705-
let tid = thread.id();
706-
// Using `unwrap` here can add ~3kB to the binary size. We have complete
707-
// control over where this is called, so just abort if there is a bug.
708-
CURRENT.with(|current| match current.set(thread) {
709-
Ok(()) => CURRENT_ID.set(Some(tid)),
710-
Err(_) => rtabort!("thread::set_current should only be called once per thread"),
711-
});
712-
}
713-
714-
/// Gets a handle to the thread that invokes it.
715-
///
716-
/// In contrast to the public `current` function, this will not panic if called
717-
/// from inside a TLS destructor.
718-
pub(crate) fn try_current() -> Option<Thread> {
719-
CURRENT
720-
.try_with(|current| {
721-
current
722-
.get_or_init(|| {
723-
let thread = Thread::new_unnamed();
724-
CURRENT_ID.set(Some(thread.id()));
725-
thread
726-
})
727-
.clone()
728-
})
729-
.ok()
730-
}
731-
732-
/// Gets the id of the thread that invokes it.
733-
#[inline]
734-
pub(crate) fn current_id() -> ThreadId {
735-
CURRENT_ID.get().unwrap_or_else(|| {
736-
// If `CURRENT_ID` isn't initialized yet, then `CURRENT` must also not be initialized.
737-
// `current()` will initialize both `CURRENT` and `CURRENT_ID` so subsequent calls to
738-
// `current_id()` will succeed immediately.
739-
current().id()
740-
})
741-
}
742-
743-
/// Gets a handle to the thread that invokes it.
744-
///
745-
/// # Examples
746-
///
747-
/// Getting a handle to the current thread with `thread::current()`:
748-
///
749-
/// ```
750-
/// use std::thread;
751-
///
752-
/// let handler = thread::Builder::new()
753-
/// .name("named thread".into())
754-
/// .spawn(|| {
755-
/// let handle = thread::current();
756-
/// assert_eq!(handle.name(), Some("named thread"));
757-
/// })
758-
/// .unwrap();
759-
///
760-
/// handler.join().unwrap();
761-
/// ```
762-
#[must_use]
763-
#[stable(feature = "rust1", since = "1.0.0")]
764-
pub fn current() -> Thread {
765-
try_current().expect(
766-
"use of std::thread::current() is not possible \
767-
after the thread's local data has been destroyed",
768-
)
769-
}
770-
771705
/// Cooperatively gives up a timeslice to the OS scheduler.
772706
///
773707
/// This calls the underlying OS scheduler's yield primitive, signaling
@@ -1225,8 +1159,11 @@ pub fn park_timeout(dur: Duration) {
12251159
pub struct ThreadId(NonZero<u64>);
12261160

12271161
impl ThreadId {
1162+
// DO NOT rely on this value.
1163+
const MAIN_THREAD: ThreadId = ThreadId(unsafe { NonZero::new_unchecked(1) });
1164+
12281165
// Generate a new unique thread ID.
1229-
fn new() -> ThreadId {
1166+
pub(crate) fn new() -> ThreadId {
12301167
#[cold]
12311168
fn exhausted() -> ! {
12321169
panic!("failed to generate unique thread ID: bitspace exhausted")
@@ -1236,7 +1173,7 @@ impl ThreadId {
12361173
if #[cfg(target_has_atomic = "64")] {
12371174
use crate::sync::atomic::AtomicU64;
12381175

1239-
static COUNTER: AtomicU64 = AtomicU64::new(0);
1176+
static COUNTER: AtomicU64 = AtomicU64::new(1);
12401177

12411178
let mut last = COUNTER.load(Ordering::Relaxed);
12421179
loop {
@@ -1252,7 +1189,7 @@ impl ThreadId {
12521189
} else {
12531190
use crate::sync::{Mutex, PoisonError};
12541191

1255-
static COUNTER: Mutex<u64> = Mutex::new(0);
1192+
static COUNTER: Mutex<u64> = Mutex::new(1);
12561193

12571194
let mut counter = COUNTER.lock().unwrap_or_else(PoisonError::into_inner);
12581195
let Some(id) = counter.checked_add(1) else {
@@ -1269,6 +1206,11 @@ impl ThreadId {
12691206
}
12701207
}
12711208

1209+
#[cfg(not(target_thread_local))]
1210+
fn from_u64(v: u64) -> Option<ThreadId> {
1211+
NonZero::new(v).map(ThreadId)
1212+
}
1213+
12721214
/// This returns a numeric identifier for the thread identified by this
12731215
/// `ThreadId`.
12741216
///
@@ -1369,27 +1311,27 @@ impl Inner {
13691311
/// should instead use a function like `spawn` to create new threads, see the
13701312
/// docs of [`Builder`] and [`spawn`] for more details.
13711313
///
1372-
/// [`thread::current`]: current
1314+
/// [`thread::current`]: current::current
13731315
pub struct Thread {
13741316
inner: Pin<Arc<Inner>>,
13751317
}
13761318

13771319
impl Thread {
13781320
/// Used only internally to construct a thread object without spawning.
1379-
pub(crate) fn new(name: String) -> Thread {
1380-
Self::new_inner(ThreadName::Other(name.into()))
1321+
pub(crate) fn new(id: ThreadId, name: String) -> Thread {
1322+
Self::new_inner(id, ThreadName::Other(name.into()))
13811323
}
13821324

1383-
pub(crate) fn new_unnamed() -> Thread {
1384-
Self::new_inner(ThreadName::Unnamed)
1325+
pub(crate) fn new_unnamed(id: ThreadId) -> Thread {
1326+
Self::new_inner(id, ThreadName::Unnamed)
13851327
}
13861328

13871329
// Used in runtime to construct main thread
13881330
pub(crate) fn new_main() -> Thread {
1389-
Self::new_inner(ThreadName::Main)
1331+
Self::new_inner(ThreadId::MAIN_THREAD, ThreadName::Main)
13901332
}
13911333

1392-
fn new_inner(name: ThreadName) -> Thread {
1334+
fn new_inner(id: ThreadId, name: ThreadName) -> Thread {
13931335
// We have to use `unsafe` here to construct the `Parker` in-place,
13941336
// which is required for the UNIX implementation.
13951337
//
@@ -1399,7 +1341,7 @@ impl Thread {
13991341
let mut arc = Arc::<Inner>::new_uninit();
14001342
let ptr = Arc::get_mut_unchecked(&mut arc).as_mut_ptr();
14011343
(&raw mut (*ptr).name).write(name);
1402-
(&raw mut (*ptr).id).write(ThreadId::new());
1344+
(&raw mut (*ptr).id).write(id);
14031345
Parker::new_in_place(&raw mut (*ptr).parker);
14041346
Pin::new_unchecked(arc.assume_init())
14051347
};

‎src/tools/miri/tests/fail/tail_calls/cc-mismatch.stderr

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ LL | extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
1515
= note: inside `std::panicking::r#try::<i32, &dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe>` at RUSTLIB/std/src/panicking.rs:LL:CC
1616
= note: inside `std::panic::catch_unwind::<&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe, i32>` at RUSTLIB/std/src/panic.rs:LL:CC
1717
= note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC
18-
= note: inside `std::panicking::r#try::do_call::<{closure@std::rt::lang_start_internal::{closure#2}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC
19-
= note: inside `std::panicking::r#try::<isize, {closure@std::rt::lang_start_internal::{closure#2}}>` at RUSTLIB/std/src/panicking.rs:LL:CC
20-
= note: inside `std::panic::catch_unwind::<{closure@std::rt::lang_start_internal::{closure#2}}, isize>` at RUSTLIB/std/src/panic.rs:LL:CC
18+
= note: inside `std::panicking::r#try::do_call::<{closure@std::rt::lang_start_internal::{closure#1}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC
19+
= note: inside `std::panicking::r#try::<isize, {closure@std::rt::lang_start_internal::{closure#1}}>` at RUSTLIB/std/src/panicking.rs:LL:CC
20+
= note: inside `std::panic::catch_unwind::<{closure@std::rt::lang_start_internal::{closure#1}}, isize>` at RUSTLIB/std/src/panic.rs:LL:CC
2121
= note: inside `std::rt::lang_start_internal` at RUSTLIB/std/src/rt.rs:LL:CC
2222
= note: inside `std::rt::lang_start::<()>` at RUSTLIB/std/src/rt.rs:LL:CC
2323

‎src/tools/miri/tests/pass/backtrace/backtrace-api-v0.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ RUSTLIB/core/src/ops/function.rs:LL:CC (std::ops::function::impls::call_once)
1010
RUSTLIB/std/src/panicking.rs:LL:CC (std::panicking::r#try::do_call)
1111
RUSTLIB/std/src/panicking.rs:LL:CC (std::panicking::r#try)
1212
RUSTLIB/std/src/panic.rs:LL:CC (std::panic::catch_unwind)
13-
RUSTLIB/std/src/rt.rs:LL:CC (std::rt::lang_start_internal::{closure#2})
13+
RUSTLIB/std/src/rt.rs:LL:CC (std::rt::lang_start_internal::{closure#1})
1414
RUSTLIB/std/src/panicking.rs:LL:CC (std::panicking::r#try::do_call)
1515
RUSTLIB/std/src/panicking.rs:LL:CC (std::panicking::r#try)
1616
RUSTLIB/std/src/panic.rs:LL:CC (std::panic::catch_unwind)

‎src/tools/miri/tests/pass/backtrace/backtrace-api-v1.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ RUSTLIB/core/src/ops/function.rs:LL:CC (std::ops::function::impls::call_once)
1010
RUSTLIB/std/src/panicking.rs:LL:CC (std::panicking::r#try::do_call)
1111
RUSTLIB/std/src/panicking.rs:LL:CC (std::panicking::r#try)
1212
RUSTLIB/std/src/panic.rs:LL:CC (std::panic::catch_unwind)
13-
RUSTLIB/std/src/rt.rs:LL:CC (std::rt::lang_start_internal::{closure#2})
13+
RUSTLIB/std/src/rt.rs:LL:CC (std::rt::lang_start_internal::{closure#1})
1414
RUSTLIB/std/src/panicking.rs:LL:CC (std::panicking::r#try::do_call)
1515
RUSTLIB/std/src/panicking.rs:LL:CC (std::panicking::r#try)
1616
RUSTLIB/std/src/panic.rs:LL:CC (std::panic::catch_unwind)

‎src/tools/miri/tests/pass/backtrace/backtrace-global-alloc.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
at RUSTLIB/std/src/panicking.rs:LL:CC
1515
7: std::panic::catch_unwind
1616
at RUSTLIB/std/src/panic.rs:LL:CC
17-
8: std::rt::lang_start_internal::{closure#2}
17+
8: std::rt::lang_start_internal::{closure#1}
1818
at RUSTLIB/std/src/rt.rs:LL:CC
1919
9: std::panicking::r#try::do_call
2020
at RUSTLIB/std/src/panicking.rs:LL:CC

‎src/tools/miri/tests/pass/backtrace/backtrace-std.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
at RUSTLIB/std/src/panicking.rs:LL:CC
2323
11: std::panic::catch_unwind
2424
at RUSTLIB/std/src/panic.rs:LL:CC
25-
12: std::rt::lang_start_internal::{closure#2}
25+
12: std::rt::lang_start_internal::{closure#1}
2626
at RUSTLIB/std/src/rt.rs:LL:CC
2727
13: std::panicking::r#try::do_call
2828
at RUSTLIB/std/src/panicking.rs:LL:CC

0 commit comments

Comments
 (0)
This repository has been archived.