In order to properly call destructors of a DLL when that DLL is
unloaded, we need to provide these as part of the static library
libmingw32.a, which gets linked into the module itself.
C++ libraries (both libstdc++ and libcxxabi) can also provide the
function __cxa_thread_atexit, and defer the actual handling to the
platform via a __cxa_thread_atexit_impl function. However, if linking
a C++ standard library dynamically, that would mean that all destructors
are registered in the C++ DLL and only invoked when that one is unloaded,
not when an individual DLL gets unloaded.
Therefore we need to provide these functions directly in a static library
that gets linked into each module (if referenced).
This probably doesn't end up used with GCC and libstdc++ (which still
probably provides its own __cxa_thread_atexit which will be linked before
libmingw32.a). But it does work with clang (if compiling with
-fuse-cxa-atexit) and libcxxabi (which doesn't provide any
__cxa_thread_atexit when targeting mingw).
Signed-off-by: Martin Storsjö
---
Removed code for cleaning up other threads' pending destructors, added
a verbose comment about the matter.
---
mingw-w64-crt/Makefile.am | 2 +-
mingw-w64-crt/crt/cxa_atexit.c | 113 +
2 files changed, 114 insertions(+), 1 deletion(-)
create mode 100644 mingw-w64-crt/crt/cxa_atexit.c
diff --git a/mingw-w64-crt/Makefile.am b/mingw-w64-crt/Makefile.am
index 8ca1072..d98fa3a 100644
--- a/mingw-w64-crt/Makefile.am
+++ b/mingw-w64-crt/Makefile.am
@@ -119,7 +119,7 @@ src_libmingw32=include/oscalls.h include/internal.h
include/sect_attribs.h \
crt/mingw_custom.c crt/mingw_helpers.c \
crt/pseudo-reloc.c crt/udll_argv.c \
crt/xtxtmode.c crt/crt_handler.c\
- crt/tlsthrd.c crt/tlsmthread.c crt/tlsmcrt.c
+ crt/tlsthrd.c crt/tlsmthread.c crt/tlsmcrt.c crt/cxa_atexit.c
src_libscrnsave=libsrc/scrnsave.c
src_libscrnsavw=libsrc/scrnsave.c
diff --git a/mingw-w64-crt/crt/cxa_atexit.c b/mingw-w64-crt/crt/cxa_atexit.c
new file mode 100644
index 000..d74968d
--- /dev/null
+++ b/mingw-w64-crt/crt/cxa_atexit.c
@@ -0,0 +1,113 @@
+/**
+ * This file has no copyright assigned and is placed in the Public Domain.
+ * This file is part of the mingw-w64 runtime package.
+ * No warranty is given; refer to the file DISCLAIMER.PD within this package.
+ */
+
+#include
+
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include
+
+#include
+#include
+#include
+
+
+typedef void (__thiscall * dtor)(void*);
+int __cxa_atexit(dtor dtor, void *obj, void *dso);
+int __cxa_thread_atexit(dtor dtor, void *obj, void *dso);
+
+typedef struct dtor_obj dtor_obj;
+struct dtor_obj {
+ dtor dtor;
+ void *obj;
+ dtor_obj *next;
+};
+
+HANDLE __dso_handle;
+
+static CRITICAL_SECTION lock;
+static int inited = 0;
+static dtor_obj *global_dtors = NULL;
+static __thread dtor_obj *tls_dtors = NULL;
+
+int __cxa_atexit(dtor dtor, void *obj, void *dso) {
+ if (!inited)
+return 1;
+ assert(!dso || dso == &__dso_handle);
+ dtor_obj *handler = (dtor_obj *) calloc(1, sizeof(*handler));
+ if (!handler)
+return 1;
+ handler->dtor = dtor;
+ handler->obj = obj;
+ EnterCriticalSection();
+ handler->next = global_dtors;
+ global_dtors = handler;
+ LeaveCriticalSection();
+ return 0;
+}
+
+static void run_dtor_list(dtor_obj **ptr) {
+ dtor_obj *list = *ptr;
+ while (list) {
+list->dtor(list->obj);
+dtor_obj *next = list->next;
+free(list);
+list = next;
+ }
+ *ptr = NULL;
+}
+
+int __cxa_thread_atexit(dtor dtor, void *obj, void *dso) {
+ if (!inited)
+return 1;
+ assert(!dso || dso == &__dso_handle);
+ dtor_obj *handler = (dtor_obj *) calloc(1, sizeof(*handler));
+ if (!handler)
+return 1;
+ handler->dtor = dtor;
+ handler->obj = obj;
+ handler->next = tls_dtors;
+ tls_dtors = handler;
+ return 0;
+}
+
+static void WINAPI tls_callback(HANDLE hDllHandle, DWORD dwReason, LPVOID
__UNUSED_PARAM(lpReserved)) {
+ switch (dwReason) {
+ case DLL_PROCESS_ATTACH:
+if (inited == 0) {
+ InitializeCriticalSection();
+ __dso_handle = hDllHandle;
+}
+inited = 1;
+break;
+ case DLL_PROCESS_DETACH:
+// If there are other threads still running that haven't been detached,
+// we don't attempt to run their destructors (MSVC doesn't either), but
+// simply leak the destructor list and whatever resources the destructors
+// would have released.
+// From Vista onwards, we could have used FlsAlloc to get a TLS key that
+// runs a destructor on each thread that has a value attached ot it, but
+// since MSVC doesn't run destructors on other threads in this case,
+// users shouldn't assume it and we don't attempt to do anything
potentially
+// risky about it. TL;DR, threads with pending TLS destructors for a DLL
+// need to be joined before unloading the DLL.
+run_dtor_list(_dtors);
+run_dtor_list(_dtors);
+