From 4a34a1196627c7e9998edde725d5e839f3ef61b9 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Sat, 20 Jan 2024 02:26:51 +0200 Subject: SL-18721 Shutdown fixes 1. After window closes viewer still takes some time to shut down, so added splash screen to not confuse users (and to see if something gets stuck) 2. Having two identical mWindowHandle caused confusion for me, so I split them. It looks like there might have been issues with thread being stuck because thread's handle wasn't cleaned up. 3. Made region clean mCacheMap immediately instead of spending time making copies on shutdown --- indra/llwindow/llwindowwin32.cpp | 61 ++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 30 deletions(-) (limited to 'indra/llwindow') diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp index 057d7a700e..0c1ed85477 100644 --- a/indra/llwindow/llwindowwin32.cpp +++ b/indra/llwindow/llwindowwin32.cpp @@ -412,8 +412,8 @@ struct LLWindowWin32::LLWindowWin32Thread : public LL::ThreadPool using FuncType = std::function; // call GetMessage() and pull enqueue messages for later processing void gatherInput(); - HWND mWindowHandle = NULL; - HDC mhDC = 0; + HWND mWindowHandleThrd = NULL; + HDC mhDCThrd = 0; // *HACK: Attempt to prevent startup crashes by deferring memory accounting // until after some graphics setup. See SL-20177. -Cosmic,2023-09-18 @@ -987,23 +987,23 @@ void LLWindowWin32::close() LL_DEBUGS("Window") << "Destroying Window" << LL_ENDL; - mWindowThread->post([=]() + mWindowThread->post([this, self = mWindowThread]() { - if (IsWindow(mWindowHandle)) + if (IsWindow(self->mWindowHandleThrd)) { - if (mhDC) + if (self->mhDCThrd) { - if (!ReleaseDC(mWindowHandle, mhDC)) + if (!ReleaseDC(self->mWindowHandleThrd, self->mhDCThrd)) { LL_WARNS("Window") << "Release of ghDC failed!" << LL_ENDL; } } // Make sure we don't leave a blank toolbar button. - ShowWindow(mWindowHandle, SW_HIDE); + ShowWindow(self->mWindowHandleThrd, SW_HIDE); // This causes WM_DESTROY to be sent *immediately* - if (!destroy_window_handler(mWindowHandle)) + if (!destroy_window_handler(self->mWindowHandleThrd)) { OSMessageBox(mCallbacks->translateString("MBDestroyWinFailed"), mCallbacks->translateString("MBShutdownErr"), @@ -1015,17 +1015,18 @@ void LLWindowWin32::close() // Something killed the window while we were busy destroying gl or handle somehow got broken LL_WARNS("Window") << "Failed to destroy Window, invalid handle!" << LL_ENDL; } - + self->mWindowHandleThrd = NULL; + self->mhDCThrd = NULL; + self->mGLReady = false; }); - // Window thread might be waiting for a getMessage(), give it - // a push to enshure it will process destroy_window_handler - kickWindowThread(); - // Even though the above lambda might not yet have run, we've already - // bound mWindowHandle into it by value, which should suffice for the - // operations we're asking. That's the last time WE should touch it. mhDC = NULL; mWindowHandle = NULL; + + // Window thread might be waiting for a getMessage(), give it + // a push to enshure it will process destroy_window_handler + kickWindowThread(); + mWindowThread->close(); } @@ -1777,8 +1778,8 @@ void LLWindowWin32::recreateWindow(RECT window_rect, DWORD dw_ex_style, DWORD dw () { LL_DEBUGS("Window") << "recreateWindow(): window_work entry" << LL_ENDL; - self->mWindowHandle = 0; - self->mhDC = 0; + self->mWindowHandleThrd = 0; + self->mhDCThrd = 0; if (oldWindowHandle) { @@ -1813,20 +1814,20 @@ void LLWindowWin32::recreateWindow(RECT window_rect, DWORD dw_ex_style, DWORD dw { // Failed to create window: clear the variables. This // assignment is valid because we're running on mWindowThread. - self->mWindowHandle = NULL; - self->mhDC = 0; + self->mWindowHandleThrd = NULL; + self->mhDCThrd = 0; } else { // Update mWindowThread's own mWindowHandle and mhDC. - self->mWindowHandle = handle; - self->mhDC = GetDC(handle); + self->mWindowHandleThrd = handle; + self->mhDCThrd = GetDC(handle); } updateWindowRect(); // It's important to wake up the future either way. - promise.set_value(std::make_pair(self->mWindowHandle, self->mhDC)); + promise.set_value(std::make_pair(self->mWindowHandleThrd, self->mhDCThrd)); LL_DEBUGS("Window") << "recreateWindow(): window_work done" << LL_ENDL; }; // But how we pass window_work to the window thread depends on whether we @@ -4589,7 +4590,7 @@ U32 LLWindowWin32::getAvailableVRAMMegabytes() #endif // LL_WINDOWS inline LLWindowWin32::LLWindowWin32Thread::LLWindowWin32Thread() - : LL::ThreadPool("Window Thread", 1, MAX_QUEUE_SIZE) + : LL::ThreadPool("Window Thread", 1, MAX_QUEUE_SIZE, false) { LL::ThreadPool::start(); } @@ -4745,7 +4746,7 @@ void LLWindowWin32::LLWindowWin32Thread::initD3D() { if (!mGLReady) { return; } - if (mDXGIAdapter == NULL && mD3DDevice == NULL && mWindowHandle != 0) + if (mDXGIAdapter == NULL && mD3DDevice == NULL && mWindowHandleThrd != 0) { mD3D = Direct3DCreate9(D3D_SDK_VERSION); @@ -4755,7 +4756,7 @@ void LLWindowWin32::LLWindowWin32Thread::initD3D() d3dpp.Windowed = TRUE; d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; - HRESULT res = mD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, mWindowHandle, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &mD3DDevice); + HRESULT res = mD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, mWindowHandleThrd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &mD3DDevice); if (FAILED(res)) { @@ -4861,7 +4862,7 @@ void LLWindowWin32::LLWindowWin32Thread::run() // lazily call initD3D inside this loop to catch when mGLReady has been set to true initDX(); - if (mWindowHandle != 0) + if (mWindowHandleThrd != 0) { // lazily call initD3D inside this loop to catch when mWindowHandle has been set, and mGLReady has been set to true // *TODO: Shutdown if this fails when mWindowHandle exists @@ -4869,16 +4870,16 @@ void LLWindowWin32::LLWindowWin32Thread::run() MSG msg; BOOL status; - if (mhDC == 0) + if (mhDCThrd == 0) { LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("w32t - PeekMessage"); - logger.onChange("PeekMessage(", std::hex, mWindowHandle, ")"); - status = PeekMessage(&msg, mWindowHandle, 0, 0, PM_REMOVE); + logger.onChange("PeekMessage(", std::hex, mWindowHandleThrd, ")"); + status = PeekMessage(&msg, mWindowHandleThrd, 0, 0, PM_REMOVE); } else { LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("w32t - GetMessage"); - logger.always("GetMessage(", std::hex, mWindowHandle, ")"); + logger.always("GetMessage(", std::hex, mWindowHandleThrd, ")"); status = GetMessage(&msg, NULL, 0, 0); } if (status > 0) -- cgit v1.2.3 From ee7c3ea792d889b8e852bfa07395848f0d893ebb Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Sat, 27 Jan 2024 02:10:46 +0200 Subject: SL-18721 Shutdown fixes #3 Fix dialog box being blank in task bar --- indra/llwindow/llwindowwin32.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'indra/llwindow') diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp index 0c1ed85477..4b72ade469 100644 --- a/indra/llwindow/llwindowwin32.cpp +++ b/indra/llwindow/llwindowwin32.cpp @@ -3657,6 +3657,9 @@ void LLSplashScreenWin32::showImpl() NULL, // no parent (DLGPROC) LLSplashScreenWin32::windowProc); ShowWindow(mWindow, SW_SHOW); + + // Should set taskbar text without creating a header for the window (caption) + SetWindowTextA(mWindow, "Second Life"); } -- cgit v1.2.3 From 2e5b105dffc41695d0a64c5b55eef7c28da49246 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Wed, 7 Feb 2024 22:50:28 +0200 Subject: SL-18721 Shutdown fixes #4 --- indra/llwindow/llwindowwin32.cpp | 42 +++++++++++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 5 deletions(-) (limited to 'indra/llwindow') diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp index 4b72ade469..8cc8f9c408 100644 --- a/indra/llwindow/llwindowwin32.cpp +++ b/indra/llwindow/llwindowwin32.cpp @@ -352,6 +352,9 @@ struct LLWindowWin32::LLWindowWin32Thread : public LL::ThreadPool void run() override; + // closes queue, wakes thread, waits until thread closes + void wakeAndClose(); + void glReady() { mGLReady = true; @@ -1022,12 +1025,8 @@ void LLWindowWin32::close() mhDC = NULL; mWindowHandle = NULL; - - // Window thread might be waiting for a getMessage(), give it - // a push to enshure it will process destroy_window_handler - kickWindowThread(); - mWindowThread->close(); + mWindowThread->wakeAndClose(); } BOOL LLWindowWin32::isValid() @@ -4940,6 +4939,39 @@ void LLWindowWin32::LLWindowWin32Thread::run() } +void LLWindowWin32::LLWindowWin32Thread::wakeAndClose() +{ + if (!mQueue->isClosed()) + { + LL_DEBUGS("Window") << "closing pool queue" << LL_ENDL; + mQueue->close(); + + // Post a nonsense user message to wake up the thred in + // case it is waiting for a getMessage() + // + // Note that mWindowHandleThrd can change at any moment and isn't thread safe + // but since we aren't writing it, should be safe to use even if value is obsolete + // worst case dead handle gets reused and some new window ignores the message + HWND old_handle = mWindowHandleThrd; + if (old_handle) + { + WPARAM wparam{ 0xB0B0 }; + LL_DEBUGS("Window") << "PostMessage(" << std::hex << old_handle + << ", " << WM_DUMMY_ + << ", " << wparam << ")" << std::dec << LL_ENDL; + PostMessage(old_handle, WM_DUMMY_, wparam, 0x1337); + } + + // now wait for our one thread to die. + for (auto& pair : mThreads) + { + LL_DEBUGS("Window") << "waiting on pool's thread " << pair.first << LL_ENDL; + pair.second.join(); + } + LL_DEBUGS("Window") << "thread pool shutdown complete" << LL_ENDL; + } +} + void LLWindowWin32::post(const std::function& func) { mFunctionQueue.pushFront(func); -- cgit v1.2.3 From fe3be58938ec3a6241d011156cd6ff3607ed1602 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Thu, 29 Feb 2024 21:14:39 +0200 Subject: SL-18721 Shutdown fixes #6 --- indra/llwindow/llwindowwin32.cpp | 183 ++++++++++++++++++++++++--------------- 1 file changed, 113 insertions(+), 70 deletions(-) (limited to 'indra/llwindow') diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp index 8cc8f9c408..0d56d2f6f6 100644 --- a/indra/llwindow/llwindowwin32.cpp +++ b/indra/llwindow/llwindowwin32.cpp @@ -353,7 +353,7 @@ struct LLWindowWin32::LLWindowWin32Thread : public LL::ThreadPool void run() override; // closes queue, wakes thread, waits until thread closes - void wakeAndClose(); + void wakeAndDestroy(); void glReady() { @@ -366,6 +366,9 @@ struct LLWindowWin32::LLWindowWin32Thread : public LL::ThreadPool // initialize D3D (if DXGI cannot be used) void initD3D(); + //clean up DXGI/D3D resources + void cleanupDX(); + // call periodically to update available VRAM void updateVRAMUsage(); @@ -990,43 +993,10 @@ void LLWindowWin32::close() LL_DEBUGS("Window") << "Destroying Window" << LL_ENDL; - mWindowThread->post([this, self = mWindowThread]() - { - if (IsWindow(self->mWindowHandleThrd)) - { - if (self->mhDCThrd) - { - if (!ReleaseDC(self->mWindowHandleThrd, self->mhDCThrd)) - { - LL_WARNS("Window") << "Release of ghDC failed!" << LL_ENDL; - } - } - - // Make sure we don't leave a blank toolbar button. - ShowWindow(self->mWindowHandleThrd, SW_HIDE); - - // This causes WM_DESTROY to be sent *immediately* - if (!destroy_window_handler(self->mWindowHandleThrd)) - { - OSMessageBox(mCallbacks->translateString("MBDestroyWinFailed"), - mCallbacks->translateString("MBShutdownErr"), - OSMB_OK); - } - } - else - { - // Something killed the window while we were busy destroying gl or handle somehow got broken - LL_WARNS("Window") << "Failed to destroy Window, invalid handle!" << LL_ENDL; - } - self->mWindowHandleThrd = NULL; - self->mhDCThrd = NULL; - self->mGLReady = false; - }); - mhDC = NULL; mWindowHandle = NULL; - mWindowThread->wakeAndClose(); + mWindowThread->wakeAndDestroy(); } BOOL LLWindowWin32::isValid() @@ -4771,6 +4741,28 @@ void LLWindowWin32::LLWindowWin32Thread::initD3D() } } +void LLWindowWin32::LLWindowWin32Thread::cleanupDX() +{ + //clean up DXGI/D3D resources + if (mDXGIAdapter) + { + mDXGIAdapter->Release(); + mDXGIAdapter = nullptr; + } + + if (mD3DDevice) + { + mD3DDevice->Release(); + mD3DDevice = nullptr; + } + + if (mD3D) + { + mD3D->Release(); + mD3D = nullptr; + } +} + void LLWindowWin32::LLWindowWin32Thread::updateVRAMUsage() { LL_PROFILE_ZONE_SCOPED; @@ -4918,58 +4910,109 @@ void LLWindowWin32::LLWindowWin32Thread::run() #endif } - //clean up DXGI/D3D resources - if (mDXGIAdapter) + cleanupDX(); +} + +void LLWindowWin32::LLWindowWin32Thread::wakeAndDestroy() +{ + if (mQueue->isClosed()) { - mDXGIAdapter->Release(); - mDXGIAdapter = nullptr; + LL_WARNS() << "Tried to close Queue. Win32 thread Queue already closed." <close(); + + // Post a nonsense user message to wake up the thread in + // case it is waiting for a getMessage() + if (old_handle) { - mD3DDevice->Release(); - mD3DDevice = nullptr; + WPARAM wparam{ 0xB0B0 }; + LL_DEBUGS("Window") << "PostMessage(" << std::hex << old_handle + << ", " << WM_DUMMY_ + << ", " << wparam << ")" << std::dec << LL_ENDL; + PostMessage(old_handle, WM_DUMMY_, wparam, 0x1337); } - if (mD3D) + // There are cases where window will refuse to close, + // can't wait forever on join, check state instead + LLTimer timeout; + timeout.setTimerExpirySec(2.0); + while (!getQueue().done() && !timeout.hasExpired() && mWindowHandleThrd) { - mD3D->Release(); - mD3D = nullptr; + ms_sleep(100); } -} - -void LLWindowWin32::LLWindowWin32Thread::wakeAndClose() -{ - if (!mQueue->isClosed()) + if (getQueue().done() || mWindowHandleThrd == NULL) { - LL_DEBUGS("Window") << "closing pool queue" << LL_ENDL; - mQueue->close(); - - // Post a nonsense user message to wake up the thred in - // case it is waiting for a getMessage() - // - // Note that mWindowHandleThrd can change at any moment and isn't thread safe - // but since we aren't writing it, should be safe to use even if value is obsolete - // worst case dead handle gets reused and some new window ignores the message - HWND old_handle = mWindowHandleThrd; - if (old_handle) + // Window is closed, started closing or is cleaning up + // now wait for our single thread to die. + if (mWindowHandleThrd) { - WPARAM wparam{ 0xB0B0 }; - LL_DEBUGS("Window") << "PostMessage(" << std::hex << old_handle - << ", " << WM_DUMMY_ - << ", " << wparam << ")" << std::dec << LL_ENDL; - PostMessage(old_handle, WM_DUMMY_, wparam, 0x1337); + LL_INFOS("Window") << "Window is closing, waiting on pool's thread to join, time since post: " << timeout.getElapsedSeconds() << "s" << LL_ENDL; + } + else + { + LL_DEBUGS("Window") << "Waiting on pool's thread, time since post: " << timeout.getElapsedSeconds() << "s" << LL_ENDL; } - - // now wait for our one thread to die. for (auto& pair : mThreads) { - LL_DEBUGS("Window") << "waiting on pool's thread " << pair.first << LL_ENDL; pair.second.join(); } - LL_DEBUGS("Window") << "thread pool shutdown complete" << LL_ENDL; } + else + { + // Something suspended window thread, can't afford to wait forever + // so kill thread instead + // Ex: This can happen if user starts dragging window arround (if it + // was visible) or a modal notification pops up + LL_WARNS("Window") << "Window is frozen, couldn't perform clean exit" << LL_ENDL; + + for (auto& pair : mThreads) + { + // very unsafe + TerminateThread(pair.second.native_handle(), 0); + pair.second.detach(); + cleanupDX(); + } + } + LL_DEBUGS("Window") << "thread pool shutdown complete" << LL_ENDL; } void LLWindowWin32::post(const std::function& func) -- cgit v1.2.3 From 1161262029f9619fb02d81575382b64d82d9cd09 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Fri, 15 Mar 2024 00:04:19 +0200 Subject: SL-18721 Restore release behavior Closing window correctly caused a significant amount of logout freezes with no known reproes. Temporarily returning to old behavior were thread was killes without closing window and will reenable in later maints to hopefully get a scenario or at least more data of what is causing the freeze. --- indra/llwindow/llwindowwin32.cpp | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) (limited to 'indra/llwindow') diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp index 0d56d2f6f6..54e5f43e87 100644 --- a/indra/llwindow/llwindowwin32.cpp +++ b/indra/llwindow/llwindowwin32.cpp @@ -351,6 +351,7 @@ struct LLWindowWin32::LLWindowWin32Thread : public LL::ThreadPool LLWindowWin32Thread(); void run() override; + void close() override; // closes queue, wakes thread, waits until thread closes void wakeAndDestroy(); @@ -4562,11 +4563,25 @@ U32 LLWindowWin32::getAvailableVRAMMegabytes() #endif // LL_WINDOWS inline LLWindowWin32::LLWindowWin32Thread::LLWindowWin32Thread() - : LL::ThreadPool("Window Thread", 1, MAX_QUEUE_SIZE, false) + : LL::ThreadPool("Window Thread", 1, MAX_QUEUE_SIZE, true /*should be false, temporary workaround for SL-18721*/) { LL::ThreadPool::start(); } +void LLWindowWin32::LLWindowWin32Thread::close() +{ + if (!mQueue->isClosed()) + { + LL_WARNS() << "Closing window thread without using destroy_window_handler" << LL_ENDL; + LL::ThreadPool::close(); + + // Workaround for SL-18721 in case window closes too early and abruptly + LLSplashScreen::show(); + LLSplashScreen::update("..."); // will be updated later + } +} + + /** * LogChange is to log changes in status while trying to avoid spamming the * log with repeated messages, especially in a tight loop. It refuses to log @@ -4917,7 +4932,8 @@ void LLWindowWin32::LLWindowWin32Thread::wakeAndDestroy() { if (mQueue->isClosed()) { - LL_WARNS() << "Tried to close Queue. Win32 thread Queue already closed." <