[PATCH 4/5] wgl: improve wglDeleteContext threading behavior

Roderick Colenbrander thunderbird2k at gmail.com
Mon Apr 26 08:49:56 CDT 2010


---
 dlls/opengl32/tests/opengl.c |   56 +++++++++++++++++++++++++++++++++++++
 dlls/winex11.drv/opengl.c    |   62 +++++++++++++++++++++++++++++------------
 2 files changed, 100 insertions(+), 18 deletions(-)

diff --git a/dlls/opengl32/tests/opengl.c b/dlls/opengl32/tests/opengl.c
index d0eb24f..a59f8d2 100644
--- a/dlls/opengl32/tests/opengl.c
+++ b/dlls/opengl32/tests/opengl.c
@@ -499,6 +499,61 @@ static void test_acceleration(HDC hdc)
     }
 }
 
+struct wgl_thread_param
+{
+    HANDLE test_finished;
+    HGLRC hglrc;
+    BOOL hglrc_deleted;
+};
+
+static DWORD WINAPI wgl_thread(void *param)
+{
+    struct wgl_thread_param *p = param;
+
+    p->hglrc_deleted = wglDeleteContext(p->hglrc);
+    SetEvent(p->test_finished);
+
+    return 0;
+}
+
+static void test_deletecontext(HDC hdc)
+{
+    struct wgl_thread_param thread_params;
+    HGLRC hglrc = wglCreateContext(hdc);
+    HANDLE thread_handle;
+    DWORD res, tid;
+
+    if(!hglrc)
+    {
+        skip("wglCreateContext failed!\n");
+        return;
+    }
+
+    res = wglMakeCurrent(hdc, hglrc);
+    if(!res)
+    {
+        skip("wglMakeCurrent failed!\n");
+        return;
+    }
+
+    /* WGL doesn't allow you to delete a context from a different thread than the one in which it is current.
+     * This differs from GLX which does allow it but it delays actual deletion until the context becomes not current.
+     */
+    thread_params.hglrc = hglrc;
+    thread_params.test_finished = CreateEvent(NULL, FALSE, FALSE, NULL);
+    thread_handle = CreateThread(NULL, 0, wgl_thread, &thread_params, 0, &tid);
+    ok(!!thread_handle, "Failed to create thread, last error %#x.\n", GetLastError());
+    if(thread_handle)
+    {
+        WaitForSingleObject(thread_handle, INFINITE);
+        ok(thread_params.hglrc_deleted == FALSE, "Attempt to delete WGL context from another thread passed but should fail!\n");
+    }
+    CloseHandle(thread_params.test_finished);
+
+    res = wglDeleteContext(hglrc);
+    ok(res == TRUE, "wglDeleteContext failed\n");
+}
+
 static void test_make_current_read(HDC hdc)
 {
     int res;
@@ -802,6 +857,7 @@ START_TEST(opengl)
             return;
         }
 
+        test_deletecontext(hdc);
         test_makecurrent(hdc);
         test_setpixelformat(hdc);
         test_sharelists(hdc);
diff --git a/dlls/winex11.drv/opengl.c b/dlls/winex11.drv/opengl.c
index cf22f95..ccd9050 100644
--- a/dlls/winex11.drv/opengl.c
+++ b/dlls/winex11.drv/opengl.c
@@ -113,6 +113,7 @@ typedef struct wine_glcontext {
     BOOL do_escape;
     BOOL has_been_current;
     BOOL sharing;
+    DWORD tid;
     BOOL gl3_context;
     XVisualInfo *vis;
     WineGLPixelFormat *fmt;
@@ -1763,29 +1764,33 @@ HGLRC CDECL X11DRV_wglCreateContext(X11DRV_PDEVICE *physDev)
 BOOL CDECL X11DRV_wglDeleteContext(HGLRC hglrc)
 {
     Wine_GLContext *ctx = (Wine_GLContext *) hglrc;
-    BOOL ret = TRUE;
 
     TRACE("(%p)\n", hglrc);
 
     if (!has_opengl()) return 0;
 
-    wine_tsx11_lock();
-    /* A game (Half Life not to name it) deletes twice the same context,
-    * so make sure it is valid first */
-    if (is_valid_context( ctx ))
-    {
-        if (ctx->ctx) pglXDestroyContext(gdi_display, ctx->ctx);
-        free_context(ctx);
-    }
-    else
+    if (!is_valid_context(ctx))
     {
         WARN("Error deleting context !\n");
         SetLastError(ERROR_INVALID_HANDLE);
-        ret = FALSE;
+        return FALSE;
     }
-    wine_tsx11_unlock();
 
-    return ret;
+    /* WGL doesn't allow deletion of a context which is current in another thread */
+    if (ctx->tid != 0 && ctx->tid != GetCurrentThreadId())
+    {
+        TRACE("Cannot delete context=%p because it is current in another thread.\n", ctx);
+        return FALSE;
+    }
+
+    if (ctx->ctx)
+    {
+        wine_tsx11_lock();
+        pglXDestroyContext(gdi_display, ctx->ctx);
+        wine_tsx11_unlock();
+    }
+
+    return TRUE;
 }
 
 /**
@@ -1857,16 +1862,26 @@ BOOL CDECL X11DRV_wglMakeCurrent(X11DRV_PDEVICE *physDev, HGLRC hglrc) {
     if (!has_opengl()) return FALSE;
 
     wine_tsx11_lock();
-    if (hglrc == NULL) {
+    if (hglrc == NULL)
+    {
+        Wine_GLContext *prev_ctx = NtCurrentTeb()->glContext;
+        if (prev_ctx) prev_ctx->tid = 0;
+
         ret = pglXMakeCurrent(gdi_display, None, NULL);
         NtCurrentTeb()->glContext = NULL;
-    } else if (ctx->fmt->iPixelFormat != physDev->current_pf) {
+    }
+    else if (ctx->fmt->iPixelFormat != physDev->current_pf)
+    {
         WARN( "mismatched pixel format hdc %p %u ctx %p %u\n",
               hdc, physDev->current_pf, ctx, ctx->fmt->iPixelFormat );
         SetLastError( ERROR_INVALID_PIXEL_FORMAT );
         ret = FALSE;
-    } else {
+    }
+    else
+    {
         Drawable drawable = get_glxdrawable(physDev);
+        Wine_GLContext *prev_ctx = NtCurrentTeb()->glContext;
+        if (prev_ctx) prev_ctx->tid = 0;
 
         /* The describe lines below are for debugging purposes only */
         if (TRACE_ON(wgl)) {
@@ -1881,6 +1896,7 @@ BOOL CDECL X11DRV_wglMakeCurrent(X11DRV_PDEVICE *physDev, HGLRC hglrc) {
         if(ret)
         {
             ctx->has_been_current = TRUE;
+            ctx->tid = GetCurrentThreadId();
             ctx->hdc = hdc;
             ctx->read_hdc = hdc;
             ctx->drawables[0] = drawable;
@@ -1913,18 +1929,28 @@ BOOL CDECL X11DRV_wglMakeContextCurrentARB(X11DRV_PDEVICE* pDrawDev, X11DRV_PDEV
     if (!has_opengl()) return 0;
 
     wine_tsx11_lock();
-    if (hglrc == NULL) {
+    if (hglrc == NULL)
+    {
+        Wine_GLContext *prev_ctx = NtCurrentTeb()->glContext;
+        if (prev_ctx) prev_ctx->tid = 0;
+
         ret = pglXMakeCurrent(gdi_display, None, NULL);
         NtCurrentTeb()->glContext = NULL;
-    } else {
+    }
+    else
+    {
         if (NULL == pglXMakeContextCurrent) {
             ret = FALSE;
         } else {
+            Wine_GLContext *prev_ctx = NtCurrentTeb()->glContext;
             Wine_GLContext *ctx = (Wine_GLContext *) hglrc;
             Drawable d_draw = get_glxdrawable(pDrawDev);
             Drawable d_read = get_glxdrawable(pReadDev);
 
+            if (prev_ctx) prev_ctx->tid = 0;
+
             ctx->has_been_current = TRUE;
+            ctx->tid = GetCurrentThreadId();
             ctx->hdc = pDrawDev->hdc;
             ctx->read_hdc = pReadDev->hdc;
             ctx->drawables[0] = d_draw;
-- 
1.6.3.3




More information about the wine-patches mailing list