207 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			207 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			C
		
	
	
	
/*
 | 
						|
 * mthr.c
 | 
						|
 *
 | 
						|
 * Implement Mingw thread-support DLL .
 | 
						|
 *
 | 
						|
 * This file is used iff the following conditions are met:
 | 
						|
 *  - gcc uses -mthreads option 
 | 
						|
 *  - user code uses C++ exceptions
 | 
						|
 *
 | 
						|
 * The sole job of the Mingw thread support DLL (MingwThr) is to catch 
 | 
						|
 * all the dying threads and clean up the data allocated in the TLSs 
 | 
						|
 * for exception contexts during C++ EH. Posix threads have key dtors, 
 | 
						|
 * but win32 TLS keys do not, hence the magic. Without this, there's at 
 | 
						|
 * least `6 * sizeof (void*)' bytes leaks for each catch/throw in each
 | 
						|
 * thread. The only public interface is __mingwthr_key_dtor(). 
 | 
						|
 *
 | 
						|
 * Created by Mumit Khan  <khan@nanotech.wisc.edu>
 | 
						|
 *
 | 
						|
 */
 | 
						|
 | 
						|
#define WIN32_LEAN_AND_MEAN
 | 
						|
#include <windows.h>
 | 
						|
#undef WIN32_LEAN_AND_MEAN
 | 
						|
#include <stdlib.h>
 | 
						|
 | 
						|
/* To protect the thread/key association data structure modifications. */
 | 
						|
CRITICAL_SECTION __mingwthr_cs;
 | 
						|
 | 
						|
typedef struct __mingwthr_thread __mingwthr_thread_t;
 | 
						|
typedef struct __mingwthr_key __mingwthr_key_t;
 | 
						|
 | 
						|
/* The list of threads active with key/dtor pairs. */
 | 
						|
struct __mingwthr_key {
 | 
						|
  DWORD key;
 | 
						|
  void (*dtor) (void *);
 | 
						|
  __mingwthr_key_t *next;
 | 
						|
};
 | 
						|
 | 
						|
/* The list of key/dtor pairs for a particular thread. */
 | 
						|
struct __mingwthr_thread {
 | 
						|
  DWORD thread_id;
 | 
						|
  __mingwthr_key_t *keys;
 | 
						|
  __mingwthr_thread_t *next;
 | 
						|
};
 | 
						|
 | 
						|
static __mingwthr_thread_t *__mingwthr_thread_list;
 | 
						|
 | 
						|
/*
 | 
						|
 * __mingwthr_key_add:
 | 
						|
 *
 | 
						|
 * Add key/dtor association for this thread. If the thread entry does not
 | 
						|
 * exist, create a new one and add to the head of the threads list; add
 | 
						|
 * the new assoc at the head of the keys list. 
 | 
						|
 *
 | 
						|
 */
 | 
						|
 | 
						|
static int
 | 
						|
__mingwthr_add_key_dtor (DWORD thread_id, DWORD key, void (*dtor) (void *))
 | 
						|
{
 | 
						|
  __mingwthr_thread_t *threadp;
 | 
						|
  __mingwthr_key_t *new_key;
 | 
						|
 | 
						|
  new_key = (__mingwthr_key_t *) calloc (1, sizeof (__mingwthr_key_t));
 | 
						|
  if (new_key == NULL)
 | 
						|
    return -1;
 | 
						|
  
 | 
						|
  new_key->key = key;
 | 
						|
  new_key->dtor = dtor;
 | 
						|
 | 
						|
  /* This may be called by multiple threads, and so we need to protect
 | 
						|
     the whole process of adding the key/dtor pair.  */ 
 | 
						|
  EnterCriticalSection (&__mingwthr_cs);
 | 
						|
 | 
						|
  for (threadp = __mingwthr_thread_list; 
 | 
						|
       threadp && (threadp->thread_id != thread_id); 
 | 
						|
       threadp = threadp->next)
 | 
						|
    ;
 | 
						|
  
 | 
						|
  if (threadp == NULL)
 | 
						|
    {
 | 
						|
      threadp = (__mingwthr_thread_t *) 
 | 
						|
        calloc (1, sizeof (__mingwthr_thread_t));
 | 
						|
      if (threadp == NULL)
 | 
						|
        {
 | 
						|
	  free (new_key);
 | 
						|
	  LeaveCriticalSection (&__mingwthr_cs);
 | 
						|
	  return -1;
 | 
						|
	}
 | 
						|
      threadp->thread_id = thread_id;
 | 
						|
      threadp->next = __mingwthr_thread_list;
 | 
						|
      __mingwthr_thread_list = threadp;
 | 
						|
    }
 | 
						|
 | 
						|
  new_key->next = threadp->keys;
 | 
						|
  threadp->keys = new_key;
 | 
						|
 | 
						|
  LeaveCriticalSection (&__mingwthr_cs);
 | 
						|
 | 
						|
#ifdef DEBUG
 | 
						|
  printf ("%s: allocating: (%ld, %ld, %x)\n", 
 | 
						|
          __FUNCTION__, thread_id, key, dtor);
 | 
						|
#endif
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * __mingwthr_run_key_dtors (DWORD thread_id):
 | 
						|
 *
 | 
						|
 * Callback from DllMain when thread detaches to clean up the key
 | 
						|
 * storage. 
 | 
						|
 *
 | 
						|
 * Note that this does not delete the key itself, but just runs
 | 
						|
 * the dtor if the current value are both non-NULL. Note that the
 | 
						|
 * keys with NULL dtors are not added by __mingwthr_key_dtor, the
 | 
						|
 * only public interface, so we don't need to check. 
 | 
						|
 *
 | 
						|
 */
 | 
						|
 | 
						|
void
 | 
						|
__mingwthr_run_key_dtors (DWORD thread_id)
 | 
						|
{
 | 
						|
  __mingwthr_thread_t *prev_threadp, *threadp;
 | 
						|
  __mingwthr_key_t *keyp;
 | 
						|
 | 
						|
#ifdef DEBUG
 | 
						|
  printf ("%s: Entering Thread id %ld\n", __FUNCTION__, thread_id);
 | 
						|
#endif
 | 
						|
 | 
						|
  /* Since this is called just once per thread, we only need to protect 
 | 
						|
     the part where we take out this thread's entry and reconfigure the 
 | 
						|
     list instead of wrapping the whole process in a critical section. */
 | 
						|
  EnterCriticalSection (&__mingwthr_cs);
 | 
						|
 | 
						|
  prev_threadp = NULL;
 | 
						|
  for (threadp = __mingwthr_thread_list; 
 | 
						|
       threadp && (threadp->thread_id != thread_id); 
 | 
						|
       prev_threadp = threadp, threadp = threadp->next)
 | 
						|
    ;
 | 
						|
  
 | 
						|
  if (threadp == NULL)
 | 
						|
    {
 | 
						|
      LeaveCriticalSection (&__mingwthr_cs);
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
  /* take the damned thread out of the chain. */
 | 
						|
  if (prev_threadp == NULL)		/* first entry hit. */
 | 
						|
    __mingwthr_thread_list = threadp->next;
 | 
						|
  else
 | 
						|
    prev_threadp->next = threadp->next;
 | 
						|
 | 
						|
  LeaveCriticalSection (&__mingwthr_cs);
 | 
						|
 | 
						|
  for (keyp = threadp->keys; keyp; )
 | 
						|
    {
 | 
						|
      __mingwthr_key_t *prev_keyp;
 | 
						|
      LPVOID value = TlsGetValue (keyp->key);
 | 
						|
      if (GetLastError () == ERROR_SUCCESS)
 | 
						|
	{
 | 
						|
#ifdef DEBUG
 | 
						|
	  printf ("   (%ld, %x)\n", keyp->key, keyp->dtor);
 | 
						|
#endif
 | 
						|
	  if (value)
 | 
						|
	    (*keyp->dtor) (value);
 | 
						|
	}
 | 
						|
#ifdef DEBUG
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  printf ("   TlsGetValue FAILED  (%ld, %x)\n", 
 | 
						|
		  keyp->key, keyp->dtor);
 | 
						|
	}
 | 
						|
#endif
 | 
						|
      prev_keyp = keyp;
 | 
						|
      keyp = keyp->next;
 | 
						|
      free (prev_keyp);
 | 
						|
    }
 | 
						|
  
 | 
						|
  free (threadp);
 | 
						|
 | 
						|
#ifdef DEBUG
 | 
						|
  printf ("%s: Exiting Thread id %ld\n", __FUNCTION__, thread_id);
 | 
						|
#endif
 | 
						|
}
 | 
						|
  
 | 
						|
/*
 | 
						|
 * __mingwthr_register_key_dtor (DWORD key, void (*dtor) (void *))
 | 
						|
 *
 | 
						|
 * Public interface called by C++ exception handling mechanism in
 | 
						|
 * libgcc (cf: __gthread_key_create).
 | 
						|
 *
 | 
						|
 */
 | 
						|
 | 
						|
__declspec(dllexport)
 | 
						|
int
 | 
						|
__mingwthr_key_dtor (DWORD key, void (*dtor) (void *))
 | 
						|
{
 | 
						|
  if (dtor)
 | 
						|
    {
 | 
						|
      DWORD thread_id = GetCurrentThreadId ();
 | 
						|
      return __mingwthr_add_key_dtor (thread_id, key, dtor);
 | 
						|
    }
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 |