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