Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit c8db4c4

Browse files
committedJul 20, 2024
std: make thread::current available in all thread_local! destructors
1 parent 1afc5fd commit c8db4c4

File tree

16 files changed

+541
-144
lines changed

16 files changed

+541
-144
lines changed
 

‎library/std/src/rt.rs

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,11 @@
1919
pub use crate::panicking::{begin_panic, panic_count};
2020
pub use core::panicking::{panic_display, panic_fmt};
2121

22+
use crate::any::Any;
2223
use crate::sync::Once;
2324
use crate::sys;
2425
use crate::thread::{self, Thread};
26+
use crate::{mem, panic};
2527

2628
// Prints to the "panic output", depending on the platform this may be:
2729
// - the standard error output
@@ -64,6 +66,11 @@ macro_rules! rtunwrap {
6466
};
6567
}
6668

69+
fn handle_rt_panic(e: Box<dyn Any + Send>) {
70+
mem::forget(e);
71+
rtabort!("initialization or cleanup bug");
72+
}
73+
6774
// One-time runtime initialization.
6875
// Runs before `main`.
6976
// SAFETY: must be called only once during runtime initialization.
@@ -99,6 +106,20 @@ unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) {
99106
thread::set_current(thread);
100107
}
101108

109+
/// Clean up the thread-local runtime state. This *should* be run after all other
110+
/// code managed by the Rust runtime, but will not cause UB if that condition is
111+
/// not fulfilled. Also note that this function is not guaranteed to be run, but
112+
/// skipping it will cause leaks and therefore is to be avoided.
113+
pub(crate) fn thread_cleanup() {
114+
// This function is run in situations where unwinding leads to an abort
115+
// (think `extern "C"` functions). Abort here instead so that we can
116+
// print a nice message.
117+
panic::catch_unwind(|| {
118+
crate::thread::drop_current();
119+
})
120+
.unwrap_or_else(handle_rt_panic);
121+
}
122+
102123
// One-time runtime cleanup.
103124
// Runs after `main` or at program exit.
104125
// NOTE: this is not guaranteed to run, for example when the program aborts.
@@ -121,11 +142,6 @@ fn lang_start_internal(
121142
argv: *const *const u8,
122143
sigpipe: u8,
123144
) -> Result<isize, !> {
124-
use crate::{mem, panic};
125-
let rt_abort = move |e| {
126-
mem::forget(e);
127-
rtabort!("initialization or cleanup bug");
128-
};
129145
// Guard against the code called by this function from unwinding outside of the Rust-controlled
130146
// code, which is UB. This is a requirement imposed by a combination of how the
131147
// `#[lang="start"]` attribute is implemented as well as by the implementation of the panicking
@@ -137,16 +153,17 @@ fn lang_start_internal(
137153
// prevent std from accidentally introducing a panic to these functions. Another is from
138154
// user code from `main` or, more nefariously, as described in e.g. issue #86030.
139155
// SAFETY: Only called once during runtime initialization.
140-
panic::catch_unwind(move || unsafe { init(argc, argv, sigpipe) }).map_err(rt_abort)?;
156+
panic::catch_unwind(move || unsafe { init(argc, argv, sigpipe) })
157+
.unwrap_or_else(handle_rt_panic);
141158
let ret_code = panic::catch_unwind(move || panic::catch_unwind(main).unwrap_or(101) as isize)
142159
.map_err(move |e| {
143160
mem::forget(e);
144161
rtabort!("drop of the panic payload panicked");
145162
});
146-
panic::catch_unwind(cleanup).map_err(rt_abort)?;
163+
panic::catch_unwind(cleanup).unwrap_or_else(handle_rt_panic);
147164
// Guard against multple threads calling `libc::exit` concurrently.
148165
// See the documentation for `unique_thread_exit` for more information.
149-
panic::catch_unwind(|| crate::sys::exit_guard::unique_thread_exit()).map_err(rt_abort)?;
166+
panic::catch_unwind(crate::sys::exit_guard::unique_thread_exit).unwrap_or_else(handle_rt_panic);
150167
ret_code
151168
}
152169

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ pub unsafe extern "C" fn runtime_entry(
104104
let result = main(argc as isize, argv);
105105

106106
crate::sys::thread_local::destructors::run();
107+
crate::rt::thread_cleanup();
108+
107109
hermit_abi::exit(result);
108110
}
109111

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

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

5151
// run all destructors
5252
crate::sys::thread_local::destructors::run();
53+
crate::rt::thread_cleanup();
5354
}
5455
}
5556
}

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ use crate::sync::atomic::{
115115
AtomicBool, AtomicPtr,
116116
Ordering::{AcqRel, Acquire, Relaxed, Release},
117117
};
118-
use crate::thread::{self, Thread};
118+
use crate::thread::{self, Thread, ThreadId};
119119

120120
// Locking uses exponential backoff. `SPIN_COUNT` indicates how many times the
121121
// locking operation will be retried.
@@ -202,7 +202,9 @@ impl Node {
202202
fn prepare(&mut self) {
203203
// Fall back to creating an unnamed `Thread` handle to allow locking in
204204
// TLS destructors.
205-
self.thread.get_or_init(|| thread::try_current().unwrap_or_else(Thread::new_unnamed));
205+
self.thread.get_or_init(|| {
206+
thread::try_current().unwrap_or_else(|| Thread::new_unnamed(ThreadId::new()))
207+
});
206208
self.completed = AtomicBool::new(false);
207209
}
208210

‎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::{set, LazyKey};
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
@@ -18,6 +18,9 @@ pub fn enable() {
1818
}
1919

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

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,13 +92,13 @@ unsafe extern "system" fn tls_callback(_h: *mut c_void, dw_reason: u32, _pv: *mu
9292
}
9393

9494
if dw_reason == c::DLL_THREAD_DETACH || dw_reason == c::DLL_PROCESS_DETACH {
95-
#[cfg(target_thread_local)]
9695
unsafe {
96+
#[cfg(target_thread_local)]
9797
super::super::destructors::run();
98-
}
99-
#[cfg(not(target_thread_local))]
100-
unsafe {
98+
#[cfg(not(target_thread_local))]
10199
super::super::key::run_dtors();
100+
101+
crate::rt::thread_cleanup();
102102
}
103103
}
104104
}

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

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

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

Lines changed: 30 additions & 16 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,47 @@ 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)]
101+
#[cfg(not(all(target_family = "wasm", not(target_feature = "atomics"))))]
95102
use super::destructors::run;
103+
#[allow(unused)]
104+
use crate::rt::thread_cleanup;
96105
}
97-
} else if #[cfg(target_os = "hermit")] {
98-
pub(super) fn enable() {}
106+
} else if #[cfg(any(
107+
target_os = "hermit",
108+
target_os = "xous",
109+
))] {
110+
// `std` is the only runtime, so it just calls the destructor functions
111+
// itself when the time comes.
112+
pub(crate) fn enable() {}
99113
} else if #[cfg(target_os = "solid_asp3")] {
100114
mod solid;
101-
pub(super) use solid::enable;
102-
} else if #[cfg(all(target_thread_local, not(target_family = "wasm")))] {
115+
pub(crate) use solid::enable;
116+
} else {
103117
mod key;
104-
pub(super) use key::enable;
118+
pub(crate) use key::enable;
105119
}
106120
}
107121
}

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

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@
3232
mod eager;
3333
mod lazy;
3434

35+
use crate::cell::Cell;
36+
use crate::ptr;
37+
3538
pub use eager::Storage as EagerStorage;
3639
pub use lazy::Storage as LazyStorage;
3740

@@ -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 & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
use super::abort_on_dtor_unwind;
2+
use super::guard;
3+
use super::key::{get, set, Key, LazyKey};
24
use crate::cell::Cell;
35
use crate::marker::PhantomData;
46
use crate::ptr;
5-
use crate::sys::thread_local::key::{get, set, Key, LazyKey};
67

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

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

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

Lines changed: 51 additions & 109 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::env;
164164
use crate::ffi::CStr;
165165
use crate::fmt;
@@ -185,29 +185,27 @@ mod scoped;
185185
#[stable(feature = "scoped_threads", since = "1.63.0")]
186186
pub use scoped::{scope, Scope, ScopedJoinHandle};
187187

188+
mod current;
189+
190+
#[stable(feature = "rust1", since = "1.0.0")]
191+
pub use current::current;
192+
pub(crate) use current::{current_id, drop_current, set_current, try_current};
193+
188194
////////////////////////////////////////////////////////////////////////////////
189195
// Thread-local storage
190196
////////////////////////////////////////////////////////////////////////////////
191197

192198
#[macro_use]
193199
mod local;
194200

195-
cfg_if::cfg_if! {
196-
if #[cfg(test)] {
197-
// Avoid duplicating the global state associated with thread-locals between this crate and
198-
// realstd. Miri relies on this.
199-
pub use realstd::thread::{local_impl, AccessError, LocalKey};
200-
} else {
201-
#[stable(feature = "rust1", since = "1.0.0")]
202-
pub use self::local::{AccessError, LocalKey};
203-
204-
// Implementation details used by the thread_local!{} macro.
205-
#[doc(hidden)]
206-
#[unstable(feature = "thread_local_internals", issue = "none")]
207-
pub mod local_impl {
208-
pub use crate::sys::thread_local::*;
209-
}
210-
}
201+
#[stable(feature = "rust1", since = "1.0.0")]
202+
pub use self::local::{AccessError, LocalKey};
203+
204+
// Implementation details used by the thread_local!{} macro.
205+
#[doc(hidden)]
206+
#[unstable(feature = "thread_local_internals", issue = "none")]
207+
pub mod local_impl {
208+
pub use crate::sys::thread_local::*;
211209
}
212210

213211
////////////////////////////////////////////////////////////////////////////////
@@ -487,7 +485,11 @@ impl Builder {
487485
amt
488486
});
489487

490-
let my_thread = name.map_or_else(Thread::new_unnamed, Thread::new);
488+
let id = ThreadId::new();
489+
let my_thread = match name {
490+
Some(name) => Thread::new(id, name.into()),
491+
None => Thread::new_unnamed(id),
492+
};
491493
let their_thread = my_thread.clone();
492494

493495
let my_packet: Arc<Packet<'scope, T>> = Arc::new(Packet {
@@ -526,14 +528,16 @@ impl Builder {
526528

527529
let f = MaybeDangling::new(f);
528530
let main = move || {
531+
// Immediately store the thread handle to avoid setting it or its ID
532+
// twice, which would cause an abort.
533+
set_current(their_thread.clone());
529534
if let Some(name) = their_thread.cname() {
530535
imp::Thread::set_name(name);
531536
}
532537

533538
crate::io::set_output_capture(output_capture);
534539

535540
let f = f.into_inner();
536-
set_current(their_thread);
537541
let try_result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
538542
crate::sys::backtrace::__rust_begin_short_backtrace(f)
539543
}));
@@ -694,84 +698,6 @@ where
694698
Builder::new().spawn(f).expect("failed to spawn thread")
695699
}
696700

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

12311157
impl ThreadId {
1158+
// DO NOT rely on this value.
1159+
const MAIN_THREAD: ThreadId = ThreadId(unsafe { NonZero::new_unchecked(1) });
1160+
12321161
// Generate a new unique thread ID.
1233-
fn new() -> ThreadId {
1162+
pub(crate) fn new() -> ThreadId {
12341163
#[cold]
12351164
fn exhausted() -> ! {
12361165
panic!("failed to generate unique thread ID: bitspace exhausted")
@@ -1240,7 +1169,7 @@ impl ThreadId {
12401169
if #[cfg(target_has_atomic = "64")] {
12411170
use crate::sync::atomic::AtomicU64;
12421171

1243-
static COUNTER: AtomicU64 = AtomicU64::new(0);
1172+
static COUNTER: AtomicU64 = AtomicU64::new(1);
12441173

12451174
let mut last = COUNTER.load(Ordering::Relaxed);
12461175
loop {
@@ -1256,7 +1185,7 @@ impl ThreadId {
12561185
} else {
12571186
use crate::sync::{Mutex, PoisonError};
12581187

1259-
static COUNTER: Mutex<u64> = Mutex::new(0);
1188+
static COUNTER: Mutex<u64> = Mutex::new(1);
12601189

12611190
let mut counter = COUNTER.lock().unwrap_or_else(PoisonError::into_inner);
12621191
let Some(id) = counter.checked_add(1) else {
@@ -1273,6 +1202,11 @@ impl ThreadId {
12731202
}
12741203
}
12751204

1205+
#[cfg(not(target_thread_local))]
1206+
fn from_u64(v: u64) -> Option<ThreadId> {
1207+
NonZero::new(v).map(ThreadId)
1208+
}
1209+
12761210
/// This returns a numeric identifier for the thread identified by this
12771211
/// `ThreadId`.
12781212
///
@@ -1372,27 +1306,27 @@ impl Inner {
13721306
/// should instead use a function like `spawn` to create new threads, see the
13731307
/// docs of [`Builder`] and [`spawn`] for more details.
13741308
///
1375-
/// [`thread::current`]: current
1309+
/// [`thread::current`]: current::current
13761310
pub struct Thread {
13771311
inner: Pin<Arc<Inner>>,
13781312
}
13791313

13801314
impl Thread {
13811315
/// Used only internally to construct a thread object without spawning.
1382-
pub(crate) fn new(name: String) -> Thread {
1383-
Self::new_inner(ThreadName::Other(name.into()))
1316+
pub(crate) fn new(id: ThreadId, name: String) -> Thread {
1317+
Self::new_inner(id, ThreadName::Other(name.into()))
13841318
}
13851319

1386-
pub(crate) fn new_unnamed() -> Thread {
1387-
Self::new_inner(ThreadName::Unnamed)
1320+
pub(crate) fn new_unnamed(id: ThreadId) -> Thread {
1321+
Self::new_inner(id, ThreadName::Unnamed)
13881322
}
13891323

13901324
// Used in runtime to construct main thread
13911325
pub(crate) fn new_main() -> Thread {
1392-
Self::new_inner(ThreadName::Main)
1326+
Self::new_inner(ThreadId::MAIN_THREAD, ThreadName::Main)
13931327
}
13941328

1395-
fn new_inner(name: ThreadName) -> Thread {
1329+
fn new_inner(id: ThreadId, name: ThreadName) -> Thread {
13961330
// We have to use `unsafe` here to construct the `Parker` in-place,
13971331
// which is required for the UNIX implementation.
13981332
//
@@ -1402,14 +1336,22 @@ impl Thread {
14021336
let mut arc = Arc::<Inner>::new_uninit();
14031337
let ptr = Arc::get_mut_unchecked(&mut arc).as_mut_ptr();
14041338
addr_of_mut!((*ptr).name).write(name);
1405-
addr_of_mut!((*ptr).id).write(ThreadId::new());
1339+
addr_of_mut!((*ptr).id).write(id);
14061340
Parker::new_in_place(addr_of_mut!((*ptr).parker));
14071341
Pin::new_unchecked(arc.assume_init())
14081342
};
14091343

14101344
Thread { inner }
14111345
}
14121346

1347+
fn into_raw(self) -> *mut () {
1348+
unsafe { Arc::into_raw(Pin::into_inner_unchecked(self.inner)) as *mut () }
1349+
}
1350+
1351+
unsafe fn from_raw(ptr: *mut ()) -> Thread {
1352+
unsafe { Thread { inner: Pin::new_unchecked(Arc::from_raw(ptr as *const Inner)) } }
1353+
}
1354+
14131355
/// Like the public [`park`], but callable on any handle. This is used to
14141356
/// allow parking in TLS destructors.
14151357
///

0 commit comments

Comments
 (0)
Please sign in to comment.