xref: /netbsd-src/external/gpl3/gcc/dist/libstdc++-v3/libsupc++/atexit_thread.cc (revision b1e838363e3c6fc78a55519254d99869742dd33c)
1*b1e83836Smrg // Copyright (C) 2012-2022 Free Software Foundation, Inc.
248fb7bfaSmrg //
348fb7bfaSmrg // This file is part of GCC.
448fb7bfaSmrg //
548fb7bfaSmrg // GCC is free software; you can redistribute it and/or modify
648fb7bfaSmrg // it under the terms of the GNU General Public License as published by
748fb7bfaSmrg // the Free Software Foundation; either version 3, or (at your option)
848fb7bfaSmrg // any later version.
948fb7bfaSmrg 
1048fb7bfaSmrg // GCC is distributed in the hope that it will be useful,
1148fb7bfaSmrg // but WITHOUT ANY WARRANTY; without even the implied warranty of
1248fb7bfaSmrg // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1348fb7bfaSmrg // GNU General Public License for more details.
1448fb7bfaSmrg 
1548fb7bfaSmrg // Under Section 7 of GPL version 3, you are granted additional
1648fb7bfaSmrg // permissions described in the GCC Runtime Library Exception, version
1748fb7bfaSmrg // 3.1, as published by the Free Software Foundation.
1848fb7bfaSmrg 
1948fb7bfaSmrg // You should have received a copy of the GNU General Public License and
2048fb7bfaSmrg // a copy of the GCC Runtime Library Exception along with this program;
2148fb7bfaSmrg // see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
2248fb7bfaSmrg // <http://www.gnu.org/licenses/>.
2348fb7bfaSmrg 
2448fb7bfaSmrg #include <cxxabi.h>
2548fb7bfaSmrg #include <cstdlib>
2648fb7bfaSmrg #include <new>
2748fb7bfaSmrg #include "bits/gthr.h"
284d5abbe8Smrg #ifdef _GLIBCXX_THREAD_ATEXIT_WIN32
294d5abbe8Smrg #define WIN32_LEAN_AND_MEAN
304d5abbe8Smrg #include <windows.h>
314d5abbe8Smrg #endif
3248fb7bfaSmrg 
33*b1e83836Smrg // Simplify it a little for this file.
34*b1e83836Smrg #ifndef _GLIBCXX_CDTOR_CALLABI
35*b1e83836Smrg #  define _GLIBCXX_CDTOR_CALLABI
36*b1e83836Smrg #endif
37*b1e83836Smrg 
38b17d1066Smrg #if _GLIBCXX_HAVE___CXA_THREAD_ATEXIT
39b17d1066Smrg 
40b17d1066Smrg // Libc provides __cxa_thread_atexit definition.
41b17d1066Smrg 
42b17d1066Smrg #elif _GLIBCXX_HAVE___CXA_THREAD_ATEXIT_IMPL
4348fb7bfaSmrg 
44*b1e83836Smrg extern "C" int __cxa_thread_atexit_impl (void (_GLIBCXX_CDTOR_CALLABI *func) (void *),
4548fb7bfaSmrg 					 void *arg, void *d);
4648fb7bfaSmrg extern "C" int
__cxa_thread_atexit(void (_GLIBCXX_CDTOR_CALLABI * dtor)(void *),void * obj,void * dso_handle)47*b1e83836Smrg __cxxabiv1::__cxa_thread_atexit (void (_GLIBCXX_CDTOR_CALLABI *dtor)(void *),
4848fb7bfaSmrg 				 void *obj, void *dso_handle)
4948fb7bfaSmrg   _GLIBCXX_NOTHROW
5048fb7bfaSmrg {
5148fb7bfaSmrg   return __cxa_thread_atexit_impl (dtor, obj, dso_handle);
5248fb7bfaSmrg }
5348fb7bfaSmrg 
54cd5ea10dSmrg #else /* _GLIBCXX_HAVE___CXA_THREAD_ATEXIT_IMPL */
5548fb7bfaSmrg 
5648fb7bfaSmrg namespace {
5748fb7bfaSmrg   // One element in a singly-linked stack of cleanups.
5848fb7bfaSmrg   struct elt
5948fb7bfaSmrg   {
60*b1e83836Smrg     void (_GLIBCXX_CDTOR_CALLABI *destructor)(void *);
6148fb7bfaSmrg     void *object;
6248fb7bfaSmrg     elt *next;
634d5abbe8Smrg #ifdef _GLIBCXX_THREAD_ATEXIT_WIN32
644d5abbe8Smrg     HMODULE dll;
654d5abbe8Smrg #endif
6648fb7bfaSmrg   };
6748fb7bfaSmrg 
6848fb7bfaSmrg   // Keep a per-thread list of cleanups in gthread_key storage.
6948fb7bfaSmrg   __gthread_key_t key;
7048fb7bfaSmrg   // But also support non-threaded mode.
7148fb7bfaSmrg   elt *single_thread;
7248fb7bfaSmrg 
7348fb7bfaSmrg   // Run the specified stack of cleanups.
run(void * p)7448fb7bfaSmrg   void run (void *p)
7548fb7bfaSmrg   {
7648fb7bfaSmrg     elt *e = static_cast<elt*>(p);
7748fb7bfaSmrg     while (e)
7848fb7bfaSmrg       {
7948fb7bfaSmrg 	elt *old_e = e;
8048fb7bfaSmrg 	e->destructor (e->object);
814d5abbe8Smrg #ifdef _GLIBCXX_THREAD_ATEXIT_WIN32
824d5abbe8Smrg 	/* Decrement DLL count */
834d5abbe8Smrg 	if (e->dll)
844d5abbe8Smrg 	  FreeLibrary (e->dll);
854d5abbe8Smrg #endif
8648fb7bfaSmrg 	e = e->next;
8748fb7bfaSmrg 	delete (old_e);
8848fb7bfaSmrg       }
8948fb7bfaSmrg   }
9048fb7bfaSmrg 
9148fb7bfaSmrg   // Run the stack of cleanups for the current thread.
run()9248fb7bfaSmrg   void run ()
9348fb7bfaSmrg   {
9448fb7bfaSmrg     void *e;
9548fb7bfaSmrg     if (__gthread_active_p ())
9648fb7bfaSmrg       {
9748fb7bfaSmrg 	e = __gthread_getspecific (key);
9848fb7bfaSmrg 	__gthread_setspecific (key, NULL);
9948fb7bfaSmrg       }
10048fb7bfaSmrg     else
10148fb7bfaSmrg       {
10248fb7bfaSmrg 	e = single_thread;
10348fb7bfaSmrg 	single_thread = NULL;
10448fb7bfaSmrg       }
10548fb7bfaSmrg     run (e);
10648fb7bfaSmrg   }
10748fb7bfaSmrg 
10848fb7bfaSmrg   // Initialize the key for the cleanup stack.  We use a static local for
10948fb7bfaSmrg   // key init/delete rather than atexit so that delete is run on dlclose.
key_init()11048fb7bfaSmrg   void key_init() {
11148fb7bfaSmrg     struct key_s {
11248fb7bfaSmrg       key_s() { __gthread_key_create (&key, run); }
11348fb7bfaSmrg       ~key_s() { __gthread_key_delete (key); }
11448fb7bfaSmrg     };
11548fb7bfaSmrg     static key_s ks;
11648fb7bfaSmrg     // Also make sure the destructors are run by std::exit.
11748fb7bfaSmrg     // FIXME TLS cleanups should run before static cleanups and atexit
11848fb7bfaSmrg     // cleanups.
11948fb7bfaSmrg     std::atexit (run);
12048fb7bfaSmrg   }
12148fb7bfaSmrg }
12248fb7bfaSmrg 
12348fb7bfaSmrg extern "C" int
__cxa_thread_atexit(void (_GLIBCXX_CDTOR_CALLABI * dtor)(void *),void * obj,void *)124*b1e83836Smrg __cxxabiv1::__cxa_thread_atexit (void (_GLIBCXX_CDTOR_CALLABI *dtor)(void *),
125*b1e83836Smrg 				 void *obj, void */*dso_handle*/)
12648fb7bfaSmrg   _GLIBCXX_NOTHROW
12748fb7bfaSmrg {
12848fb7bfaSmrg   // Do this initialization once.
12948fb7bfaSmrg   if (__gthread_active_p ())
13048fb7bfaSmrg     {
13148fb7bfaSmrg       // When threads are active use __gthread_once.
13248fb7bfaSmrg       static __gthread_once_t once = __GTHREAD_ONCE_INIT;
13348fb7bfaSmrg       __gthread_once (&once, key_init);
13448fb7bfaSmrg     }
13548fb7bfaSmrg   else
13648fb7bfaSmrg     {
13748fb7bfaSmrg       // And when threads aren't active use a static local guard.
13848fb7bfaSmrg       static bool queued;
13948fb7bfaSmrg       if (!queued)
14048fb7bfaSmrg 	{
14148fb7bfaSmrg 	  queued = true;
14248fb7bfaSmrg 	  std::atexit (run);
14348fb7bfaSmrg 	}
14448fb7bfaSmrg     }
14548fb7bfaSmrg 
14648fb7bfaSmrg   elt *first;
14748fb7bfaSmrg   if (__gthread_active_p ())
14848fb7bfaSmrg     first = static_cast<elt*>(__gthread_getspecific (key));
14948fb7bfaSmrg   else
15048fb7bfaSmrg     first = single_thread;
15148fb7bfaSmrg 
15248fb7bfaSmrg   elt *new_elt = new (std::nothrow) elt;
15348fb7bfaSmrg   if (!new_elt)
15448fb7bfaSmrg     return -1;
15548fb7bfaSmrg   new_elt->destructor = dtor;
15648fb7bfaSmrg   new_elt->object = obj;
15748fb7bfaSmrg   new_elt->next = first;
1584d5abbe8Smrg #ifdef _GLIBCXX_THREAD_ATEXIT_WIN32
1594d5abbe8Smrg   /* Store the DLL address for a later call to FreeLibrary in new_elt and
1604d5abbe8Smrg      increment DLL load count.  This blocks the unloading of the DLL
1614d5abbe8Smrg      before the thread-local dtors have been called.  This does NOT help
1624d5abbe8Smrg      if FreeLibrary/dlclose is called in excess. */
1634d5abbe8Smrg   GetModuleHandleExW (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
1644d5abbe8Smrg 		      (LPCWSTR) dtor, &new_elt->dll);
1654d5abbe8Smrg #endif
16648fb7bfaSmrg 
16748fb7bfaSmrg   if (__gthread_active_p ())
16848fb7bfaSmrg     __gthread_setspecific (key, new_elt);
16948fb7bfaSmrg   else
17048fb7bfaSmrg     single_thread = new_elt;
17148fb7bfaSmrg 
17248fb7bfaSmrg   return 0;
17348fb7bfaSmrg }
17448fb7bfaSmrg 
175cd5ea10dSmrg #endif /* _GLIBCXX_HAVE___CXA_THREAD_ATEXIT_IMPL */
176