@@ -353,7 +353,7 @@ struct LLWindowWin32::LLWindowWin32Thread : public LL::ThreadPool
353353 void run () override ;
354354
355355 // closes queue, wakes thread, waits until thread closes
356- void wakeAndClose ();
356+ void wakeAndDestroy ();
357357
358358 void glReady ()
359359 {
@@ -366,6 +366,9 @@ struct LLWindowWin32::LLWindowWin32Thread : public LL::ThreadPool
366366 // initialize D3D (if DXGI cannot be used)
367367 void initD3D ();
368368
369+ // clean up DXGI/D3D resources
370+ void cleanupDX ();
371+
369372 // call periodically to update available VRAM
370373 void updateVRAMUsage ();
371374
@@ -990,43 +993,10 @@ void LLWindowWin32::close()
990993
991994 LL_DEBUGS (" Window" ) << " Destroying Window" << LL_ENDL;
992995
993- mWindowThread ->post ([this , self = mWindowThread ]()
994- {
995- if (IsWindow (self->mWindowHandleThrd ))
996- {
997- if (self->mhDCThrd )
998- {
999- if (!ReleaseDC (self->mWindowHandleThrd , self->mhDCThrd ))
1000- {
1001- LL_WARNS (" Window" ) << " Release of ghDC failed!" << LL_ENDL;
1002- }
1003- }
1004-
1005- // Make sure we don't leave a blank toolbar button.
1006- ShowWindow (self->mWindowHandleThrd , SW_HIDE);
1007-
1008- // This causes WM_DESTROY to be sent *immediately*
1009- if (!destroy_window_handler (self->mWindowHandleThrd ))
1010- {
1011- OSMessageBox (mCallbacks ->translateString (" MBDestroyWinFailed" ),
1012- mCallbacks ->translateString (" MBShutdownErr" ),
1013- OSMB_OK);
1014- }
1015- }
1016- else
1017- {
1018- // Something killed the window while we were busy destroying gl or handle somehow got broken
1019- LL_WARNS (" Window" ) << " Failed to destroy Window, invalid handle!" << LL_ENDL;
1020- }
1021- self->mWindowHandleThrd = NULL ;
1022- self->mhDCThrd = NULL ;
1023- self->mGLReady = false ;
1024- });
1025-
1026996 mhDC = NULL ;
1027997 mWindowHandle = NULL ;
1028998
1029- mWindowThread ->wakeAndClose ();
999+ mWindowThread ->wakeAndDestroy ();
10301000}
10311001
10321002BOOL LLWindowWin32::isValid ()
@@ -4771,6 +4741,28 @@ void LLWindowWin32::LLWindowWin32Thread::initD3D()
47714741 }
47724742}
47734743
4744+ void LLWindowWin32::LLWindowWin32Thread::cleanupDX ()
4745+ {
4746+ // clean up DXGI/D3D resources
4747+ if (mDXGIAdapter )
4748+ {
4749+ mDXGIAdapter ->Release ();
4750+ mDXGIAdapter = nullptr ;
4751+ }
4752+
4753+ if (mD3DDevice )
4754+ {
4755+ mD3DDevice ->Release ();
4756+ mD3DDevice = nullptr ;
4757+ }
4758+
4759+ if (mD3D )
4760+ {
4761+ mD3D ->Release ();
4762+ mD3D = nullptr ;
4763+ }
4764+ }
4765+
47744766void LLWindowWin32::LLWindowWin32Thread::updateVRAMUsage ()
47754767{
47764768 LL_PROFILE_ZONE_SCOPED;
@@ -4918,58 +4910,109 @@ void LLWindowWin32::LLWindowWin32Thread::run()
49184910#endif
49194911 }
49204912
4921- // clean up DXGI/D3D resources
4922- if (mDXGIAdapter )
4913+ cleanupDX ();
4914+ }
4915+
4916+ void LLWindowWin32::LLWindowWin32Thread::wakeAndDestroy ()
4917+ {
4918+ if (mQueue ->isClosed ())
49234919 {
4924- mDXGIAdapter ->Release ();
4925- mDXGIAdapter = nullptr ;
4920+ LL_WARNS () << " Tried to close Queue. Win32 thread Queue already closed." <<LL_ENDL;
49264921 }
49274922
4928- if (mD3DDevice )
4923+ // Make sure we don't leave a blank toolbar button.
4924+ // Also hiding window now prevents user from suspending it
4925+ // via some action (like dragging it around)
4926+ ShowWindow (mWindowHandleThrd , SW_HIDE);
4927+
4928+ // Schedule destruction
4929+ HWND old_handle = mWindowHandleThrd ;
4930+ post ([this ]()
4931+ {
4932+ if (IsWindow (mWindowHandleThrd ))
4933+ {
4934+ if (mhDCThrd)
4935+ {
4936+ if (!ReleaseDC (mWindowHandleThrd , mhDCThrd))
4937+ {
4938+ LL_WARNS (" Window" ) << " Release of ghDC failed!" << LL_ENDL;
4939+ }
4940+ mhDCThrd = NULL ;
4941+ }
4942+
4943+ // This causes WM_DESTROY to be sent *immediately*
4944+ if (!destroy_window_handler (mWindowHandleThrd ))
4945+ {
4946+ LL_WARNS (" Window" ) << " Failed to destroy Window! " << std::hex << GetLastError () << LL_ENDL;
4947+ }
4948+ }
4949+ else
4950+ {
4951+ // Something killed the window while we were busy destroying gl or handle somehow got broken
4952+ LL_WARNS (" Window" ) << " Failed to destroy Window, invalid handle!" << LL_ENDL;
4953+ }
4954+ mWindowHandleThrd = NULL ;
4955+ mhDCThrd = NULL ;
4956+ mGLReady = false ;
4957+ });
4958+
4959+ LL_DEBUGS (" Window" ) << " Closing window's pool queue" << LL_ENDL;
4960+ mQueue ->close ();
4961+
4962+ // Post a nonsense user message to wake up the thread in
4963+ // case it is waiting for a getMessage()
4964+ if (old_handle)
49294965 {
4930- mD3DDevice ->Release ();
4931- mD3DDevice = nullptr ;
4966+ WPARAM wparam{ 0xB0B0 };
4967+ LL_DEBUGS (" Window" ) << " PostMessage(" << std::hex << old_handle
4968+ << " , " << WM_DUMMY_
4969+ << " , " << wparam << " )" << std::dec << LL_ENDL;
4970+ PostMessage (old_handle, WM_DUMMY_, wparam, 0x1337 );
49324971 }
49334972
4934- if (mD3D )
4973+ // There are cases where window will refuse to close,
4974+ // can't wait forever on join, check state instead
4975+ LLTimer timeout;
4976+ timeout.setTimerExpirySec (2.0 );
4977+ while (!getQueue ().done () && !timeout.hasExpired () && mWindowHandleThrd )
49354978 {
4936- mD3D ->Release ();
4937- mD3D = nullptr ;
4979+ ms_sleep (100 );
49384980 }
49394981
4940- }
4941-
4942- void LLWindowWin32::LLWindowWin32Thread::wakeAndClose ()
4943- {
4944- if (!mQueue ->isClosed ())
4982+ if (getQueue ().done () || mWindowHandleThrd == NULL )
49454983 {
4946- LL_DEBUGS (" Window" ) << " closing pool queue" << LL_ENDL;
4947- mQueue ->close ();
4948-
4949- // Post a nonsense user message to wake up the thred in
4950- // case it is waiting for a getMessage()
4951- //
4952- // Note that mWindowHandleThrd can change at any moment and isn't thread safe
4953- // but since we aren't writing it, should be safe to use even if value is obsolete
4954- // worst case dead handle gets reused and some new window ignores the message
4955- HWND old_handle = mWindowHandleThrd ;
4956- if (old_handle)
4984+ // Window is closed, started closing or is cleaning up
4985+ // now wait for our single thread to die.
4986+ if (mWindowHandleThrd )
49574987 {
4958- WPARAM wparam{ 0xB0B0 } ;
4959- LL_DEBUGS ( " Window " ) << " PostMessage( " << std::hex << old_handle
4960- << " , " << WM_DUMMY_
4961- << " , " << wparam << " ) " << std::dec << LL_ENDL;
4962- PostMessage (old_handle, WM_DUMMY_, wparam, 0x1337 ) ;
4988+ LL_INFOS ( " Window " ) << " Window is closing, waiting on pool's thread to join, time since post: " << timeout. getElapsedSeconds () << " s " << LL_ENDL ;
4989+ }
4990+ else
4991+ {
4992+ LL_DEBUGS ( " Window " ) << " Waiting on pool's thread, time since post: " << timeout. getElapsedSeconds () << " s " << LL_ENDL ;
49634993 }
4964-
4965- // now wait for our one thread to die.
49664994 for (auto & pair : mThreads )
49674995 {
4968- LL_DEBUGS (" Window" ) << " waiting on pool's thread " << pair.first << LL_ENDL;
49694996 pair.second .join ();
49704997 }
4971- LL_DEBUGS (" Window" ) << " thread pool shutdown complete" << LL_ENDL;
49724998 }
4999+ else
5000+ {
5001+ // Something suspended window thread, can't afford to wait forever
5002+ // so kill thread instead
5003+ // Ex: This can happen if user starts dragging window arround (if it
5004+ // was visible) or a modal notification pops up
5005+ LL_WARNS (" Window" ) << " Window is frozen, couldn't perform clean exit" << LL_ENDL;
5006+
5007+ for (auto & pair : mThreads )
5008+ {
5009+ // very unsafe
5010+ TerminateThread (pair.second .native_handle (), 0 );
5011+ pair.second .detach ();
5012+ cleanupDX ();
5013+ }
5014+ }
5015+ LL_DEBUGS (" Window" ) << " thread pool shutdown complete" << LL_ENDL;
49735016}
49745017
49755018void LLWindowWin32::post (const std::function<void ()>& func)
0 commit comments