[PATCH] d3d9 : Do not proceed reset or the final release of device if they are called from another thread than the one which created it. With tests.

Jérôme Gardou jerome.gardou at laposte.net
Sun Nov 30 12:12:25 CST 2008


---
 dlls/d3d9/d3d9_private.h |    2 +
 dlls/d3d9/device.c       |   18 ++++++++++-
 dlls/d3d9/directx.c      |    1 +
 dlls/d3d9/tests/device.c |   76 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 96 insertions(+), 1 deletions(-)

diff --git a/dlls/d3d9/d3d9_private.h b/dlls/d3d9/d3d9_private.h
index c925cf5..abb50a2 100644
--- a/dlls/d3d9/d3d9_private.h
+++ b/dlls/d3d9/d3d9_private.h
@@ -187,6 +187,8 @@ typedef struct IDirect3DDevice9Impl
     unsigned int                 numConvertedDecls, declArraySize;
 
     BOOL                          notreset;
+    
+    DWORD                         CreatorThread ;
 } IDirect3DDevice9Impl;
 
 
diff --git a/dlls/d3d9/device.c b/dlls/d3d9/device.c
index 679eaab..dcf8c23 100644
--- a/dlls/d3d9/device.c
+++ b/dlls/d3d9/device.c
@@ -76,14 +76,23 @@ static ULONG WINAPI IDirect3DDevice9Impl_AddRef(LPDIRECT3DDEVICE9EX iface) {
 static ULONG WINAPI IDirect3DDevice9Impl_Release(LPDIRECT3DDEVICE9EX iface) {
     IDirect3DDevice9Impl *This = (IDirect3DDevice9Impl *)iface;
     ULONG ref;
+/* This one is really weird... Wehn called from an other thread than the creator one, 
+ * the reference is decreased, but the interface is still alive, permitting it to live
+ * with a reference of 0 or less... This one behaviour is demonstrated into the tests...
+ */
 
     if (This->inDestruction) return 0;
+    
     ref = InterlockedDecrement(&This->ref);
 
     TRACE("(%p) : ReleaseRef to %d\n", This, ref);
 
-    if (ref == 0) {
+    if (ref <= 0) {
       unsigned i;
+      if (This->CreatorThread != GetCurrentThreadId()) {
+          WARN("Trying to proceed the final release of %p from a different thread than the creator one\n", This) ;
+          return ref ;
+      }
       This->inDestruction = TRUE;
 
       EnterCriticalSection(&d3d9_cs);
@@ -348,6 +357,13 @@ static HRESULT  WINAPI  IDirect3DDevice9Impl_Reset(LPDIRECT3DDEVICE9EX iface, D3
      * Unsetting them is no problem, because the states are supposed to be reset anyway. If the validation
      * below fails, the device is considered "lost", and _Reset and _Release are the only allowed calls
      */
+
+    /*The thread calling reset must be the same than the one who called create */
+    if (This->CreatorThread != GetCurrentThreadId()) {
+        WARN("Trying to reset device from thread %u, created from %u", GetCurrentThreadId(), This->CreatorThread) ;
+        return D3DERR_INVALIDCALL ;
+    }
+
     IWineD3DDevice_SetIndices(This->WineD3DDevice, NULL);
     for(i = 0; i < 16; i++) {
         IWineD3DDevice_SetStreamSource(This->WineD3DDevice, i, NULL, 0, 0);
diff --git a/dlls/d3d9/directx.c b/dlls/d3d9/directx.c
index cb04b1f..beabbbe 100644
--- a/dlls/d3d9/directx.c
+++ b/dlls/d3d9/directx.c
@@ -548,6 +548,7 @@ static HRESULT WINAPI IDirect3D9Impl_CreateDevice(LPDIRECT3D9EX iface, UINT Adap
      * can be used without further checking
      */
     object->convertedDecls = HeapAlloc(GetProcessHeap(), 0, 0);
+    object->CreatorThread = GetCurrentThreadId() ;
     LeaveCriticalSection(&d3d9_cs);
 
     return hr;
diff --git a/dlls/d3d9/tests/device.c b/dlls/d3d9/tests/device.c
index b90d005..4c3d196 100644
--- a/dlls/d3d9/tests/device.c
+++ b/dlls/d3d9/tests/device.c
@@ -2139,6 +2139,81 @@ err_out:
     return;
 }
 
+typedef struct thread_test_data
+{
+    IDirect3DDevice9 *pDevice ;
+    D3DPRESENT_PARAMETERS* pD3Dpp ;
+} thread_test_data ;
+
+DWORD WINAPI otherthread(LPVOID data) {
+    HRESULT hr ;
+    thread_test_data* thread_data = (thread_test_data*) data ;
+    IDirect3DDevice9 *pDevice = thread_data->pDevice ;
+    D3DPRESENT_PARAMETERS* pD3Dpp = thread_data->pD3Dpp ;
+    ULONG ref ;
+    
+    hr = IDirect3DDevice9_Reset(pDevice, pD3Dpp) ;
+    ok(hr == D3DERR_INVALIDCALL, "Could reset device from a thread different from the one which created it. Unexpected\n") ;
+
+    IDirect3DDevice9_Release(pDevice) ;
+    ref = IDirect3DDevice9_AddRef(pDevice) ;
+    ok(ref == 1, "Expected ref to be 1, but got %u\n", ref) ;
+    IDirect3DDevice9_Release(pDevice) ;
+    ref = IDirect3DDevice9_Release(pDevice) ;
+    ok (ref == -1, "expected ref to be -1, got %d\n", ref) ;
+
+    return 0 ;
+}
+
+static void test_multithread(void) {
+    HRESULT               hr;
+    HWND                  hwnd = NULL;
+
+    IDirect3D9            *pD3d = NULL;
+    IDirect3DDevice9      *pDevice = NULL;
+    D3DPRESENT_PARAMETERS d3dpp;
+    D3DDISPLAYMODE        d3ddm;
+
+    HANDLE thread ;
+    thread_test_data thread_data ;
+    DWORD tid ;
+ 
+    pD3d = pDirect3DCreate9( D3D_SDK_VERSION );
+    ok(pD3d != NULL, "Failed to create IDirect3D9 object\n");
+    hwnd = CreateWindow( "static", "d3d9_test", WS_OVERLAPPEDWINDOW, 100, 100, 160, 160, NULL, NULL, NULL, NULL );
+    ok(hwnd != NULL, "Failed to create window\n");
+    if (!pD3d || !hwnd) goto cleanup;
+
+    IDirect3D9_GetAdapterDisplayMode( pD3d, D3DADAPTER_DEFAULT, &d3ddm );
+    ZeroMemory( &d3dpp, sizeof(d3dpp) );
+    d3dpp.Windowed         = TRUE;
+    d3dpp.SwapEffect       = D3DSWAPEFFECT_DISCARD;
+    d3dpp.BackBufferFormat = d3ddm.Format;
+
+    hr = IDirect3D9_CreateDevice( pD3d, D3DADAPTER_DEFAULT, D3DDEVTYPE_NULLREF, hwnd,
+                                  D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_MULTITHREADED, &d3dpp, &pDevice );
+    ok(SUCCEEDED(hr) || hr == D3DERR_NOTAVAILABLE, "Failed to create IDirect3D9Device (%08x)\n", hr);
+    if (FAILED(hr)) {
+        skip("failed to create a d3d device\n");
+        goto cleanup;
+    }
+
+    thread_data.pDevice = pDevice ;
+    thread_data.pD3Dpp = &d3dpp ;
+
+    thread = CreateThread(NULL, 0, otherthread, (LPVOID) &thread_data, 0, &tid) ;
+    ok(thread != NULL, "Could not create the thread.\n") ;
+    if (thread == NULL)
+        goto cleanup ;
+    WaitForSingleObject(thread, INFINITE) ;
+    CloseHandle(thread) ;
+
+cleanup:
+    if (pD3d)     IUnknown_Release( pD3d );
+    if (pDevice)  IUnknown_Release( pDevice );
+    DestroyWindow( hwnd );
+}
+
 
 START_TEST(device)
 {
@@ -2169,5 +2244,6 @@ START_TEST(device)
         test_lights();
         test_set_stream_source();
         test_scissor_size();
+        test_multithread() ;
     }
 }
-- 
1.6.0.4


--------------090706050502090406050900--



More information about the wine-patches mailing list