refactor: Use WaitForSingleObject instead of sleep for Windows process shutdown

This commit is contained in:
Nick Bolton 2025-02-24 16:37:54 +00:00
parent a99d408be1
commit f46474c9f2

View file

@ -123,47 +123,53 @@ void MSWindowsProcess::shutdown(HANDLE handle, DWORD pid, int timeout)
LOG_DEBUG("sending close event to close process gracefully"); LOG_DEBUG("sending close event to close process gracefully");
HANDLE hCloseEvent = OpenEvent(EVENT_MODIFY_STATE, FALSE, deskflow::common::kCloseEventName); HANDLE hCloseEvent = OpenEvent(EVENT_MODIFY_STATE, FALSE, deskflow::common::kCloseEventName);
if (hCloseEvent) { // NOSONAR -- Readability if (hCloseEvent != nullptr) { // NOSONAR -- Readability
SetEvent(hCloseEvent); SetEvent(hCloseEvent);
CloseHandle(hCloseEvent); CloseHandle(hCloseEvent);
} else { } else {
LOG_WARN("could not send close event to process"); // This is expected to fail when no server/client is running.
throw XArch(new XArchEvalWindows); LOG_DEBUG("could not send close event to process");
} }
DWORD exitCode; DWORD exitCode;
if (!GetExitCodeProcess(handle, &exitCode)) { if (GetExitCodeProcess(handle, &exitCode)) {
LOG_ERR("failed to get process exit code for process %d", pid); if (exitCode == STILL_ACTIVE) {
throw XArch(new XArchEvalWindows); LOG_DEBUG("process %d is still running", pid);
} } else {
// Don't bother shutting down a process that has already exited.
if (exitCode != STILL_ACTIVE) {
LOG_DEBUG("process %d is already shutdown", pid); LOG_DEBUG("process %d is already shutdown", pid);
return; return;
} }
} else {
// wait for process to exit gracefully. XArchEvalWindows error;
double start = ARCH->time(); LOG_ERR("failed to get process exit code for process %d, error:", pid, error.eval().c_str());
while (true) { // NOSONAR -- Multiple breaks necessary
GetExitCodeProcess(handle, &exitCode);
if (exitCode != STILL_ACTIVE) {
// yay, we got a graceful shutdown. there should be no hook in use errors!
LOG((CLOG_DEBUG "process %d was shutdown gracefully", pid));
break;
} }
if (double elapsed = (ARCH->time() - start); elapsed > timeout) { // Wait for process to exit gracefully.
// if timeout reached, kill forcefully. LOG_DEBUG("waiting for process %d to exit gracefully", pid);
// calling TerminateProcess on deskflow is very bad! DWORD waitResult = WaitForSingleObject(handle, timeout * 1000);
// it causes the hook DLL to stay loaded in some apps, if (waitResult == WAIT_OBJECT_0) { // NOSONAR - Readability
// making it impossible to start deskflow again. if (!GetExitCodeProcess(handle, &exitCode)) {
LOG((CLOG_WARN "shutdown timed out after %d secs, forcefully terminating", (int)elapsed)); XArchEvalWindows error;
TerminateProcess(handle, kExitSuccess); LOG_ERR("failed to retrieve exit code after process exit for process %d, error:", pid, error.eval().c_str());
break;
} }
ARCH->sleep(1); LOG_DEBUG("process %d was shutdown gracefully with exit code %d", pid, exitCode);
return;
} else if (waitResult == WAIT_TIMEOUT) {
LOG_WARN("process %d did not exit within the expected time", pid);
} else {
XArchEvalWindows error;
LOG_ERR("error waiting for process %d to exit, error:", pid, error.eval().c_str());
}
// Last resort, terminate the process forcefully.
if (TerminateProcess(handle, kExitSuccess)) {
LOG_WARN("forcefully terminated process %d", pid);
} else {
XArchEvalWindows error;
LOG_ERR("failed to terminate process %d, error:", pid, error.eval().c_str());
} }
} }