diff options
Diffstat (limited to 'src/gallium/state_trackers/wgl/stw_tls.c')
-rw-r--r-- | src/gallium/state_trackers/wgl/stw_tls.c | 218 |
1 files changed, 192 insertions, 26 deletions
diff --git a/src/gallium/state_trackers/wgl/stw_tls.c b/src/gallium/state_trackers/wgl/stw_tls.c index 4bd6a9289c9..c0da930b214 100644 --- a/src/gallium/state_trackers/wgl/stw_tls.c +++ b/src/gallium/state_trackers/wgl/stw_tls.c @@ -1,6 +1,6 @@ /************************************************************************** * - * Copyright 2009 VMware, Inc. + * Copyright 2009-2013 VMware, Inc. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a @@ -26,13 +26,37 @@ **************************************************************************/ #include <windows.h> +#include <tlhelp32.h> #include "pipe/p_compiler.h" -#include "util/u_memory.h" +#include "util/u_debug.h" #include "stw_tls.h" static DWORD tlsIndex = TLS_OUT_OF_INDEXES; + +/** + * Static mutex to protect the access to g_pendingTlsData global and + * stw_tls_data::next member. + */ +static CRITICAL_SECTION g_mutex = { + (PCRITICAL_SECTION_DEBUG)-1, -1, 0, 0, 0, 0 +}; + +/** + * There is no way to invoke TlsSetValue for a different thread, so we + * temporarily put the thread data for non-current threads here. + */ +static struct stw_tls_data *g_pendingTlsData = NULL; + + +static INLINE struct stw_tls_data * +stw_tls_data_create(DWORD dwThreadId); + +static struct stw_tls_data * +stw_tls_lookup_pending_data(DWORD dwThreadId); + + boolean stw_tls_init(void) { @@ -41,35 +65,110 @@ stw_tls_init(void) return FALSE; } + /* + * DllMain is called with DLL_THREAD_ATTACH only for threads created after + * the DLL is loaded by the process. So enumerate and add our hook to all + * previously existing threads. + * + * XXX: Except for the current thread since it there is an explicit + * stw_tls_init_thread() call for it later on. + */ + if (1) { + DWORD dwCurrentProcessId = GetCurrentProcessId(); + DWORD dwCurrentThreadId = GetCurrentThreadId(); + HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, dwCurrentProcessId); + if (hSnapshot != INVALID_HANDLE_VALUE) { + THREADENTRY32 te; + te.dwSize = sizeof te; + if (Thread32First(hSnapshot, &te)) { + do { + if (te.dwSize >= FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) + + sizeof te.th32OwnerProcessID) { + if (te.th32OwnerProcessID == dwCurrentProcessId) { + if (te.th32ThreadID != dwCurrentThreadId) { + struct stw_tls_data *data; + data = stw_tls_data_create(te.th32ThreadID); + if (data) { + EnterCriticalSection(&g_mutex); + data->next = g_pendingTlsData; + g_pendingTlsData = data; + LeaveCriticalSection(&g_mutex); + } + } + } + } + te.dwSize = sizeof te; + } while (Thread32Next(hSnapshot, &te)); + } + CloseHandle(hSnapshot); + } + } + return TRUE; } + +/** + * Install windows hook for a given thread (not necessarily the current one). + */ static INLINE struct stw_tls_data * -stw_tls_data_create() +stw_tls_data_create(DWORD dwThreadId) { struct stw_tls_data *data; - data = CALLOC_STRUCT(stw_tls_data); - if (!data) + if (0) { + debug_printf("%s(0x%04lx)\n", __FUNCTION__, dwThreadId); + } + + data = (struct stw_tls_data *)calloc(1, sizeof *data); + if (!data) { goto no_data; + } + + data->dwThreadId = dwThreadId; data->hCallWndProcHook = SetWindowsHookEx(WH_CALLWNDPROC, stw_call_window_proc, NULL, - GetCurrentThreadId()); - if(data->hCallWndProcHook == NULL) + dwThreadId); + if (data->hCallWndProcHook == NULL) { goto no_hook; - - TlsSetValue(tlsIndex, data); + } return data; no_hook: - FREE(data); + free(data); no_data: return NULL; } +/** + * Destroy the per-thread data/hook. + * + * It is important to remove all hooks when unloading our DLL, otherwise our + * hook function might be called after it is no longer there. + */ +static void +stw_tls_data_destroy(struct stw_tls_data *data) +{ + assert(data); + if (!data) { + return; + } + + if (0) { + debug_printf("%s(0x%04lx)\n", __FUNCTION__, data->dwThreadId); + } + + if (data->hCallWndProcHook) { + UnhookWindowsHookEx(data->hCallWndProcHook); + data->hCallWndProcHook = NULL; + } + + free(data); +} + boolean stw_tls_init_thread(void) { @@ -79,9 +178,12 @@ stw_tls_init_thread(void) return FALSE; } - data = stw_tls_data_create(); - if(!data) + data = stw_tls_data_create(GetCurrentThreadId()); + if (!data) { return FALSE; + } + + TlsSetValue(tlsIndex, data); return TRUE; } @@ -96,15 +198,15 @@ stw_tls_cleanup_thread(void) } data = (struct stw_tls_data *) TlsGetValue(tlsIndex); - if(data) { + if (data) { TlsSetValue(tlsIndex, NULL); - - if(data->hCallWndProcHook) { - UnhookWindowsHookEx(data->hCallWndProcHook); - data->hCallWndProcHook = NULL; - } - - FREE(data); + } else { + /* See if there this thread's data in on the pending list */ + data = stw_tls_lookup_pending_data(GetCurrentThreadId()); + } + + if (data) { + stw_tls_data_destroy(data); } } @@ -112,11 +214,52 @@ void stw_tls_cleanup(void) { if (tlsIndex != TLS_OUT_OF_INDEXES) { + /* + * Destroy all items in g_pendingTlsData linked list. + */ + EnterCriticalSection(&g_mutex); + while (g_pendingTlsData) { + struct stw_tls_data * data = g_pendingTlsData; + g_pendingTlsData = data->next; + stw_tls_data_destroy(data); + } + LeaveCriticalSection(&g_mutex); + TlsFree(tlsIndex); tlsIndex = TLS_OUT_OF_INDEXES; } } +/* + * Search for the current thread in the g_pendingTlsData linked list. + * + * It will remove and return the node on success, or return NULL on failure. + */ +static struct stw_tls_data * +stw_tls_lookup_pending_data(DWORD dwThreadId) +{ + struct stw_tls_data ** p_data; + struct stw_tls_data *data = NULL; + + EnterCriticalSection(&g_mutex); + for (p_data = &g_pendingTlsData; *p_data; p_data = &(*p_data)->next) { + if ((*p_data)->dwThreadId == dwThreadId) { + data = *p_data; + + /* + * Unlink the node. + */ + *p_data = data->next; + data->next = NULL; + + break; + } + } + LeaveCriticalSection(&g_mutex); + + return data; +} + struct stw_tls_data * stw_tls_get_data(void) { @@ -127,13 +270,36 @@ stw_tls_get_data(void) } data = (struct stw_tls_data *) TlsGetValue(tlsIndex); - if(!data) { - /* DllMain is called with DLL_THREAD_ATTACH only by threads created after - * the DLL is loaded by the process */ - data = stw_tls_data_create(); - if(!data) - return NULL; + if (!data) { + DWORD dwCurrentThreadId = GetCurrentThreadId(); + + /* + * Search for the current thread in the g_pendingTlsData linked list. + */ + data = stw_tls_lookup_pending_data(dwCurrentThreadId); + + if (!data) { + /* + * This should be impossible now. + */ + assert(!"Failed to find thread data for thread id"); + + /* + * DllMain is called with DLL_THREAD_ATTACH only by threads created + * after the DLL is loaded by the process + */ + data = stw_tls_data_create(dwCurrentThreadId); + if (!data) { + return NULL; + } + } + + TlsSetValue(tlsIndex, data); } + assert(data); + assert(data->dwThreadId = GetCurrentThreadId()); + assert(data->next == NULL); + return data; } |