diff --git a/library/std/src/sys/exit_guard.rs b/library/std/src/sys/exit_guard.rs index 5a090f506661d..94107b8ff51da 100644 --- a/library/std/src/sys/exit_guard.rs +++ b/library/std/src/sys/exit_guard.rs @@ -1,14 +1,8 @@ cfg_if::cfg_if! { if #[cfg(target_os = "linux")] { - /// pthread_t is a pointer on some platforms, - /// so we wrap it in this to impl Send + Sync. - #[derive(Clone, Copy)] - #[repr(transparent)] - struct PThread(libc::pthread_t); - // Safety: pthread_t is safe to send between threads - unsafe impl Send for PThread {} - // Safety: pthread_t is safe to share between threads - unsafe impl Sync for PThread {} + use crate::mem; + use crate::sync::atomic::{AtomicUsize, Ordering}; + /// Mitigation for /// /// On glibc, `libc::exit` has been observed to not always be thread-safe. @@ -31,27 +25,29 @@ cfg_if::cfg_if! { #[cfg_attr(any(test, doctest), allow(dead_code))] pub(crate) fn unique_thread_exit() { let this_thread_id = unsafe { libc::pthread_self() }; - use crate::sync::{Mutex, PoisonError}; - static EXITING_THREAD_ID: Mutex> = Mutex::new(None); - let mut exiting_thread_id = - EXITING_THREAD_ID.lock().unwrap_or_else(PoisonError::into_inner); - match *exiting_thread_id { - None => { + + const _: () = assert!(mem::size_of::() <= mem::size_of::()); + + const NONE: usize = 0; + static EXITING_THREAD_ID: AtomicUsize = AtomicUsize::new(NONE); + match EXITING_THREAD_ID.compare_exchange(NONE, this_thread_id as usize, Ordering::Relaxed, Ordering::Relaxed).unwrap_or_else(|id| id) { + NONE => { // This is the first thread to call `unique_thread_exit`, // and this is the first time it is called. - // Set EXITING_THREAD_ID to this thread's ID and return. - *exiting_thread_id = Some(PThread(this_thread_id)); - }, - Some(exiting_thread_id) if exiting_thread_id.0 == this_thread_id => { + // + // We set `EXITING_THREAD_ID` to this thread's ID already + // and will return. + } + id if id == this_thread_id as usize => { // This is the first thread to call `unique_thread_exit`, // but this is the second time it is called. + // // Abort the process. core::panicking::panic_nounwind("std::process::exit called re-entrantly") } - Some(_) => { + _ => { // This is not the first thread to call `unique_thread_exit`. // Pause until the process exits. - drop(exiting_thread_id); loop { // Safety: libc::pause is safe to call. unsafe { libc::pause(); }