From 26a685187e321040fd85c6eb71d34ec18377d1ca Mon Sep 17 00:00:00 2001 From: Kelsi Date: Wed, 25 Feb 2026 13:37:09 -0800 Subject: [PATCH] Fix /logout hang caused by blocking worker thread joins MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit unloadAll() joins worker threads which blocks if they're mid-tile (prepareTile can take seconds for heavy ADTs). Replace with softReset() which clears tile data, queues, and water surfaces without stopping worker threads — workers find empty queues and idle naturally. --- include/rendering/terrain_manager.hpp | 1 + src/core/application.cpp | 5 +++-- src/rendering/terrain_manager.cpp | 26 ++++++++++++++++++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/include/rendering/terrain_manager.hpp b/include/rendering/terrain_manager.hpp index da99ed53..ee06eb30 100644 --- a/include/rendering/terrain_manager.hpp +++ b/include/rendering/terrain_manager.hpp @@ -212,6 +212,7 @@ public: * Unload all tiles */ void unloadAll(); + void softReset(); // Clear tile data without stopping worker threads (non-blocking) /** * Precache a set of tiles (for taxi routes, etc.) diff --git a/src/core/application.cpp b/src/core/application.cpp index e7d7f656..994813c6 100644 --- a/src/core/application.cpp +++ b/src/core/application.cpp @@ -661,9 +661,10 @@ void Application::logoutToLogin() { if (auto* m2 = renderer->getM2Renderer()) { m2->clear(); } - // Unload all terrain tiles + water surfaces so next world entry starts fresh + // Clear terrain tile tracking + water surfaces so next world entry starts fresh. + // Use softReset() instead of unloadAll() to avoid blocking on worker thread joins. if (auto* terrain = renderer->getTerrainManager()) { - terrain->unloadAll(); + terrain->softReset(); } if (auto* questMarkers = renderer->getQuestMarkerRenderer()) { questMarkers->clear(); diff --git a/src/rendering/terrain_manager.cpp b/src/rendering/terrain_manager.cpp index afd50824..3554b3d3 100644 --- a/src/rendering/terrain_manager.cpp +++ b/src/rendering/terrain_manager.cpp @@ -1296,6 +1296,32 @@ void TerrainManager::unloadAll() { } } +void TerrainManager::softReset() { + // Clear queues (workers may still be running — they'll find empty queues) + { + std::lock_guard lock(queueMutex); + loadQueue.clear(); + while (!readyQueue.empty()) readyQueue.pop(); + } + pendingTiles.clear(); + finalizingTiles_.clear(); + placedDoodadIds.clear(); + + LOG_INFO("Soft-resetting terrain (clearing tiles + water, workers stay alive)"); + loadedTiles.clear(); + failedTiles.clear(); + + currentTile = {-1, -1}; + lastStreamTile = {-1, -1}; + + if (terrainRenderer) { + terrainRenderer->clear(); + } + if (waterRenderer) { + waterRenderer->clear(); + } +} + TileCoord TerrainManager::worldToTile(float glX, float glY) const { auto [tileX, tileY] = core::coords::worldToTile(glX, glY); return {tileX, tileY};