Deadlock in cygwin1.dll when joining a thread before thread_local initialization

kikairoya kikairoya@gmail.com
Wed Nov 5 14:00:43 GMT 2025


Thank you for the investigating.
I believe I understand what happens in the runtime.

I found that a similar problem has been reported to UCRT runtime, and the 
reporter says that Darwin libc and glibc does unlock the mutex while 
calling atexit-ed functions.

The report: 
https://developercommunity.visualstudio.com/t/atexit-deadlock-with-thread-safe-static-/1654756

glibc: 
https://sourceware.org/git/?p=glibc.git;a=blob;f=stdlib/cxa_finalize.c;hb=2642002380aafb71a1d3b569b6d7ebeab3284816#l96

Darwin: 
https://github.com/apple-open-source-mirror/Libc/blob/5e566be7a7047360adfb35ffc44c6a019a854bea/stdlib/FreeBSD/atexit.c#L274-L292


I'm also feel scared to do that, but it might be practically safe.

 > Thanks for the report. I looked into the issue and found the
 > cause is in newlib. I have confirmed that the following patch
 > fixes the issue, but I am not very sure unlocking mutex here
 > is safe. Let me consider a bit more.
 >
 >
 > diff --git a/newlib/libc/stdlib/__call_atexit.c 
b/newlib/libc/stdlib/__call_atexit.c
 > index 710440389..44f1f6acc 100644
 > --- a/newlib/libc/stdlib/__call_atexit.c
 > +++ b/newlib/libc/stdlib/__call_atexit.c
 > @@ -114,6 +114,11 @@ __call_exitprocs (int code, void *d)
 >
 >           ind = p->_ind;
 >
 > +#ifndef __SINGLE_THREAD__
 > +         /* Unlock __atexit_recursive_mutex; otherwise, the function 
fn() may
 > +            deadlock if it waits for another thread which calls 
atexit(). */
 > +         __lock_release_recursive(__atexit_recursive_mutex);
 > +#endif
 >           /* Call the function.  */
 >           if (!args || (args->_fntypes & i) == 0)
 >             fn ();
 > @@ -121,6 +126,9 @@ __call_exitprocs (int code, void *d)
 >             (*((void (*)(int, void *)) fn))(code, args->_fnargs[n]);
 >           else
 >             (*((void (*)(void *)) fn))(args->_fnargs[n]);
 > +#ifndef __SINGLE_THREAD__
 > +         __lock_acquire_recursive(__atexit_recursive_mutex);
 > +#endif
 >
 >           /* The function we called call atexit and registered another
 >              function (or functions).  Call these new functions before


--
Tomohiro Kashiwada (@kikairoya)


More information about the Cygwin mailing list