mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			170 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			170 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			C++
		
	
	
	
| // Copyright (C) 2012-2017 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
 | |
| 
 | |
| #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 (*func) (void *),
 | |
| 					 void *arg, void *d);
 | |
| extern "C" int
 | |
| __cxxabiv1::__cxa_thread_atexit (void (*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 (*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 (*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 */
 |