Roderick Colenbrander : wgl: Improve wglDeleteContext threading behavior.
Alexandre Julliard
julliard at winehq.org
Tue Apr 27 16:59:31 CDT 2010
Module: wine
Branch: master
Commit: b86787e57c6b12e1d6e9f04f45e4bcbc0e1fc99c
URL: http://source.winehq.org/git/wine.git/?a=commit;h=b86787e57c6b12e1d6e9f04f45e4bcbc0e1fc99c
Author: Roderick Colenbrander <thunderbird2k at 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;
More information about the wine-cvs
mailing list