Skip to content

Commit 8ffdb00

Browse files
authored
Rollup merge of #139206 - joboet:unique_thread_errno, r=ibraheemdev
std: use the address of `errno` to identify threads in `unique_thread_exit` Getting the address of `errno` should be just as cheap as `pthread_self()` and avoids having to use the expensive `Mutex` logic because it always results in a pointer.
2 parents c3f500e + ff37c7d commit 8ffdb00

File tree

2 files changed

+21
-24
lines changed

2 files changed

+21
-24
lines changed

library/std/src/sys/exit_guard.rs

+20-23
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,5 @@
11
cfg_if::cfg_if! {
22
if #[cfg(target_os = "linux")] {
3-
/// pthread_t is a pointer on some platforms,
4-
/// so we wrap it in this to impl Send + Sync.
5-
#[derive(Clone, Copy)]
6-
#[repr(transparent)]
7-
struct PThread(libc::pthread_t);
8-
// Safety: pthread_t is safe to send between threads
9-
unsafe impl Send for PThread {}
10-
// Safety: pthread_t is safe to share between threads
11-
unsafe impl Sync for PThread {}
123
/// Mitigation for <https://github.com/rust-lang/rust/issues/126600>
134
///
145
/// On glibc, `libc::exit` has been observed to not always be thread-safe.
@@ -30,28 +21,34 @@ cfg_if::cfg_if! {
3021
/// (waiting for the process to exit).
3122
#[cfg_attr(any(test, doctest), allow(dead_code))]
3223
pub(crate) fn unique_thread_exit() {
33-
let this_thread_id = unsafe { libc::pthread_self() };
34-
use crate::sync::{Mutex, PoisonError};
35-
static EXITING_THREAD_ID: Mutex<Option<PThread>> = Mutex::new(None);
36-
let mut exiting_thread_id =
37-
EXITING_THREAD_ID.lock().unwrap_or_else(PoisonError::into_inner);
38-
match *exiting_thread_id {
39-
None => {
24+
use crate::ffi::c_int;
25+
use crate::ptr;
26+
use crate::sync::atomic::AtomicPtr;
27+
use crate::sync::atomic::Ordering::{Acquire, Relaxed};
28+
29+
static EXITING_THREAD_ID: AtomicPtr<c_int> = AtomicPtr::new(ptr::null_mut());
30+
31+
// We use the address of `errno` as a cheap and safe way to identify
32+
// threads. As the C standard mandates that `errno` must have thread
33+
// storage duration, we can rely on its address not changing over the
34+
// lifetime of the thread. Additionally, accesses to `errno` are
35+
// async-signal-safe, so this function is available in all imaginable
36+
// circumstances.
37+
let this_thread_id = crate::sys::os::errno_location();
38+
match EXITING_THREAD_ID.compare_exchange(ptr::null_mut(), this_thread_id, Acquire, Relaxed) {
39+
Ok(_) => {
4040
// This is the first thread to call `unique_thread_exit`,
41-
// and this is the first time it is called.
42-
// Set EXITING_THREAD_ID to this thread's ID and return.
43-
*exiting_thread_id = Some(PThread(this_thread_id));
44-
},
45-
Some(exiting_thread_id) if exiting_thread_id.0 == this_thread_id => {
41+
// and this is the first time it is called. Continue exiting.
42+
}
43+
Err(exiting_thread_id) if exiting_thread_id == this_thread_id => {
4644
// This is the first thread to call `unique_thread_exit`,
4745
// but this is the second time it is called.
4846
// Abort the process.
4947
core::panicking::panic_nounwind("std::process::exit called re-entrantly")
5048
}
51-
Some(_) => {
49+
Err(_) => {
5250
// This is not the first thread to call `unique_thread_exit`.
5351
// Pause until the process exits.
54-
drop(exiting_thread_id);
5552
loop {
5653
// Safety: libc::pause is safe to call.
5754
unsafe { libc::pause(); }

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ unsafe extern "C" {
5656
#[cfg_attr(target_os = "aix", link_name = "_Errno")]
5757
// SAFETY: this will always return the same pointer on a given thread.
5858
#[unsafe(ffi_const)]
59-
fn errno_location() -> *mut c_int;
59+
pub safe fn errno_location() -> *mut c_int;
6060
}
6161

6262
/// Returns the platform-specific value of errno

0 commit comments

Comments
 (0)