| // Copyright (C) 2012-2021 Free Software Foundation, Inc. |
| // |
| // This file is part of GCC. |
| // |
| // GCC is free software; you can redistribute it and/or modify |
| // it under the terms of the GNU General Public License as published by |
| // the Free Software Foundation; either version 3, or (at your option) |
| // any later version. |
| |
| // GCC is distributed in the hope that it will be useful, |
| // but WITHOUT ANY WARRANTY; without even the implied warranty of |
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| // GNU General Public License for more details. |
| |
| // Under Section 7 of GPL version 3, you are granted additional |
| // permissions described in the GCC Runtime Library Exception, version |
| // 3.1, as published by the Free Software Foundation. |
| |
| // You should have received a copy of the GNU General Public License and |
| // a copy of the GCC Runtime Library Exception along with this program; |
| // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see |
| // <http://www.gnu.org/licenses/>. |
| |
| #include <cxxabi.h> |
| #include <cstdlib> |
| #include <new> |
| #include "bits/gthr.h" |
| #ifdef _GLIBCXX_THREAD_ATEXIT_WIN32 |
| #define WIN32_LEAN_AND_MEAN |
| #include <windows.h> |
| #endif |
| |
| // Simplify it a little for this file. |
| #ifndef _GLIBCXX_CDTOR_CALLABI |
| # define _GLIBCXX_CDTOR_CALLABI |
| #endif |
| |
| #if _GLIBCXX_HAVE___CXA_THREAD_ATEXIT |
| |
| // Libc provides __cxa_thread_atexit definition. |
| |
| #elif _GLIBCXX_HAVE___CXA_THREAD_ATEXIT_IMPL |
| |
| extern "C" int __cxa_thread_atexit_impl (void (_GLIBCXX_CDTOR_CALLABI *func) (void *), |
| void *arg, void *d); |
| extern "C" int |
| __cxxabiv1::__cxa_thread_atexit (void (_GLIBCXX_CDTOR_CALLABI *dtor)(void *), |
| void *obj, void *dso_handle) |
| _GLIBCXX_NOTHROW |
| { |
| return __cxa_thread_atexit_impl (dtor, obj, dso_handle); |
| } |
| |
| #else /* _GLIBCXX_HAVE___CXA_THREAD_ATEXIT_IMPL */ |
| |
| namespace { |
| // One element in a singly-linked stack of cleanups. |
| struct elt |
| { |
| void (_GLIBCXX_CDTOR_CALLABI *destructor)(void *); |
| void *object; |
| elt *next; |
| #ifdef _GLIBCXX_THREAD_ATEXIT_WIN32 |
| HMODULE dll; |
| #endif |
| }; |
| |
| // Keep a per-thread list of cleanups in gthread_key storage. |
| __gthread_key_t key; |
| // But also support non-threaded mode. |
| elt *single_thread; |
| |
| // Run the specified stack of cleanups. |
| void run (void *p) |
| { |
| elt *e = static_cast<elt*>(p); |
| while (e) |
| { |
| elt *old_e = e; |
| e->destructor (e->object); |
| #ifdef _GLIBCXX_THREAD_ATEXIT_WIN32 |
| /* Decrement DLL count */ |
| if (e->dll) |
| FreeLibrary (e->dll); |
| #endif |
| e = e->next; |
| delete (old_e); |
| } |
| } |
| |
| // Run the stack of cleanups for the current thread. |
| void run () |
| { |
| void *e; |
| if (__gthread_active_p ()) |
| { |
| e = __gthread_getspecific (key); |
| __gthread_setspecific (key, NULL); |
| } |
| else |
| { |
| e = single_thread; |
| single_thread = NULL; |
| } |
| run (e); |
| } |
| |
| // Initialize the key for the cleanup stack. We use a static local for |
| // key init/delete rather than atexit so that delete is run on dlclose. |
| void key_init() { |
| struct key_s { |
| key_s() { __gthread_key_create (&key, run); } |
| ~key_s() { __gthread_key_delete (key); } |
| }; |
| static key_s ks; |
| // Also make sure the destructors are run by std::exit. |
| // FIXME TLS cleanups should run before static cleanups and atexit |
| // cleanups. |
| std::atexit (run); |
| } |
| } |
| |
| extern "C" int |
| __cxxabiv1::__cxa_thread_atexit (void (_GLIBCXX_CDTOR_CALLABI *dtor)(void *), |
| void *obj, void */*dso_handle*/) |
| _GLIBCXX_NOTHROW |
| { |
| // Do this initialization once. |
| if (__gthread_active_p ()) |
| { |
| // When threads are active use __gthread_once. |
| static __gthread_once_t once = __GTHREAD_ONCE_INIT; |
| __gthread_once (&once, key_init); |
| } |
| else |
| { |
| // And when threads aren't active use a static local guard. |
| static bool queued; |
| if (!queued) |
| { |
| queued = true; |
| std::atexit (run); |
| } |
| } |
| |
| elt *first; |
| if (__gthread_active_p ()) |
| first = static_cast<elt*>(__gthread_getspecific (key)); |
| else |
| first = single_thread; |
| |
| elt *new_elt = new (std::nothrow) elt; |
| if (!new_elt) |
| return -1; |
| new_elt->destructor = dtor; |
| new_elt->object = obj; |
| new_elt->next = first; |
| #ifdef _GLIBCXX_THREAD_ATEXIT_WIN32 |
| /* Store the DLL address for a later call to FreeLibrary in new_elt and |
| increment DLL load count. This blocks the unloading of the DLL |
| before the thread-local dtors have been called. This does NOT help |
| if FreeLibrary/dlclose is called in excess. */ |
| GetModuleHandleExW (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, |
| (LPCWSTR) dtor, &new_elt->dll); |
| #endif |
| |
| if (__gthread_active_p ()) |
| __gthread_setspecific (key, new_elt); |
| else |
| single_thread = new_elt; |
| |
| return 0; |
| } |
| |
| #endif /* _GLIBCXX_HAVE___CXA_THREAD_ATEXIT_IMPL */ |