11cfg_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( ) ; }
0 commit comments