#include #include #include #include "Common/File/FileUtil.h" #include "Common/Thread/ThreadUtil.h" #include "Common/Log.h" #include "Common/TimeUtil.h" #include "Core/Loaders.h" #include "Core/Util/RecentFiles.h" #include "Core/Config.h" // Not in Config.h because it's #included a lot. std::mutex recentIsosLock; std::mutex recentIsosThreadLock; std::thread recentIsosThread; bool recentIsosThreadPending = false; std::vector recentIsos; std::vector GetRecentIsos() { std::lock_guard guard(recentIsosLock); return recentIsos; } bool HasRecentIsos() { std::lock_guard guard(recentIsosLock); return !recentIsos.empty(); } void ClearRecentIsos() { ResetRecentIsosThread(); std::lock_guard guard(recentIsosLock); recentIsos.clear(); } void ResetRecentIsosThread() { std::lock_guard guard(recentIsosThreadLock); if (recentIsosThreadPending && recentIsosThread.joinable()) recentIsosThread.join(); } void SetRecentIsosThread(std::function f) { std::lock_guard guard(recentIsosThreadLock); if (recentIsosThreadPending && recentIsosThread.joinable()) recentIsosThread.join(); recentIsosThread = std::thread(f); recentIsosThreadPending = true; } void LoadRecentIsos(const Section *recent, int maxRecent) { ResetRecentIsosThread(); std::lock_guard guard(recentIsosLock); recentIsos.clear(); for (int i = 0; i < maxRecent; i++) { char keyName[64]; std::string fileName; snprintf(keyName, sizeof(keyName), "FileName%d", i); if (recent->Get(keyName, &fileName, "") && !fileName.empty()) { recentIsos.push_back(fileName); } } } void SaveRecentIsos(Section *recent, int maxRecent) { ResetRecentIsosThread(); for (int i = 0; i < maxRecent; i++) { char keyName[64]; snprintf(keyName, sizeof(keyName), "FileName%d", i); std::lock_guard guard(recentIsosLock); if (i < (int)recentIsos.size()) { recent->Set(keyName, recentIsos[i]); } else { recent->Delete(keyName); // delete the nonexisting FileName } } } void RemoveRecentResolved(const std::string &resolvedFilename) { ResetRecentIsosThread(); std::lock_guard guard(recentIsosLock); auto iter = std::remove_if(recentIsos.begin(), recentIsos.end(), [resolvedFilename](const auto &str) { const std::string recent = File::ResolvePath(str); return resolvedFilename == recent; }); // remove_if is weird. recentIsos.erase(iter, recentIsos.end()); } void AddRecent(const std::string &filename, int maxRecent) { std::string resolvedFilename = File::ResolvePath(filename); RemoveRecentResolved(resolvedFilename); ResetRecentIsosThread(); std::lock_guard guard(recentIsosLock); recentIsos.insert(recentIsos.begin(), resolvedFilename); if ((int)recentIsos.size() > maxRecent) recentIsos.resize(maxRecent); } void RemoveRecent(const std::string &filename) { std::string resolvedFilename = File::ResolvePath(filename); RemoveRecentResolved(resolvedFilename); } void CleanRecentIsos() { SetRecentIsosThread([] { SetCurrentThreadName("RecentISOs"); AndroidJNIThreadContext jniContext; // destructor detaches double startTime = time_now_d(); std::lock_guard guard(recentIsosLock); std::vector cleanedRecent; if (recentIsos.empty()) { INFO_LOG(Log::Loader, "No recents list found."); } for (size_t i = 0; i < recentIsos.size(); i++) { bool exists = false; Path path = Path(recentIsos[i]); switch (path.Type()) { case PathType::CONTENT_URI: case PathType::NATIVE: exists = File::Exists(path); if (!exists) { if (TryUpdateSavedPath(&path)) { exists = File::Exists(path); INFO_LOG(Log::Loader, "Exists=%d when checking updated path: %s", exists, path.c_str()); } } break; default: FileLoader *loader = ConstructFileLoader(path); exists = loader->ExistsFast(); delete loader; break; } if (exists) { std::string pathStr = path.ToString(); // Make sure we don't have any redundant items. auto duplicate = std::find(cleanedRecent.begin(), cleanedRecent.end(), pathStr); if (duplicate == cleanedRecent.end()) { cleanedRecent.push_back(pathStr); } } else { DEBUG_LOG(Log::Loader, "Removed %s from recent. errno=%d", path.c_str(), errno); } } double recentTime = time_now_d() - startTime; if (recentTime > 0.1) { INFO_LOG(Log::System, "CleanRecent took %0.2f", recentTime); } recentIsos = cleanedRecent; }); }