diff --git a/UI/GameInfoCache.cpp b/UI/GameInfoCache.cpp index 2a58c6365b..4380c6b37a 100644 --- a/UI/GameInfoCache.cpp +++ b/UI/GameInfoCache.cpp @@ -298,6 +298,11 @@ std::string GameInfo::GetTitle() { return title; } +bool GameInfo::IsPending() { + lock_guard guard(lock); + return pending; +} + void GameInfo::SetTitle(const std::string &newTitle) { lock_guard guard(lock); title = newTitle; @@ -581,8 +586,11 @@ handleELF: info_->saveDataSize = info_->GetSaveDataSizeInBytes(); info_->installDataSize = info_->GetInstallDataSizeInBytes(); } - info_->pending = false; + info_->DisposeFileLoader(); + + lock_guard lock(info_->lock); + info_->pending = false; // ILOG("Completed writing info for %s", info_->GetTitle().c_str()); } @@ -703,8 +711,15 @@ void GameInfoCache::PurgeType(IdentifiedFileType fileType) { } void GameInfoCache::WaitUntilDone(GameInfo *info) { - // Hack - should really wait specifically for that item. - gameInfoWQ_->WaitUntilDone(); + while (info->IsPending()) { + if (gameInfoWQ_->WaitUntilDone(false)) { + // A true return means everything finished, so bail out. + // This way even if something gets stuck, we won't hang. + break; + } + + // Otherwise, wait again if it's not done... + } } @@ -743,12 +758,12 @@ again: { lock_guard lock(info->lock); info->wantFlags |= wantFlags; + info->pending = true; } GameInfoWorkItem *item = new GameInfoWorkItem(gamePath, info); gameInfoWQ_->Add(item); - info->pending = true; info_[gamePath] = info; return info; } diff --git a/UI/GameInfoCache.h b/UI/GameInfoCache.h index 366c6c1825..1cea1ce090 100644 --- a/UI/GameInfoCache.h +++ b/UI/GameInfoCache.h @@ -119,6 +119,8 @@ public: std::string GetTitle(); void SetTitle(const std::string &newTitle); + bool IsPending(); + // Hold this when reading or writing from the GameInfo. // Don't need to hold it when just passing around the pointer, // and obviously also not when creating it and holding the only pointer diff --git a/ext/native/thread/prioritizedworkqueue.cpp b/ext/native/thread/prioritizedworkqueue.cpp index 421d08b92d..8d6d7561cd 100644 --- a/ext/native/thread/prioritizedworkqueue.cpp +++ b/ext/native/thread/prioritizedworkqueue.cpp @@ -33,28 +33,45 @@ void PrioritizedWorkQueue::Flush() { ILOG("Flushed %d un-executed tasks", flush_count); } -void PrioritizedWorkQueue::WaitUntilDone() { - if (queue_.empty()) - return; - // This could be made more elegant.. - while (true) { - bool empty; - { - lock_guard guard(mutex_); - empty = queue_.empty() && !working_; - } - if (empty) { - break; - } - sleep_ms(10); +bool PrioritizedWorkQueue::WaitUntilDone(bool all) { + // We'll lock drain this entire time, so make sure you follow that lock ordering. + lock_guard guard(drainMutex_); + if (AllItemsDone()) { + return true; } + + while (!AllItemsDone()) { + drain_.wait(drainMutex_); + if (!all) { + // Return whether empty or not, something just drained. + return AllItemsDone(); + } + } + + return true; } +void PrioritizedWorkQueue::NotifyDrain() { + lock_guard guard(drainMutex_); + drain_.notify_one(); +} + +bool PrioritizedWorkQueue::AllItemsDone() { + lock_guard guard(mutex_); + return queue_.empty() && !working_; +} // The worker should simply call this in a loop. Will block when appropriate. PrioritizedWorkQueueItem *PrioritizedWorkQueue::Pop() { + { + lock_guard guard(mutex_); + working_ = false; // The thread only calls Pop if it's done. + } + + // Important: make sure mutex_ is not locked while draining. + NotifyDrain(); + lock_guard guard(mutex_); - working_ = false; // The thread only calls Pop if it's done. if (done_) { return 0; } diff --git a/ext/native/thread/prioritizedworkqueue.h b/ext/native/thread/prioritizedworkqueue.h index 9ebfd21df0..5d3acea216 100644 --- a/ext/native/thread/prioritizedworkqueue.h +++ b/ext/native/thread/prioritizedworkqueue.h @@ -34,17 +34,22 @@ public: void Flush(); bool Done() { return done_; } void Stop(); - void WaitUntilDone(); + bool WaitUntilDone(bool all = true); bool IsWorking() { return working_; } private: + void NotifyDrain(); + bool AllItemsDone(); + bool done_; bool working_; recursive_mutex mutex_; + recursive_mutex drainMutex_; condition_variable notEmpty_; + condition_variable drain_; std::vector queue_;