diff --git a/Alc/ALc.c b/Alc/ALc.c index c4492c96..66ce8579 100644 --- a/Alc/ALc.c +++ b/Alc/ALc.c @@ -786,34 +786,22 @@ static void alc_init(void); static void alc_deinit(void); static void alc_deinit_safe(void); -UIntMap TlsDestructor; +extern UIntMap TlsDestructors; #ifndef AL_LIBTYPE_STATIC -BOOL APIENTRY DllMain(HINSTANCE hModule,DWORD ul_reason_for_call,LPVOID lpReserved) +BOOL APIENTRY DllMain(HINSTANCE hModule, DWORD reason, LPVOID lpReserved) { - ALsizei i; - - // Perform actions based on the reason for calling. - switch(ul_reason_for_call) + switch(reason) { case DLL_PROCESS_ATTACH: /* Pin the DLL so we won't get unloaded until the process terminates */ GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (WCHAR*)hModule, &hModule); - InitUIntMap(&TlsDestructor, ~0); + InitUIntMap(&TlsDestructors, ~0); alc_init(); break; case DLL_THREAD_DETACH: - LockUIntMapRead(&TlsDestructor); - for(i = 0;i < TlsDestructor.size;i++) - { - void *ptr = altss_get(TlsDestructor.array[i].key); - altss_dtor_t callback = (altss_dtor_t)TlsDestructor.array[i].value; - if(ptr && callback) - callback(ptr); - } - UnlockUIntMapRead(&TlsDestructor); break; case DLL_PROCESS_DETACH: @@ -821,7 +809,7 @@ BOOL APIENTRY DllMain(HINSTANCE hModule,DWORD ul_reason_for_call,LPVOID lpReserv alc_deinit(); else alc_deinit_safe(); - ResetUIntMap(&TlsDestructor); + ResetUIntMap(&TlsDestructors); break; } return TRUE; diff --git a/Alc/threads.c b/Alc/threads.c index e5df3086..ca004903 100644 --- a/Alc/threads.c +++ b/Alc/threads.c @@ -198,6 +198,42 @@ int almtx_timedlock(almtx_t *mtx, const struct timespec *ts) } +/* An associative map of uint:void* pairs. The key is the TLS index (given by + * TlsAlloc), and the value is the altss_dtor_t callback. When a thread exits, + * it iterates over the thread-local value for each TLS key and calls the + * destructor function if they're both not NULL. Placing a PIMAGE_TLS_CALLBACK + * function pointer in a ".CRT$XLx" section (where x is a character A to Z) + * ensures the CRT will call it similar to DllMain. + */ +UIntMap TlsDestructors; + +static void NTAPI altss_callback(void* UNUSED(handle), DWORD reason, void* UNUSED(reserved)) +{ + ALsizei i; + + if(reason != DLL_THREAD_DETACH) + return; + + LockUIntMapRead(&TlsDestructors); + for(i = 0;i < TlsDestructors.size;i++) + { + void *ptr = altss_get(TlsDestructors.array[i].key); + altss_dtor_t callback = (altss_dtor_t)TlsDestructors.array[i].value; + if(ptr && callback) + callback(ptr); + } + UnlockUIntMapRead(&TlsDestructors); +} +#ifdef _MSC_VER +#pragma section(".CRT$XLB",read) +__declspec(allocate(".CRT$XLB")) PIMAGE_TLS_CALLBACK altss_callback_ = altss_callback; +#elif defined(__GNUC__) +PIMAGE_TLS_CALLBACK altss_callback_ __attribute__((section(".CRT$XLB"))) = altss_callback; +#else +#warning "No TLS callback support, thread-local contexts may leak references on poorly written applications." +PIMAGE_TLS_CALLBACK altss_callback_ = altss_callback; +#endif + int altss_create(altss_t *tss_id, altss_dtor_t callback) { DWORD key = TlsAlloc(); @@ -206,13 +242,13 @@ int altss_create(altss_t *tss_id, altss_dtor_t callback) *tss_id = key; if(callback != NULL) - InsertUIntMapEntry(&TlsDestructor, key, callback); + InsertUIntMapEntry(&TlsDestructors, key, callback); return althrd_success; } void altss_delete(altss_t tss_id) { - InsertUIntMapEntry(&TlsDestructor, tss_id, NULL); + RemoveUIntMapKey(&TlsDestructors, tss_id); TlsFree(tss_id); } diff --git a/Alc/uintmap.h b/Alc/uintmap.h index 2c70f161..6fc1190e 100644 --- a/Alc/uintmap.h +++ b/Alc/uintmap.h @@ -14,7 +14,6 @@ typedef struct UIntMap { ALsizei limit; RWLock lock; } UIntMap; -extern UIntMap TlsDestructor; void InitUIntMap(UIntMap *map, ALsizei limit); void ResetUIntMap(UIntMap *map);