1 /**************************************************************************
3 * Copyright 2009-2013 VMware, Inc.
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sub license, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 **************************************************************************/
31 #include "pipe/p_compiler.h"
32 #include "util/u_debug.h"
35 static DWORD tlsIndex
= TLS_OUT_OF_INDEXES
;
39 * Static mutex to protect the access to g_pendingTlsData global and
40 * stw_tls_data::next member.
42 static CRITICAL_SECTION g_mutex
= {
43 (PCRITICAL_SECTION_DEBUG
)-1, -1, 0, 0, 0, 0
47 * There is no way to invoke TlsSetValue for a different thread, so we
48 * temporarily put the thread data for non-current threads here.
50 static struct stw_tls_data
*g_pendingTlsData
= NULL
;
53 static INLINE
struct stw_tls_data
*
54 stw_tls_data_create(DWORD dwThreadId
);
56 static struct stw_tls_data
*
57 stw_tls_lookup_pending_data(DWORD dwThreadId
);
63 tlsIndex
= TlsAlloc();
64 if (tlsIndex
== TLS_OUT_OF_INDEXES
) {
69 * DllMain is called with DLL_THREAD_ATTACH only for threads created after
70 * the DLL is loaded by the process. So enumerate and add our hook to all
71 * previously existing threads.
73 * XXX: Except for the current thread since it there is an explicit
74 * stw_tls_init_thread() call for it later on.
77 DWORD dwCurrentProcessId
= GetCurrentProcessId();
78 DWORD dwCurrentThreadId
= GetCurrentThreadId();
79 HANDLE hSnapshot
= CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD
, dwCurrentProcessId
);
80 if (hSnapshot
!= INVALID_HANDLE_VALUE
) {
82 te
.dwSize
= sizeof te
;
83 if (Thread32First(hSnapshot
, &te
)) {
85 if (te
.dwSize
>= FIELD_OFFSET(THREADENTRY32
, th32OwnerProcessID
) +
86 sizeof te
.th32OwnerProcessID
) {
87 if (te
.th32OwnerProcessID
== dwCurrentProcessId
) {
88 if (te
.th32ThreadID
!= dwCurrentThreadId
) {
89 struct stw_tls_data
*data
;
90 data
= stw_tls_data_create(te
.th32ThreadID
);
92 EnterCriticalSection(&g_mutex
);
93 data
->next
= g_pendingTlsData
;
94 g_pendingTlsData
= data
;
95 LeaveCriticalSection(&g_mutex
);
100 te
.dwSize
= sizeof te
;
101 } while (Thread32Next(hSnapshot
, &te
));
103 CloseHandle(hSnapshot
);
112 * Install windows hook for a given thread (not necessarily the current one).
114 static INLINE
struct stw_tls_data
*
115 stw_tls_data_create(DWORD dwThreadId
)
117 struct stw_tls_data
*data
;
120 debug_printf("%s(0x%04lx)\n", __FUNCTION__
, dwThreadId
);
123 data
= (struct stw_tls_data
*)calloc(1, sizeof *data
);
128 data
->dwThreadId
= dwThreadId
;
130 data
->hCallWndProcHook
= SetWindowsHookEx(WH_CALLWNDPROC
,
131 stw_call_window_proc
,
134 if (data
->hCallWndProcHook
== NULL
) {
147 * Destroy the per-thread data/hook.
149 * It is important to remove all hooks when unloading our DLL, otherwise our
150 * hook function might be called after it is no longer there.
153 stw_tls_data_destroy(struct stw_tls_data
*data
)
161 debug_printf("%s(0x%04lx)\n", __FUNCTION__
, data
->dwThreadId
);
164 if (data
->hCallWndProcHook
) {
165 UnhookWindowsHookEx(data
->hCallWndProcHook
);
166 data
->hCallWndProcHook
= NULL
;
173 stw_tls_init_thread(void)
175 struct stw_tls_data
*data
;
177 if (tlsIndex
== TLS_OUT_OF_INDEXES
) {
181 data
= stw_tls_data_create(GetCurrentThreadId());
186 TlsSetValue(tlsIndex
, data
);
192 stw_tls_cleanup_thread(void)
194 struct stw_tls_data
*data
;
196 if (tlsIndex
== TLS_OUT_OF_INDEXES
) {
200 data
= (struct stw_tls_data
*) TlsGetValue(tlsIndex
);
202 TlsSetValue(tlsIndex
, NULL
);
204 /* See if there this thread's data in on the pending list */
205 data
= stw_tls_lookup_pending_data(GetCurrentThreadId());
209 stw_tls_data_destroy(data
);
214 stw_tls_cleanup(void)
216 if (tlsIndex
!= TLS_OUT_OF_INDEXES
) {
218 * Destroy all items in g_pendingTlsData linked list.
220 EnterCriticalSection(&g_mutex
);
221 while (g_pendingTlsData
) {
222 struct stw_tls_data
* data
= g_pendingTlsData
;
223 g_pendingTlsData
= data
->next
;
224 stw_tls_data_destroy(data
);
226 LeaveCriticalSection(&g_mutex
);
229 tlsIndex
= TLS_OUT_OF_INDEXES
;
234 * Search for the current thread in the g_pendingTlsData linked list.
236 * It will remove and return the node on success, or return NULL on failure.
238 static struct stw_tls_data
*
239 stw_tls_lookup_pending_data(DWORD dwThreadId
)
241 struct stw_tls_data
** p_data
;
242 struct stw_tls_data
*data
= NULL
;
244 EnterCriticalSection(&g_mutex
);
245 for (p_data
= &g_pendingTlsData
; *p_data
; p_data
= &(*p_data
)->next
) {
246 if ((*p_data
)->dwThreadId
== dwThreadId
) {
252 *p_data
= data
->next
;
258 LeaveCriticalSection(&g_mutex
);
263 struct stw_tls_data
*
264 stw_tls_get_data(void)
266 struct stw_tls_data
*data
;
268 if (tlsIndex
== TLS_OUT_OF_INDEXES
) {
272 data
= (struct stw_tls_data
*) TlsGetValue(tlsIndex
);
274 DWORD dwCurrentThreadId
= GetCurrentThreadId();
277 * Search for the current thread in the g_pendingTlsData linked list.
279 data
= stw_tls_lookup_pending_data(dwCurrentThreadId
);
283 * This should be impossible now.
285 assert(!"Failed to find thread data for thread id");
288 * DllMain is called with DLL_THREAD_ATTACH only by threads created
289 * after the DLL is loaded by the process
291 data
= stw_tls_data_create(dwCurrentThreadId
);
297 TlsSetValue(tlsIndex
, data
);
301 assert(data
->dwThreadId
= GetCurrentThreadId());
302 assert(data
->next
== NULL
);