diff --git a/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DMouse.cpp b/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DMouse.cpp index 5b71653c8d8..11d913b8252 100644 --- a/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DMouse.cpp +++ b/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DMouse.cpp @@ -75,8 +75,15 @@ void MouseThreadClass::Thread_Function() while (running) { isThread = TRUE; - if (TheMouse) - TheMouse->draw(); + // Hold the mutex for the TheMouse check AND the draw() call together so + // that TheMouse cannot be destroyed between the null-check and the actual + // dereference. CriticalSectionClass uses a Windows CRITICAL_SECTION which + // is re-entrant per-thread, so the nested acquisition inside draw() is safe. + { + CriticalSectionClass::LockClass m(mutex); + if (TheMouse) + TheMouse->draw(); + } isThread = FALSE; Switch_Thread(); } @@ -108,6 +115,12 @@ W3DMouse::W3DMouse() W3DMouse::~W3DMouse() { + // Stop the mouse update thread FIRST, before freeing any assets it may be + // actively using. If the thread is mid-draw(), Stop() will wait for it to + // finish before returning, preventing a use-after-free when we release + // D3D/W3D resources below. + thread.Stop(); + LPDIRECT3DDEVICE8 m_pDev = DX8Wrapper::_Get_D3D_Device8(); if (m_pDev) @@ -119,8 +132,6 @@ W3DMouse::~W3DMouse() freeD3DAssets(); freeW3DAssets(); - thread.Stop(); - } void W3DMouse::initPolygonAssets()