From: José Fonseca Date: Thu, 4 Apr 2013 19:27:39 +0000 (+0100) Subject: st/wgl: Install our windows message hook to threads created before the ICD is loaded. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=1fefc65d20c126e834b0b42a560ffdd8a3a1197e;p=mesa.git st/wgl: Install our windows message hook to threads created before the ICD is loaded. Otherwise we will not receive destroy windows events, causing framebuffers to leak. This happens particularly with java and jogl. Tested with java + jogl, MATLAB. VMware Internal Bug Number: 1013086. Reviewed-by: Brian Paul --- 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 +#include #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; } diff --git a/src/gallium/state_trackers/wgl/stw_tls.h b/src/gallium/state_trackers/wgl/stw_tls.h index fbf8b1cbee4..0d2cf6b06e8 100644 --- a/src/gallium/state_trackers/wgl/stw_tls.h +++ b/src/gallium/state_trackers/wgl/stw_tls.h @@ -32,7 +32,11 @@ struct stw_tls_data { + DWORD dwThreadId; + HHOOK hCallWndProcHook; + + struct stw_tls_data *next; }; boolean