Mesa (master): st/wgl: Install our windows message hook to threads created before the ICD is loaded .

Jose Fonseca jrfonseca at kemper.freedesktop.org
Fri Apr 5 17:28:21 UTC 2013


Module: Mesa
Branch: master
Commit: 1fefc65d20c126e834b0b42a560ffdd8a3a1197e
URL:    http://cgit.freedesktop.org/mesa/mesa/commit/?id=1fefc65d20c126e834b0b42a560ffdd8a3a1197e

Author: José Fonseca <jfonseca at vmware.com>
Date:   Thu Apr  4 20:27:39 2013 +0100

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 <brianp at vmware.com>

---

 src/gallium/state_trackers/wgl/stw_tls.c |  218 ++++++++++++++++++++++++++----
 src/gallium/state_trackers/wgl/stw_tls.h |    4 +
 2 files changed, 196 insertions(+), 26 deletions(-)

diff --git a/src/gallium/state_trackers/wgl/stw_tls.c b/src/gallium/state_trackers/wgl/stw_tls.c
index 4bd6a92..c0da930 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;
 }
diff --git a/src/gallium/state_trackers/wgl/stw_tls.h b/src/gallium/state_trackers/wgl/stw_tls.h
index fbf8b1c..0d2cf6b 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




More information about the mesa-commit mailing list