From d489a97e490ca8c7efe47fc82ec157a641f32692 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 23 Aug 2026 18:19:16 +0200 Subject: [PATCH] GameInfoCache: Odds and ends GameInfoTex::Clear() only reset dataLoaded when there was data to clear, but several paths deliberately set it on a file that turned out not to exist (the ARCHIVE_ZIP case, the "no icon" fallback). Those kept dataLoaded across a Clear(), so FinishPendingTextureLoads stamped timeLoaded again and the tex read as permanently Failed(). PurgeType slept 10ms even when it had nothing to retry. Fix three comments that no longer described the code: Clear() doesn't start a thread, Priority() no longer calls GetFileLoader(), and the work item's destructor doesn't touch the flags - Run() has to mark them itself, which is worth stating since missing it strands them in pendingFlags for good. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01FzzCUp8y1ahgVueb1Cq92Y --- UI/GameInfoCache.cpp | 22 ++++++++++++---------- UI/GameInfoCache.h | 2 +- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/UI/GameInfoCache.cpp b/UI/GameInfoCache.cpp index c9be03dd43..881f8d550e 100644 --- a/UI/GameInfoCache.cpp +++ b/UI/GameInfoCache.cpp @@ -50,10 +50,11 @@ GameInfoCache *g_gameInfoCache; void GameInfoTex::Clear() { - if (!data.empty()) { - data.clear(); - dataLoaded = false; - } + data.clear(); + // Note: has to be reset even when data was already empty - plenty of paths set dataLoaded on a + // file that turned out not to exist, and leaving it set makes FinishPendingTextureLoads stamp + // timeLoaded again, so the tex reads as permanently Failed(). + dataLoaded = false; if (texture) { texture->Release(); texture = nullptr; @@ -338,8 +339,7 @@ bool GameInfo::CreateLoader() { std::shared_ptr GameInfo::GetFileLoader() { if (filePath_.empty()) { - // Happens when workqueue tries to figure out priorities, - // because Priority() calls GetFileLoader()... gnarly. + // Defensive - don't try to construct a loader for nothing. Just hand back whatever we have. return fileLoader; } @@ -557,10 +557,10 @@ public: } void Run() override { - // An early-return will result in the destructor running, where we can set - // flags like working and pending. + // Every exit from here has to MarkReadyNoLock(flags_) - otherwise those bits stay in + // pendingFlags forever and GetInfo() will never ask for them again. if (!info_->CreateLoader() || !info_->GetFileLoader()) { - // Mark everything requested as done, so + // Mark everything requested as done, so the caller can handle the missing data. std::unique_lock lock(info_->lock); info_->MarkReadyNoLock(flags_); ERROR_LOG(Log::Loader, "Failed getting game info for %s", info_->GetFilePath().ToVisualString().c_str()); @@ -1091,7 +1091,9 @@ void GameInfoCache::PurgeType(IdentifiedFileType fileType) { } } - sleep_ms(10, "game-info-cache-purge-poll"); + if (retry) { + sleep_ms(10, "game-info-cache-purge-poll"); + } } while (retry); } diff --git a/UI/GameInfoCache.h b/UI/GameInfoCache.h index dae51138c8..50e4dde834 100644 --- a/UI/GameInfoCache.h +++ b/UI/GameInfoCache.h @@ -217,7 +217,7 @@ public: GameInfoCache(); ~GameInfoCache(); - // This creates a background worker thread! + // Cancels in-flight loads and drops everything, textures included. Main thread only. void Clear(); void PurgeType(IdentifiedFileType fileType);