diff options
| author | isanae <14251494+isanae@users.noreply.github.com> | 2019-10-29 07:28:12 -0400 |
|---|---|---|
| committer | isanae <14251494+isanae@users.noreply.github.com> | 2019-11-06 07:44:54 -0500 |
| commit | 18b438cf27a552e69e984bfee63187b6471682ab (patch) | |
| tree | 9738198af8236de09387699027ed5a36535561d0 /src | |
| parent | bff5a22f48b933fe9eba3a15497882ddf2a03990 (diff) | |
split to getRunningUSVFSProcesses()
simplified waitForAllUSVFSProcesses() to always get the list of running processes after one process completes
Diffstat (limited to 'src')
| -rw-r--r-- | src/envmodule.cpp | 6 | ||||
| -rw-r--r-- | src/envmodule.h | 2 | ||||
| -rw-r--r-- | src/organizercore.cpp | 220 | ||||
| -rw-r--r-- | src/spawn.cpp | 53 | ||||
| -rw-r--r-- | src/spawn.h | 4 | ||||
| -rw-r--r-- | src/usvfsconnector.cpp | 47 | ||||
| -rw-r--r-- | src/usvfsconnector.h | 2 |
7 files changed, 161 insertions, 173 deletions
diff --git a/src/envmodule.cpp b/src/envmodule.cpp index abbe02e5..160a54fa 100644 --- a/src/envmodule.cpp +++ b/src/envmodule.cpp @@ -511,4 +511,10 @@ DWORD getProcessParentID(DWORD pid) return ppid; } + +DWORD getProcessParentID(HANDLE handle) +{ + return getProcessParentID(GetProcessId(handle)); +} + } // namespace diff --git a/src/envmodule.h b/src/envmodule.h index 212f6f7b..6c0a028d 100644 --- a/src/envmodule.h +++ b/src/envmodule.h @@ -121,7 +121,9 @@ std::vector<Process> getRunningProcesses(); std::vector<Module> getLoadedModules(); QString getProcessName(HANDLE process); + DWORD getProcessParentID(DWORD pid); +DWORD getProcessParentID(HANDLE handle); } // namespace env diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 2a228fda..2a97b998 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -1355,17 +1355,13 @@ bool OrganizerCore::waitForApplication(HANDLE handle, LPDWORD exitCode) if (!Settings::instance().interface().lockGUI()) return true; - ILockedWaitingForProcess* uilock = nullptr; - if (m_MainWindow != nullptr) { - uilock = m_MainWindow->lock(); - } + bool r = false; - ON_BLOCK_EXIT([&] () { - if (m_MainWindow != nullptr) { - m_MainWindow->unlock(); - } }); + withLock([&](auto* uilock) { + r = waitForProcessCompletion(handle, exitCode, uilock); + }); - return waitForProcessCompletion(handle, exitCode, uilock); + return r; } bool OrganizerCore::waitForProcessCompletion( @@ -1387,6 +1383,9 @@ bool OrganizerCore::waitForProcessCompletion( bool OrganizerCore::waitForAllUSVFSProcessesWithLock() { + if (!Settings::instance().interface().lockGUI()) + return true; + bool r = false; withLock([&](auto* uilock) { @@ -1396,178 +1395,81 @@ bool OrganizerCore::waitForAllUSVFSProcessesWithLock() return r; } -bool OrganizerCore::waitForAllUSVFSProcesses(ILockedWaitingForProcess* uilock) +HANDLE getInterestingProcess( + const std::vector<HANDLE>& handles, + const std::vector<QString>& hidden, DWORD preferedParentPid) { - // Certain process names we wish to "hide" for aesthetic reason: - std::vector<QString> hiddenList; - hiddenList.push_back(QFileInfo(QCoreApplication::applicationFilePath()).fileName()); - - bool originalHandle = true; - bool newHandle = true; - bool uiunlocked = false; - - HANDLE handle = findAndOpenAUSVFSProcess(hiddenList, GetCurrentProcessId()); - DWORD* exitCode = nullptr; - DWORD currentPID = 0; - QString processName; - - auto waitForChildUntil = GetTickCount64(); - if (handle != INVALID_HANDLE_VALUE) { - currentPID = GetProcessId(handle); - processName = env::getProcessName(handle); - } - - bool waitingOnHidden = false; - for (QString hide : hiddenList) - if (processName.contains(hide, Qt::CaseInsensitive)) - waitingOnHidden = true; + HANDLE best_match = INVALID_HANDLE_VALUE; + bool best_match_hidden = true; - // The main reason for adding the hidden list is to hide the MO proxy we use to spawn virtualized processes. - // On the one hand we want to display the real executable without it feeling laggy, on the other we don't want - // to requery processes all the time if for some reason we are waiting on hidden processes and find no "unhidden" - // process. For this reason we use exponential backoff and also start with a delibrately low value to improve - // the responsiveness of the initial update - DWORD64 nextHiddenCheck = GetTickCount64(); - DWORD64 nextHiddenCheckDelay = 50; + for (auto handle : handles) { + const QString pname = env::getProcessName(handle); - constexpr DWORD INPUT_EVENT = WAIT_OBJECT_0 + 1; - DWORD res = WAIT_TIMEOUT; - while (handle != INVALID_HANDLE_VALUE && (newHandle || res == WAIT_TIMEOUT || res == INPUT_EVENT)) - { - if (newHandle) { - processName += QString(" (%1)").arg(currentPID); - if (uilock) - uilock->setProcessName(processName); + bool phidden = false; + for (auto h : hidden) + if (pname.contains(h, Qt::CaseInsensitive)) + phidden = true; - log::debug( - "Waiting for {} process completion: {}", - (originalHandle ? "spawned" : "usvfs"), processName); + bool pprefered = preferedParentPid && env::getProcessParentID(handle) == preferedParentPid; - newHandle = false; + if (best_match == INVALID_HANDLE_VALUE || best_match_hidden || (!phidden && pprefered)) { + best_match = handle; + best_match_hidden = phidden; } - // Wait for a an event on the handle, a key press, mouse click or timeout - res = MsgWaitForMultipleObjects(1, &handle, FALSE, 200, QS_KEY | QS_MOUSEBUTTON); - if (res == WAIT_FAILED) { - log::warn("Failed waiting for process completion : MsgWaitForMultipleObjects WAIT_FAILED {}", GetLastError()); - break; - } + if (!phidden && pprefered) + return best_match; + } - // keep processing events so the app doesn't appear dead - QCoreApplication::sendPostedEvents(); - QCoreApplication::processEvents(); + return best_match; +} - if (uilock && uilock->unlockForced()) { - uiunlocked = true; +bool OrganizerCore::waitForAllUSVFSProcesses(ILockedWaitingForProcess* uilock) +{ + // Certain process names we wish to "hide" for aesthetic reason: + std::vector<QString> hiddenList; + hiddenList.push_back(QFileInfo(QCoreApplication::applicationFilePath()).fileName()); + + for (;;) { + const auto handles = getRunningUSVFSProcesses(); + if (handles.empty()) { break; } - if (res == WAIT_OBJECT_0) { - // process we were waiting on has completed - if (originalHandle && exitCode && !::GetExitCodeProcess(handle, exitCode)) - log::warn("Failed getting exit code of complete process: {}", GetLastError()); - CloseHandle(handle); - handle = INVALID_HANDLE_VALUE; - originalHandle = false; - // if the previous process spawned a child process and immediately exits we may miss it if we check immediately - waitForChildUntil = GetTickCount64() + 800; - } + const auto interesting = getInterestingProcess( + handles, hiddenList, GetCurrentProcessId()); - // search for another process to wait on if either: - // 1. we just completed waiting for a process and need to find/wait for an inject child - // 2. we are currently waiting on a hidden process so periodically check if there is a non-hidden process to wait on - bool firstIteration = true; - while ((handle == INVALID_HANDLE_VALUE && GetTickCount64() <= waitForChildUntil) - || (waitingOnHidden && GetTickCount64() >= nextHiddenCheck)) - { - if (firstIteration) - firstIteration = false; - else { - QThread::msleep(200); - QCoreApplication::sendPostedEvents(); - QCoreApplication::processEvents(); - } + if (uilock) { + const DWORD pid = ::GetProcessId(interesting); + const QString processName = QString("%1 (%2)") + .arg(env::getProcessName(interesting)) + .arg(pid); - // search if there is another usvfs process active - handle = findAndOpenAUSVFSProcess(hiddenList, currentPID); - waitingOnHidden = false; - newHandle = handle != INVALID_HANDLE_VALUE; - if (newHandle) { - currentPID = GetProcessId(handle); - processName = env::getProcessName(handle); - for (QString hide : hiddenList) - if (processName.contains(hide, Qt::CaseInsensitive)) - waitingOnHidden = true; - } - if (waitingOnHidden) { - nextHiddenCheck = GetTickCount64() + nextHiddenCheckDelay; - nextHiddenCheckDelay = std::min(nextHiddenCheckDelay * 2, (DWORD64) 2000); - } - else { - nextHiddenCheck = GetTickCount64(); - nextHiddenCheckDelay = 200; - } + uilock->setProcessName(processName); } - } - if (res == WAIT_OBJECT_0) - log::debug("Waiting for process completion successfull"); - else if (uiunlocked) - log::debug("Waiting for process completion aborted by UI"); - else - log::debug("Waiting for process completion not successfull: {}", res); + const auto r = spawn::waitForProcess(interesting, nullptr, uilock); - if (handle != INVALID_HANDLE_VALUE) - ::CloseHandle(handle); - - return res == WAIT_OBJECT_0; -} - -HANDLE OrganizerCore::findAndOpenAUSVFSProcess(const std::vector<QString>& hiddenList, DWORD preferedParentPid) { - // for practical reasons a querySize of 1 is probably enough, we use a larger query as a heuristics - // to find a more "aesthetic injected processes (attempting to comply to hiddenList and preferedParentPid) - constexpr size_t querySize = 100; - DWORD pids[querySize]; - size_t found = querySize; - if (!::GetVFSProcessList(&found, pids)) { - log::warn("Failed seeking USVFS processes : GetVFSProcessList failed?!"); - return INVALID_HANDLE_VALUE; - } - - HANDLE best_match = INVALID_HANDLE_VALUE; - bool best_match_hidden = true; - for (size_t i = 0; i < found; ++i) { - if (pids[i] == GetCurrentProcessId()) - continue; // obviously don't wait for MO process - - HANDLE handle = ::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION | SYNCHRONIZE, FALSE, pids[i]); - if (handle == INVALID_HANDLE_VALUE) { - log::warn("Failed opening USVFS process {}: OpenProcess failed {}", pids[i], GetLastError()); - continue; - } - - QString pname = env::getProcessName(handle); - bool phidden = false; - for (auto hide : hiddenList) - if (pname.contains(hide, Qt::CaseInsensitive)) - phidden = true; + switch (r) + { + case spawn::WaitResults::Completed: + // this process is completed, check for others + break; - bool pprefered = preferedParentPid && env::getProcessParentID(pids[i]) == preferedParentPid; + case spawn::WaitResults::Unlocked: + // force unlocked + log::debug("waiting for process completion aborted by UI"); + return true; - if (best_match == INVALID_HANDLE_VALUE || best_match_hidden || (!phidden && pprefered)) { - if (best_match != INVALID_HANDLE_VALUE) - CloseHandle(best_match); - best_match = handle; - best_match_hidden = phidden; + case spawn::WaitResults::Error: // fall-through + default: + log::debug("waiting for process completion not successful"); + return false; } - else - CloseHandle(handle); - - if (!phidden && pprefered) - return best_match; } - return best_match; + log::debug("Waiting for process completion successful"); + return true; } bool OrganizerCore::onAboutToRun( diff --git a/src/spawn.cpp b/src/spawn.cpp index f0b3b2c7..1003024f 100644 --- a/src/spawn.cpp +++ b/src/spawn.cpp @@ -1094,7 +1094,8 @@ FileExecutionContext getFileExecutionContext( return {{}, {}, FileExecutionTypes::Other};
}
-WaitResults waitForProcess(HANDLE handle, DWORD* exitCode, ILockedWaitingForProcess* uilock)
+WaitResults waitForProcess(
+ HANDLE handle, DWORD* exitCode, ILockedWaitingForProcess* uilock)
{
if (handle == INVALID_HANDLE_VALUE) {
return WaitResults::Error;
@@ -1108,37 +1109,62 @@ WaitResults waitForProcess(HANDLE handle, DWORD* exitCode, ILockedWaitingForProc if (uilock)
uilock->setProcessName(processName);
- constexpr DWORD INPUT_EVENT = WAIT_OBJECT_0 + 1;
- DWORD res = WAIT_TIMEOUT;
-
log::debug(
"waiting for process completion '{}' ({})",
processName, pid);
+ std::vector<HANDLE> handles;
+ handles.push_back(handle);
+
+ std::vector<DWORD> exitCodes;
+
+ const auto r = waitForProcesses(handles, exitCodes, uilock);
+ if (exitCode && !exitCodes.empty()) {
+ *exitCode = exitCodes[0];
+ }
+
+ return r;
+}
+
+WaitResults waitForProcesses(
+ const std::vector<HANDLE>& handles, std::vector<DWORD>& exitCodes,
+ ILockedWaitingForProcess* uilock)
+{
+ if (handles.empty()) {
+ return WaitResults::Completed;
+ }
+
+ const auto WAIT_OBJECT_N = static_cast<DWORD>(WAIT_OBJECT_0 + handles.size());
+
for (;;) {
// Wait for a an event on the handle, a key press, mouse click or timeout
const auto res = MsgWaitForMultipleObjects(
- 1, &handle, FALSE, 50, QS_KEY | QS_MOUSEBUTTON);
+ static_cast<DWORD>(handles.size()), &handles[0],
+ TRUE, 50, QS_KEY | QS_MOUSEBUTTON);
if (res == WAIT_FAILED) {
// error
const auto e = ::GetLastError();
log::error(
- "failed waiting for process completion '{}' ({}), {}",
- processName, pid, formatSystemMessage(e));
+ "failed waiting for process completion, {}", formatSystemMessage(e));
return WaitResults::Error;
- } else if (res == WAIT_OBJECT_0) {
+ } else if (res >= WAIT_OBJECT_0 && res < WAIT_OBJECT_N) {
// completed
- log::debug("process '{}' ({}) completed", processName, pid);
+ exitCodes.resize(handles.size());
+ std::fill(exitCodes.begin(), exitCodes.end(), 0);
+
+ for (std::size_t i=0; i<handles.size(); ++i) {
+ DWORD exitCode = 0;
- if (exitCode) {
- if (!::GetExitCodeProcess(handle, exitCode)) {
+ if (::GetExitCodeProcess(handles[i], &exitCode)) {
+ exitCodes[i] = exitCode;
+ } else {
const auto e = ::GetLastError();
log::warn(
- "failed to get exit code of process '{}' ({}): {}",
- processName, pid, formatSystemMessage(e));
+ "failed to get exit code of process, {}",
+ formatSystemMessage(e));
}
}
@@ -1150,7 +1176,6 @@ WaitResults waitForProcess(HANDLE handle, DWORD* exitCode, ILockedWaitingForProc QCoreApplication::processEvents();
if (uilock && uilock->unlockForced()) {
- log::debug("waiting for process '{}' ({}) aborted by UI", processName, pid);
return WaitResults::Unlocked;
}
}
diff --git a/src/spawn.h b/src/spawn.h index 441cad2c..6b947f2f 100644 --- a/src/spawn.h +++ b/src/spawn.h @@ -136,6 +136,10 @@ enum class WaitResults WaitResults waitForProcess(
HANDLE handle, DWORD* exitCode, ILockedWaitingForProcess* uilock);
+WaitResults waitForProcesses(
+ const std::vector<HANDLE>& handles, std::vector<DWORD>& exitCodes,
+ ILockedWaitingForProcess* uilock);
+
} // namespace
diff --git a/src/usvfsconnector.cpp b/src/usvfsconnector.cpp index 311c6dd3..3dba3efc 100644 --- a/src/usvfsconnector.cpp +++ b/src/usvfsconnector.cpp @@ -20,6 +20,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>. #include "usvfsconnector.h" #include "settings.h" #include "organizercore.h" +#include "envmodule.h" #include "shared/util.h" #include <memory> #include <sstream> @@ -256,3 +257,49 @@ void UsvfsConnector::updateForcedLibraries(const QList<MOBase::ExecutableForcedL } } } + + +std::vector<HANDLE> getRunningUSVFSProcesses() +{ + std::vector<DWORD> pids; + + { + size_t count = 0; + DWORD* buffer = nullptr; + if (!::GetVFSProcessList2(&count, &buffer)) { + log::error("failed to get usvfs process list"); + return {}; + } + + if (buffer) { + pids.assign(buffer, buffer + count); + std::free(buffer); + } + } + + const auto thisPid = GetCurrentProcessId(); + std::vector<HANDLE> v; + + for (auto&& pid : pids) { + if (pid == thisPid) { + continue; // obviously don't wait for MO process + } + + HANDLE handle = ::OpenProcess( + PROCESS_QUERY_LIMITED_INFORMATION | SYNCHRONIZE, FALSE, pid); + + if (handle == INVALID_HANDLE_VALUE) { + const auto e = GetLastError(); + + log::warn( + "failed to open usvfs process {}: {}", + pid, formatSystemMessage(e)); + + continue; + } + + v.push_back(handle); + } + + return v; +} diff --git a/src/usvfsconnector.h b/src/usvfsconnector.h index d0071678..5982778b 100644 --- a/src/usvfsconnector.h +++ b/src/usvfsconnector.h @@ -103,4 +103,6 @@ private: CrashDumpsType crashDumpsType(int type); +std::vector<HANDLE> getRunningUSVFSProcesses(); + #endif // USVFSCONNECTOR_H |
