mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-11-04 21:33:58 +01:00
PathBrowser: Abandon a network listing when navigating away
PathBrowser lists on one thread, so going up from the network folder waited for its listing to finish (up to 8 seconds) before the drive list showed. Pass GetFilesInDir a cancel flag that PathBrowser sets whenever the path changes, or on destruction, and check it while waiting. Also don't probe the drives in the drive list for game folders. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
c14fc1d1d0
commit
adfbd7a259
5 files changed
+29
-10
No files matched your search
@@ -145,10 +145,11 @@ struct NetListing {
|
||||
// Network calls can block for a long time (half a minute for the shell's network folder, even after it
|
||||
// has found everything), and can't be cancelled. So run them on a thread of their own, and abandon it
|
||||
// when the deadline passes, or when it has found something and then gone quiet for a while.
|
||||
static bool ListNetworkWithTimeout(const Path &directory, std::function<bool(NetListing &)> func, std::vector<FileInfo> *files) {
|
||||
static bool ListNetworkWithTimeout(const Path &directory, std::function<bool(NetListing &)> func, std::vector<FileInfo> *files, const bool *cancel) {
|
||||
// Servers turn up in the network folder after 2 to 6 seconds.
|
||||
constexpr auto timeout = std::chrono::seconds(8);
|
||||
constexpr auto quietTime = std::chrono::seconds(1);
|
||||
constexpr auto cancelPollTime = std::chrono::milliseconds(20);
|
||||
auto listing = std::make_shared<NetListing>();
|
||||
std::thread([listing, func] {
|
||||
SetCurrentThreadName("NetListing");
|
||||
@@ -166,11 +167,15 @@ static bool ListNetworkWithTimeout(const Path &directory, std::function<bool(Net
|
||||
if (!listing->files.empty()) {
|
||||
wakeup = std::min(deadline, listing->lastAdd + quietTime);
|
||||
}
|
||||
if (std::chrono::steady_clock::now() >= wakeup) {
|
||||
if (cancel && *cancel) {
|
||||
return false;
|
||||
}
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
if (now >= wakeup) {
|
||||
INFO_LOG(Log::IO, "Stopped waiting for the listing of %s, found %d entries", directory.c_str(), (int)listing->files.size());
|
||||
break;
|
||||
}
|
||||
listing->cond.wait_until(guard, wakeup);
|
||||
listing->cond.wait_until(guard, cancel ? std::min(wakeup, now + cancelPollTime) : wakeup);
|
||||
}
|
||||
files->insert(files->end(), listing->files.begin(), listing->files.end());
|
||||
return listing->success || !listing->files.empty();
|
||||
@@ -387,7 +392,7 @@ std::vector<File::FileInfo> ApplyFilter(std::vector<File::FileInfo> files, const
|
||||
return files;
|
||||
}
|
||||
|
||||
bool GetFilesInDir(const Path &directory, std::vector<FileInfo> *files, const char *filter, int flags, std::string_view prefix) {
|
||||
bool GetFilesInDir(const Path &directory, std::vector<FileInfo> *files, const char *filter, int flags, std::string_view prefix, const bool *cancel) {
|
||||
if (LOG_IO) {
|
||||
INFO_LOG(Log::IO, "GetFilesInDir '%s' (ext %s, prefix %.*s)", directory.ToVisualString().c_str(), filter, (int)prefix.size(), prefix.data());
|
||||
}
|
||||
@@ -452,11 +457,11 @@ bool GetFilesInDir(const Path &directory, std::vector<FileInfo> *files, const ch
|
||||
if (server.empty()) {
|
||||
success = ListNetworkWithTimeout(directory, [prefixCopy](NetListing &listing) {
|
||||
return GetNetworkServers(prefixCopy, listing);
|
||||
}, files);
|
||||
}, files, cancel);
|
||||
} else {
|
||||
success = ListNetworkWithTimeout(directory, [directory, server, prefixCopy](NetListing &listing) {
|
||||
return GetUNCServerShares(directory, server, prefixCopy, listing);
|
||||
}, files);
|
||||
}, files, cancel);
|
||||
}
|
||||
std::sort(files->begin(), files->end());
|
||||
return success;
|
||||
|
||||
@@ -41,7 +41,8 @@ enum {
|
||||
};
|
||||
|
||||
// Note: extensionFilter is ignored for directories, so you can recurse.
|
||||
bool GetFilesInDir(const Path &directory, std::vector<FileInfo> *files, const char *extensionFilter = nullptr, int flags = 0, std::string_view prefix = std::string_view());
|
||||
// cancel is only checked while waiting for a network listing (Windows), which can take seconds.
|
||||
bool GetFilesInDir(const Path &directory, std::vector<FileInfo> *files, const char *extensionFilter = nullptr, int flags = 0, std::string_view prefix = std::string_view(), const bool *cancel = nullptr);
|
||||
std::vector<File::FileInfo> ApplyFilter(std::vector<File::FileInfo> files, const char *extensionFilter, std::string_view prefix);
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
@@ -115,6 +115,7 @@ PathBrowser::~PathBrowser() {
|
||||
std::unique_lock<std::mutex> guard(pendingLock_);
|
||||
pendingCancel_ = true;
|
||||
pendingStop_ = true;
|
||||
listingAbort_ = true;
|
||||
pendingCond_.notify_all();
|
||||
}
|
||||
if (pendingThread_.joinable()) {
|
||||
@@ -136,6 +137,10 @@ void PathBrowser::RestrictToRoot(const Path &root) {
|
||||
}
|
||||
|
||||
void PathBrowser::HandlePath() {
|
||||
// Asking for the path that's being listed again shouldn't restart it.
|
||||
if (path_ != pendingPath_) {
|
||||
listingAbort_ = true;
|
||||
}
|
||||
if (!path_.empty() && path_.ToString()[0] == '!') {
|
||||
if (pendingActive_) {
|
||||
pendingCancel_ = true;
|
||||
@@ -171,6 +176,7 @@ void PathBrowser::HandlePath() {
|
||||
break;
|
||||
}
|
||||
lastPath = pendingPath_;
|
||||
listingAbort_ = false;
|
||||
if (lastPath.Type() == PathType::HTTP) {
|
||||
std::string userAgentCopy = userAgent_;
|
||||
guard.unlock();
|
||||
@@ -184,8 +190,13 @@ void PathBrowser::HandlePath() {
|
||||
} else {
|
||||
guard.unlock();
|
||||
results.clear();
|
||||
bool tempSuccess = File::GetFilesInDir(lastPath, &results, nullptr);
|
||||
bool tempSuccess = File::GetFilesInDir(lastPath, &results, nullptr, 0, std::string_view(), &listingAbort_);
|
||||
guard.lock();
|
||||
if (listingAbort_) {
|
||||
// We navigated away, maybe and back again, in which case this lists it again.
|
||||
lastPath.clear();
|
||||
continue;
|
||||
}
|
||||
success_ = tempSuccess;
|
||||
if (!success_) {
|
||||
WARN_LOG(Log::IO, "PathBrowser: Failed to list directory: %s", lastPath.c_str());
|
||||
|
||||
@@ -68,6 +68,8 @@ private:
|
||||
bool pendingActive_ = false;
|
||||
bool pendingCancel_ = false;
|
||||
bool pendingStop_ = false;
|
||||
// Abandons a slow listing (network) when the path changes.
|
||||
bool listingAbort_ = false;
|
||||
bool ready_ = false;
|
||||
bool success_ = true;
|
||||
};
|
||||
+2
-2
@@ -1133,8 +1133,8 @@ void GameBrowser::Refresh() {
|
||||
path_.GetListing(fileInfo, "iso:cso:chd:pbp:elf:prx:ppdmp:cue:ccd:mds:bin:img:");
|
||||
// PS1 discs: a .bin or .img shows up as its cue sheet or .ccd, when it has one.
|
||||
RemoveDescribedDiscImages(&fileInfo);
|
||||
// Servers aren't games, and probing one that's gone away can take seconds.
|
||||
const bool probeDirs = !path_.GetPath().IsNetworkRoot();
|
||||
// Drives and servers aren't games, and probing a server that's gone away can take seconds.
|
||||
const bool probeDirs = !path_.GetPath().IsRoot() && !path_.GetPath().IsNetworkRoot();
|
||||
for (size_t i = 0; i < fileInfo.size(); i++) {
|
||||
bool isGame = !fileInfo[i].isDirectory;
|
||||
bool isSaveData = false;
|
||||
|
||||
Reference in new issue
Block a user