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;
|
||
|
}
|
||
|
|