Resend: Protect IDirectDrawImpl against race conditions
Stefan Dösinger
stefandoesinger at gmx.at
Fri Nov 3 12:59:51 CST 2006
Resend of the last version of this patch, updated against current git(Depth
stencil patches), otherwise unmodified
-------------- next part --------------
From 709299fcee87f158ccca29f89a7fe36c63d6cc8f Mon Sep 17 00:00:00 2001
From: Stefan Doesinger <stefan at codeweavers.com>
Date: Sat, 14 Oct 2006 21:55:16 +0200
Subject: [PATCH] DDraw: Protect IDirectDrawImpl against race conditions
---
dlls/ddraw/ddraw.c | 116 +++++++++++++++++++++++++++++++++++++++++---
dlls/ddraw/ddraw_private.h | 20 +++++++-
dlls/ddraw/direct3d.c | 12 +++++
dlls/ddraw/main.c | 2 +
dlls/ddraw/surface.c | 2 +
5 files changed, 144 insertions(+), 8 deletions(-)
diff --git a/dlls/ddraw/ddraw.c b/dlls/ddraw/ddraw.c
index 11de3ef..adcb41f 100644
--- a/dlls/ddraw/ddraw.c
+++ b/dlls/ddraw/ddraw.c
@@ -140,6 +140,9 @@ IDirectDrawImpl_QueryInterface(IDirectDr
IsEqualGUID( &IID_IDirect3D3 , refiid ) ||
IsEqualGUID( &IID_IDirect3D7 , refiid ) )
{
+ /* Lock because we might modify the ImplType and the d3dversion */
+ ddobj_lock(This);
+
/* Check the surface implementation */
if(This->ImplType == SURFACE_UNKNOWN)
{
@@ -184,6 +187,7 @@ IDirectDrawImpl_QueryInterface(IDirectDr
*obj = ICOM_INTERFACE(This, IDirect3D7);
TRACE(" returning Direct3D7 interface at %p.\n", *obj);
}
+ ddobj_unlock(This);
}
/* Unknown interface */
@@ -241,6 +245,10 @@ IDirectDrawImpl_AddRef(IDirectDraw7 *ifa
void
IDirectDrawImpl_Destroy(IDirectDrawImpl *This)
{
+ /* No need to lock the object here, after the refcount falls to 0
+ * it shouldn't be in use anywere else
+ */
+
/* Clear the cooplevel to restore window and display mode */
IDirectDraw7_SetCooperativeLevel(ICOM_INTERFACE(This, IDirectDraw7),
NULL,
@@ -263,6 +271,13 @@ IDirectDrawImpl_Destroy(IDirectDrawImpl
IWineD3DDevice_Release(This->wineD3DDevice);
IWineD3D_Release(This->wineD3D);
+ if(This->critName)
+ {
+ DeleteCriticalSection(&This->crit);
+ HeapFree(GetProcessHeap(), 0, This->critName);
+ This->critName = NULL;
+ }
+
/* Now free the object */
HeapFree(GetProcessHeap(), 0, This);
}
@@ -301,6 +316,8 @@ IDirectDrawImpl_Release(IDirectDraw7 *if
* Helper function that modifies a HWND's Style and ExStyle for proper
* fullscreen use.
*
+ * Assumes that the critical section is properly held
+ *
* Params:
* This: Pointer to the DirectDraw implementation
* HWND: Window to setup
@@ -356,6 +373,8 @@ IDirectDrawImpl_SetupFullscreenWindow(ID
* Helper function that restores a windows' properties when taking it out
* of fullscreen mode
*
+ * Assumes that the critical section is properly held
+ *
* Params:
* This: Pointer to the DirectDraw implementation
* HWND: Window to setup
@@ -461,6 +480,7 @@ IDirectDrawImpl_SetCooperativeLevel(IDir
return DDERR_INVALIDPARAMS;
}
+ ddobj_lock(This);
/* Handle those levels first which set various hwnds */
if(cooplevel & DDSCL_SETFOCUSWINDOW)
{
@@ -475,11 +495,13 @@ IDirectDrawImpl_SetCooperativeLevel(IDir
DDSCL_EXCLUSIVE |
DDSCL_FULLSCREEN ) )
{
+ ddobj_unlock(This);
TRACE("Called with incompatible flags, returning DDERR_INVALIDPARAMS\n");
return DDERR_INVALIDPARAMS;
}
else if( (This->cooperative_level & DDSCL_FULLSCREEN) && window)
{
+ ddobj_unlock(This);
TRACE("Setting DDSCL_SETFOCUSWINDOW with an already set window, returning DDERR_HWNDALREADYSET\n");
return DDERR_HWNDALREADYSET;
}
@@ -504,6 +526,7 @@ IDirectDrawImpl_SetCooperativeLevel(IDir
/* Can't coexist with fullscreen or exclusive */
if(cooplevel & (DDSCL_FULLSCREEN | DDSCL_EXCLUSIVE) )
{
+ ddobj_unlock(This);
TRACE("(%p) DDSCL_NORMAL is not compative with DDSCL_FULLSCREEN or DDSCL_EXCLUSIVE\n", This);
return DDERR_INVALIDPARAMS;
}
@@ -539,6 +562,7 @@ IDirectDrawImpl_SetCooperativeLevel(IDir
/* Needs DDSCL_EXCLUSIVE */
if(!(cooplevel & DDSCL_EXCLUSIVE) )
{
+ ddobj_unlock(This);
TRACE("(%p) DDSCL_FULLSCREEN needs DDSCL_EXCLUSIVE\n", This);
return DDERR_INVALIDPARAMS;
}
@@ -576,6 +600,7 @@ IDirectDrawImpl_SetCooperativeLevel(IDir
}
else if(cooplevel & DDSCL_EXCLUSIVE)
{
+ ddobj_unlock(This);
TRACE("(%p) DDSCL_EXCLUSIVE needs DDSCL_FULLSCREEN\n", This);
return DDERR_INVALIDPARAMS;
}
@@ -599,13 +624,39 @@ IDirectDrawImpl_SetCooperativeLevel(IDir
}
}
+ if(cooplevel & DDSCL_MULTITHREADED && !(This->cooperative_level & DDSCL_MULTITHREADED))
+ {
+ if(This->surfaces != 0)
+ {
+ ERR("Setting DDSCL_MULTITHREADED, but surfaces exist already\n");
+ }
+
+ TRACE("(%p): Creating a critical section for the IDirectDrawImpl object\n", This);
+
+ /* DirectDraw critical section debug name:
+ * "ddraw_0xAAAAAAAAbbbbbbbb_crit"
+ * Size depens on the size of a pointer, 32 or 64 bit.
+ */
+ This->critName = HeapAlloc(GetProcessHeap(), 0, sizeof(This->critName[0]) * (16 + 2*sizeof(IDirectDrawImpl *)));;
+ if(!This->critName)
+ {
+ /* We did a nop-Lock before, do a nop unlock now to keep the code in a proper state */
+ ddobj_unlock(This);
+ ERR("Out of memory when allocating a critical section name\n");
+ return DDERR_OUTOFMEMORY;
+ }
+ sprintf(This->critName, "ddraw_%p_crit", This);
+ InitializeCriticalSection(&This->crit);
+ This->crit.DebugInfo->Spare[0] = (DWORD_PTR) This->critName;
+
+ ddobj_lock(This); /* Lock to allow the unlock later to work */
+ }
+
/* Unhandled flags */
if(cooplevel & DDSCL_ALLOWREBOOT)
WARN("(%p) Unhandled flag DDSCL_ALLOWREBOOT, harmless\n", This);
if(cooplevel & DDSCL_ALLOWMODEX)
WARN("(%p) Unhandled flag DDSCL_ALLOWMODEX, harmless\n", This);
- if(cooplevel & DDSCL_MULTITHREADED)
- FIXME("(%p) Unhandled flag DDSCL_MULTITHREADED, Uh Oh...\n", This);
if(cooplevel & DDSCL_FPUSETUP)
WARN("(%p) Unhandled flag DDSCL_FPUSETUP, harmless\n", This);
if(cooplevel & DDSCL_FPUPRESERVE)
@@ -613,6 +664,7 @@ IDirectDrawImpl_SetCooperativeLevel(IDir
/* Store the cooperative_level */
This->cooperative_level |= cooplevel;
+ ddobj_unlock(This);
TRACE("SetCooperativeLevel retuning DD_OK\n");
return DD_OK;
}
@@ -629,6 +681,10 @@ IDirectDrawImpl_SetCooperativeLevel(IDir
* It could mean that the current video mode should be left as-is. (But why
* call it then?)
*
+ * The msdn states that SetDisplayMode has to be called from the same thread
+ * that created the application window. Needs checking. What if there is no
+ * application window? (See above). That suggests that we need no locking.
+ *
* Params:
* Height, Width: Screen dimension
* BPP: Color depth in Bits per pixel
@@ -718,6 +774,9 @@ IDirectDrawImpl_RestoreDisplayMode(IDire
ICOM_THIS_FROM(IDirectDrawImpl, IDirectDraw7, iface);
TRACE("(%p)\n", This);
+ /* No locking needed, the orig_* are initialized at creation time and
+ * never changed
+ */
return IDirectDraw7_SetDisplayMode(ICOM_INTERFACE(This, IDirectDraw7),
This->orig_width,
This->orig_height,
@@ -748,6 +807,7 @@ IDirectDrawImpl_GetCaps(IDirectDraw7 *if
{
ICOM_THIS_FROM(IDirectDrawImpl, IDirectDraw7, iface);
TRACE("(%p)->(%p,%p)\n", This, DriverCaps, HELCaps);
+ /* The caps are only set at startup and not changed, no need to lock */
/* One structure must be != NULL */
if( (!DriverCaps) && (!HELCaps) )
@@ -820,6 +880,7 @@ IDirectDrawImpl_GetDisplayMode(IDirectDr
WINED3DDISPLAYMODE Mode;
DWORD Size;
TRACE("(%p)->(%p): Relay\n", This, DDSD);
+ /* Does not use structure members, no need to lock */
/* This seems sane */
if(!DDSD)
@@ -942,8 +1003,10 @@ IDirectDrawImpl_GetVerticalBlankStatus(I
/* This looks sane, the MSDN suggests it too */
if(!status) return DDERR_INVALIDPARAMS;
+ ddobj_lock(This);
*status = This->fake_vblank;
This->fake_vblank = !This->fake_vblank;
+ ddobj_unlock(This);
return DD_OK;
}
@@ -967,6 +1030,7 @@ IDirectDrawImpl_GetAvailableVidMem(IDire
{
ICOM_THIS_FROM(IDirectDrawImpl, IDirectDraw7, iface);
TRACE("(%p)->(%p, %p, %p)\n", This, Caps, total, free);
+ /* Total vidmem is const, free comes from wineD3D, no need to lock */
if(TRACE_ON(ddraw))
{
@@ -1103,11 +1167,13 @@ static HRESULT WINAPI IDirectDrawImpl_Ge
&Mode);
/* Fake the line sweeping of the monitor */
- /* FIXME: We should synchronize with a source to keep the refresh rate */
+ /* FIXME: We should synchronize with a source to keep the refresh rate */
+ ddobj_lock(This);
*Scanline = This->cur_scanline++;
/* Assume 20 scan lines in the vertical blank */
if (This->cur_scanline >= Mode.Height + 20)
This->cur_scanline = 0;
+ ddobj_unlock(This);
return DD_OK;
}
@@ -1130,6 +1196,7 @@ IDirectDrawImpl_TestCooperativeLevel(IDi
ICOM_THIS_FROM(IDirectDrawImpl, IDirectDraw7, iface);
HRESULT hr;
TRACE("(%p)\n", This);
+ /* Only data from wineD3D is used, no need to lock */
/* Description from MSDN:
* For fullscreen apps return DDERR_NOEXCLUSIVEMODE if the user switched
@@ -1195,6 +1262,7 @@ IDirectDrawImpl_GetGDISurface(IDirectDra
HRESULT hr;
DDSCAPS2 ddsCaps;
TRACE("(%p)->(%p)\n", This, GDISurface);
+ /* No need to lock, only data from wineD3D is used */
/* Get the back buffer from the wineD3DDevice and search its
* attached surfaces for the front buffer
@@ -1390,6 +1458,8 @@ IDirectDrawImpl_GetDeviceIdentifier(IDir
/* The DDGDI_GETHOSTIDENTIFIER returns the information about the 2D
* host adapter, if there's a secondary 3D adapter. This doesn't apply
* to any modern hardware, nor is it interesting for Wine, so ignore it
+ *
+ * The device identifier is global and const, no need to lock anything
*/
*DDDI = deviceidentifier;
@@ -1407,7 +1477,7 @@ IDirectDrawImpl_GetDeviceIdentifier(IDir
* Surface: Address to write the surface pointer to
*
* Returns:
- * Always returns DD_OK because it's a stub
+ * Always returns DDERR_NOTFOUND because it's a stub
*
*****************************************************************************/
static HRESULT WINAPI
@@ -1499,6 +1569,8 @@ IDirectDrawImpl_StartModeTest(IDirectDra
* Enumeration callback for IDirectDrawImpl_RecreateAllSurfaces.
* It re-recreates the WineD3DSurface. It's pretty straightforward
*
+ * Assumes that the lock is held properly by the caller
+ *
*****************************************************************************/
HRESULT WINAPI
IDirectDrawImpl_RecreateSurfacesCallback(IDirectDrawSurface7 *surf,
@@ -1607,7 +1679,9 @@ IDirectDrawImpl_RecreateSurfacesCallback
*
* A function, that converts all wineD3DSurfaces to the new implementation type
* It enumerates all surfaces with IWineD3DDevice::EnumSurfaces, creates a
- * new WineD3DSurface, copies the content and releases the old surface
+ * new WineD3DSurface, copies the content and releases the old surface.
+ *
+ * Assumes that the lock is held properly by the caller
*
*****************************************************************************/
static HRESULT
@@ -1642,6 +1716,8 @@ IDirectDrawImpl_RecreateAllSurfaces(IDir
* correct mipmap sublevel, and returns it to WineD3D.
* The surfaces are created already by IDirectDraw7::CreateSurface
*
+ * Assumes that the lock is held properly by IDirectDraw7::CreateSurface
+ *
* Params:
* With, Height: With and height of the surface
* Format: The requested format
@@ -1684,6 +1760,10 @@ D3D7CB_CreateSurface(IUnknown *device,
* A helper function for IDirectDraw7::CreateSurface. It creates a new surface
* with the passed parameters.
*
+ * Assumes that the lock on the IDirectDrawImpl object is held by
+ * IDirectDraw7::CreateSurface. No need to hold the lock on the surface,
+ * because it is beeing created
+ *
* Params:
* DDSD: Description of the surface to create
* Surf: Address to store the interface pointer at
@@ -2147,6 +2227,7 @@ IDirectDrawImpl_CreateSurface(IDirectDra
&Mode);
if(FAILED(hr))
{
+ /* This->orig_bpp is never changed, no need to lock */
ERR("Failed to read display mode from wined3d\n");
switch(This->orig_bpp)
{
@@ -2289,7 +2370,9 @@ IDirectDrawImpl_CreateSurface(IDirectDra
}
/* Create the first surface */
+ ddobj_lock(This);
hr = IDirectDrawImpl_CreateNewSurface(This, &desc2, &object, 0);
+ ddobj_unlock(This);
if( hr != DD_OK)
{
ERR("IDirectDrawImpl_CreateNewSurface failed with %08x\n", hr);
@@ -2330,10 +2413,12 @@ IDirectDrawImpl_CreateSurface(IDirectDra
if(desc2.dwHeight > 1) desc2.dwHeight /= 2;
}
+ ddobj_lock(This);
hr = IDirectDrawImpl_CreateNewSurface(This,
&desc2,
&object2,
level);
+ ddobj_unlock(This);
if(hr != DD_OK)
{
/* This destroys and possibly created surfaces too */
@@ -2401,6 +2486,7 @@ IDirectDrawImpl_CreateSurface(IDirectDra
WINED3DFORMAT Format;
WINED3DPOOL Pool = WINED3DPOOL_DEFAULT;
+ ddobj_lock(This);
This->tex_root = object;
if(desc2.ddsCaps.dwCaps & DDSCAPS_MIPMAP)
@@ -2438,6 +2524,7 @@ IDirectDrawImpl_CreateSurface(IDirectDra
(IUnknown *) ICOM_INTERFACE(object, IDirectDrawSurface7),
D3D7CB_CreateSurface );
This->tex_root = NULL;
+ ddobj_unlock(This);
}
return hr;
@@ -2603,6 +2690,7 @@ IDirectDrawImpl_EnumSurfaces(IDirectDraw
if(!Callback)
return DDERR_INVALIDPARAMS;
+ ddobj_lock(This);
/* Use the _SAFE enumeration, the app may destroy enumerated surfaces */
LIST_FOR_EACH_SAFE(entry, entry2, &This->surface_list)
{
@@ -2612,9 +2700,13 @@ IDirectDrawImpl_EnumSurfaces(IDirectDraw
desc = surf->surface_desc;
IDirectDrawSurface7_AddRef(ICOM_INTERFACE(surf, IDirectDrawSurface7));
if(Callback( ICOM_INTERFACE(surf, IDirectDrawSurface7), &desc, Context) != DDENUMRET_OK)
+ {
+ ddobj_unlock(This);
return DD_OK;
+ }
}
}
+ ddobj_unlock(This);
return DD_OK;
}
@@ -2624,7 +2716,9 @@ IDirectDrawImpl_EnumSurfaces(IDirectDraw
* Callback called by WineD3D to create Surfaces for render target usage
* This function takes the D3D target from the IDirectDrawImpl structure,
* and returns the WineD3DSurface. To avoid double usage, the surface
- * is marked as render target afterwards
+ * is marked as render target afterwards.
+ *
+ * Assumes that the lock is properly held by IDirectDraw7::CreateSurface
*
* Params
* device: The WineD3DDevice's parent
@@ -2730,7 +2824,10 @@ D3D7CB_CreateDepthStencilSurface(IUnknow
* Callback function for WineD3D which creates a new WineD3DSwapchain
* interface. It also creates an IParent interface to store that pointer,
* so the WineD3DSwapchain has a parent and can be released when the D3D
- * device is destroyed
+ * device is destroyed.
+ *
+ * Assumes that the lock is properly held by IDirectDraw7::CreateSurface
+ *
*****************************************************************************/
static HRESULT WINAPI
D3D7CB_CreateAdditionalSwapChain(IUnknown *device,
@@ -2819,6 +2916,7 @@ IDirectDrawImpl_AttachD3DDevice(IDirectD
/* If there's no window, create a hidden window. WineD3D needs it */
if(window == 0)
{
+ ddobj_lock(This);
window = CreateWindowExA(0, This->classname, "Hidden D3D Window",
WS_DISABLED, 0, 0,
GetSystemMetrics(SM_CXSCREEN),
@@ -2828,6 +2926,7 @@ IDirectDrawImpl_AttachD3DDevice(IDirectD
ShowWindow(window, SW_HIDE); /* Just to be sure */
WARN("(%p) No window for the Direct3DDevice, created a hidden window. HWND=%p\n", This, window);
This->d3d_window = window;
+ ddobj_unlock(This);
}
else
{
@@ -2879,6 +2978,7 @@ IDirectDrawImpl_AttachD3DDevice(IDirectD
/* Set this NOW, otherwise creating the depth stencil surface will cause a
* recursive loop until ram or emulated video memory is full
*/
+ ddobj_lock(This);
This->d3d_initialized = TRUE;
hr = IWineD3DDevice_Init3D(This->wineD3DDevice,
@@ -2887,8 +2987,10 @@ IDirectDrawImpl_AttachD3DDevice(IDirectD
if(FAILED(hr))
{
This->wineD3DDevice = NULL;
+ ddobj_unlock(This);
return hr;
}
+ ddobj_unlock(This);
/* Create an Index Buffer parent */
TRACE("(%p) Successfully initialized 3D\n", This);
diff --git a/dlls/ddraw/ddraw_private.h b/dlls/ddraw/ddraw_private.h
index aaa61a9..16acfdc 100644
--- a/dlls/ddraw/ddraw_private.h
+++ b/dlls/ddraw/ddraw_private.h
@@ -139,13 +139,19 @@ struct IDirectDrawImpl
IDirectDrawSurfaceImpl *tex_root;
BOOL depthstencil;
- /* For the dll unload cleanup code */
+ /* For the dll unload cleanup code. Lock the global list critical section
+ * when using this field!
+ */
struct list ddraw_list_entry;
/* The surface list - can't relay this to WineD3D
* because of IParent
*/
struct list surface_list;
LONG surfaces;
+
+ /* Multithreading synchronisation */
+ CRITICAL_SECTION crit;
+ char *critName;
};
/* Declare the VTables. They can be found ddraw.c */
@@ -189,6 +195,18 @@ remove_ddraw_object(IDirectDrawImpl *ddr
/* The default surface type */
extern WINED3DSURFTYPE DefaultSurfaceType;
+/* Helper inlines for multithreading, for all objects */
+static inline void
+ddobj_lock(IDirectDrawImpl *This)
+{
+ if(This->critName) EnterCriticalSection(&This->crit);
+}
+static inline void
+ddobj_unlock(IDirectDrawImpl *This)
+{
+ if(This->critName) LeaveCriticalSection(&This->crit);
+}
+
/*****************************************************************************
* IDirectDrawSurface implementation structure
*****************************************************************************/
diff --git a/dlls/ddraw/direct3d.c b/dlls/ddraw/direct3d.c
index 8de0c1c..5540df1 100644
--- a/dlls/ddraw/direct3d.c
+++ b/dlls/ddraw/direct3d.c
@@ -743,9 +743,12 @@ IDirect3DImpl_7_CreateDevice(IDirect3D7
*Device = NULL;
+ /* Lock to be sure */
+ ddobj_lock(This);
/* Fail device creation if non-opengl surfaces are used */
if(This->ImplType != SURFACE_OPENGL)
{
+ ddobj_unlock(This);
ERR("The application wants to create a Direct3D device, but non-opengl surfaces are set in the registry. Please set the surface implementation to opengl or autodetection to allow 3D rendering\n");
/* We only hit this path if a default surface is set in the registry. Incorrect autodetection
@@ -757,6 +760,7 @@ IDirect3DImpl_7_CreateDevice(IDirect3D7
/* So far we can only create one device per ddraw object */
if(This->d3ddevice)
{
+ ddobj_unlock(This);
FIXME("(%p): Only one Direct3D device per DirectDraw object supported\n", This);
return DDERR_INVALIDPARAMS;
}
@@ -764,6 +768,7 @@ IDirect3DImpl_7_CreateDevice(IDirect3D7
object = HeapAlloc(GetProcessHeap(), 0, sizeof(IDirect3DDeviceImpl));
if(!object)
{
+ ddobj_unlock(This);
ERR("Out of memory when allocating a IDirect3DDevice implementation\n");
return DDERR_OUTOFMEMORY;
}
@@ -790,6 +795,7 @@ IDirect3DImpl_7_CreateDevice(IDirect3D7
IndexBufferParent = HeapAlloc(GetProcessHeap(), 0, sizeof(IParentImpl *));
if(!IndexBufferParent)
{
+ ddobj_unlock(This);
ERR("Allocating memory for an index buffer parent failed\n");
HeapFree(GetProcessHeap(), 0, object);
return DDERR_OUTOFMEMORY;
@@ -813,6 +819,7 @@ IDirect3DImpl_7_CreateDevice(IDirect3D7
if(FAILED(hr))
{
+ ddobj_unlock(This);
ERR("Failed to create an index buffer\n");
HeapFree(GetProcessHeap(), 0, object);
return hr;
@@ -853,7 +860,11 @@ IDirect3DImpl_7_CreateDevice(IDirect3D7
This->d3d_target->WineD3DSurface,
target->WineD3DSurface);
if(hr != D3D_OK)
+ {
+ ddobj_unlock(This);
ERR("(%p) Error %08x setting the front and back buffer\n", This, hr);
+ return hr;
+ }
object->OffScreenTarget = TRUE;
}
@@ -887,6 +898,7 @@ IDirect3DImpl_7_CreateDevice(IDirect3D7
IDirectDrawSurface7_Release(depthbuffer);
}
+ ddobj_unlock(This);
return D3D_OK;
}
diff --git a/dlls/ddraw/main.c b/dlls/ddraw/main.c
index eec98f1..4a517c9 100644
--- a/dlls/ddraw/main.c
+++ b/dlls/ddraw/main.c
@@ -752,6 +752,8 @@ HRESULT WINAPI DllCanUnloadNow(void)
* Callback function for the EnumSurfaces call in DllMain.
* Dumps some surface info and releases the surface
*
+ * No need to hold the lock because this is called when the last thread detaches
+ *
* Params:
* surf: The enumerated surface
* desc: it's description
diff --git a/dlls/ddraw/surface.c b/dlls/ddraw/surface.c
index ab7eda6..70e374d 100644
--- a/dlls/ddraw/surface.c
+++ b/dlls/ddraw/surface.c
@@ -238,7 +238,9 @@ static void IDirectDrawSurfaceImpl_Destr
/* Reduce the ddraw surface count */
InterlockedDecrement(&This->ddraw->surfaces);
+ ddobj_lock(This->ddraw);
list_remove(&This->surface_list_entry);
+ ddobj_unlock(This->ddraw);
HeapFree(GetProcessHeap(), 0, This);
}
--
1.4.1.1
More information about the wine-patches
mailing list