Module: wine Branch: master Commit: b86787e57c6b12e1d6e9f04f45e4bcbc0e1fc99c URL: http://source.winehq.org/git/wine.git/?a=commit;h=b86787e57c6b12e1d6e9f04f45...
Author: Roderick Colenbrander thunderbird2k@gmail.com Date: Mon Apr 26 15:49:56 2010 +0200
wgl: Improve wglDeleteContext threading behavior.
---
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;