On 18/11/2025 20:02, Jeremy Drake via Cygwin wrote:
On Tue, 18 Nov 2025, Takashi Yano via Cygwin wrote:

Hi Jeremy,

On Mon, 17 Nov 2025 11:16:41 -0800 (PST)
Jeremy Drake wrote:
On Sat, 8 Nov 2025, Takashi Yano via Cygwin wrote:

I looked into the problem, and found that the executable for
the following code registers two pthread_keys with each destructor;
one is void emutls_destroy(void *ptr) in libgcc/emutls.c, and the
other is void run(void *p) in libstdc++-v3/libsupc++/atexit_thread.cc.
emutls_destroy() free's the memory erea of static thread_local X,
that is accessed from X::~X() which is called from run(). As a result,
if the emutls_destroy() is called before run(), run() referres to
the memory erea already free'ed.

I think this is a bug of gcc. This issue does not occur in Linux,
because Linux does not use emutls.

There is a similar longstanding issue in mingw-w64.  The problem there is
that the pthread_key destructors run before the native Windows TLS
callbacks.  emutls still uses pthread_key to manage static thread_locals,
but C++ destructors are called from the Windows TLS callbacks (by way of
__cxa_thread_atexit if memory serves).
Thanks for the information. When I compile my reproducer with mingw
compiler, the issue does not seem to happen. How does mingw handle
this issue?
I remember working on this a while back, and adjusting the order that
destructors are called to try to make it as correct as I could, but this
last scenario was not fixable in the existing model.  LIU Hao actually
made a new thread model for Win32/GCC largely to get all the destructors
to run in the standards-compliant order.  Perhaps he can shed some light
on what is supposed to happen here from the C/C++ standard side?

Cygwin should have it comparatively easy: it controls all the pieces (it
doesn't need to care about when Windows TLS callbacks happen because if
somebody calls ExitThread they get the undefined behavior they deserve).
Couldn't Cygwin simply provide its own __cxa_thread_atexit and ensure
destructors registered there run before pthread_key destructors?
It is not difficult to add a workaround for this issue in cygwin side.
However, IIRC, BSD libc does the same with cygwin 3.7.0-dev. I don't
think it is good idea to add workaround to cygwin for a bug of apps
on cygwin.

Regardless, is it really undefined in what order pthread_key destructors
run?  I would expect they'd run in reverse order of registration (most
recently registered first).  Wouldn't that prevent this issue too
(without mucking about with the Itanium C++ ABI)?
https://pubs.opengroup.org/onlinepubs/9799919799/ says:
"The order of destructor calls is unspecified if more than one destructor
  exists for a thread when it exits."

As you expected, the reverse-order'ed destructor-call hides the issue.
(That is what 3.6.5 does.)
This sounds like pthread_key destructors are not fit for purpose for
running C++ destructors then, unless possibly used to register a single
"meta-destructor" that runs the destructors in the proper order...  I
think Cygwin would be better served with a different __cxa_thread_atexit
implementation since the order of destructor calls is significant to the
C++ standard.  Then it would be a matter of running those *before* the
pthread_key destructors.

Implementation hint:

That cygwin is available only as a DLL provides an additional way to run
implementcleanup on orderly thread exit:  One of the last things that
happen in an orderly thread exit (ExitThread, returning from the thread
function etc.) is that Win32 entersa special start/stop global critical
section and then calls DllMain in cygwin1.dll with theDETACH_THREAD
value in the context of the exiting thread.  This provides a last chance
for the Cygwin runtime to do cleanups while the TLS slots of the thread
still exist.  Officially, some/many Win32 APIs are not allowed in that
critical section, mostly actions related to creating threads and/or
loading DLLs.  The historic Win16 WEP() entrypoint has similar but
stricter restrictions in what it could do during final cleanup.


Enjoy

Jakob
--
Jakob Bohm, CIO, Partner, WiseMo A/S.  https://www.wisemo.com
Transformervej 29, 2860 Søborg, Denmark.  Direct +45 31 13 16 10
This public discussion message is non-binding and may contain errors.
WiseMo - Remote Service Management for PCs, Phones and Embedded


--
Problem reports:      https://cygwin.com/problems.html
FAQ:                  https://cygwin.com/faq/
Documentation:        https://cygwin.com/docs.html
Unsubscribe info:     https://cygwin.com/ml/#unsubscribe-simple

Reply via email to