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ö <mar...@martin.st>
---
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 0000000..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 <sect_attribs.h>
+
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+
+#include <assert.h>
+#include <stdio.h>
+#include <corecrt_startup.h>
+
+
+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(&lock);
+  handler->next = global_dtors;
+  global_dtors = handler;
+  LeaveCriticalSection(&lock);
+  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(&lock);
+      __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(&tls_dtors);
+    run_dtor_list(&global_dtors);
+    if (inited == 1) {
+      inited = 0;
+      DeleteCriticalSection(&lock);
+    }
+    break;
+  case DLL_THREAD_ATTACH:
+    break;
+  case DLL_THREAD_DETACH:
+    run_dtor_list(&tls_dtors);
+    break;
+  }
+}
+
+_CRTALLOC(".CRT$XLE") PIMAGE_TLS_CALLBACK __xl_e = (PIMAGE_TLS_CALLBACK) 
tls_callback;
-- 
2.7.4


------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
Mingw-w64-public mailing list
Mingw-w64-public@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/mingw-w64-public

Reply via email to