refactor: Implement Windows event loop in AppUtilWindows for graceful shutdown handling

This commit is contained in:
Nick Bolton
2025-02-10 19:15:41 +00:00
parent e967944c1e
commit e07a2efbcc
7 changed files with 82 additions and 8 deletions

View File

@ -41,3 +41,11 @@ enum
kExitArgs = 3, // bad arguments
kExitConfig = 4, // cannot read configuration
};
#if WINAPI_MSWINDOWS
namespace deskflow::common {
const auto kCloseEventName = "Global\\DeskflowCloseEvent";
}
#endif

View File

@ -23,6 +23,7 @@
#include "deskflow/protocol_types.h"
#include "ipc/IpcMessage.h"
#include "ipc/IpcServerProxy.h"
#include <thread>
#if SYSAPI_WIN32
#include "arch/win32/ArchMiscWindows.h"
@ -165,7 +166,6 @@ void App::loggingFilterWarning()
void App::initApp(int argc, const char **argv)
{
std::string configFilename;
CLI::App cliApp{kAppDescription};
cliApp.add_option("--config-toml", configFilename, "Use TOML configuration file");

View File

@ -20,6 +20,7 @@
#endif
#include <stdexcept>
#include <thread>
namespace deskflow {
class Screen;

View File

@ -1,6 +1,6 @@
/*
* Deskflow -- mouse and keyboard sharing utility
* SPDX-FileCopyrightText: (C) 2012 Symless Ltd.
* SPDX-FileCopyrightText: (C) 2012 - 2025 Symless Ltd.
* SPDX-FileCopyrightText: (C) 2002 Chris Schoeneman
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
*/
@ -10,11 +10,10 @@
#include "arch/win32/ArchMiscWindows.h"
#include "arch/win32/XArchWindows.h"
#include "base/Event.h"
#include "base/EventQueue.h"
#include "base/IEventQueue.h"
#include "base/Log.h"
#include "base/log_outputters.h"
#include "common/constants.h"
#include "common/common.h"
#include "deskflow/App.h"
#include "deskflow/ArgsBase.h"
#include "deskflow/Screen.h"
@ -24,9 +23,7 @@
#include <VersionHelpers.h>
#include <Windows.h>
#include <conio.h>
#include <iostream>
#include <memory>
#include <sstream>
#if HAVE_WINTOAST
#include "wintoastlib.h"
@ -37,10 +34,21 @@ AppUtilWindows::AppUtilWindows(IEventQueue *events) : m_events(events), m_exitMo
if (SetConsoleCtrlHandler((PHANDLER_ROUTINE)consoleHandler, TRUE) == FALSE) {
throw XArch(new XArchEvalWindows());
}
m_eventThread = std::thread(&AppUtilWindows::eventLoop, this); // NOSONAR - No jthread on Windows
// Waiting for the event loop start prevents race condition in fast fail scenario,
// where the dtor is called just before the event loop starts.
LOG_DEBUG("waiting for event thread to start");
std::unique_lock<std::mutex> lock(m_eventThreadStartedMutex);
m_eventThreadStartedCond.wait(lock, [this] { return m_eventThreadRunning; });
LOG_DEBUG("event thread started");
}
AppUtilWindows::~AppUtilWindows()
{
m_eventThreadRunning = false;
m_eventThread.join();
}
BOOL WINAPI AppUtilWindows::consoleHandler(DWORD)
@ -263,3 +271,39 @@ void AppUtilWindows::showNotification(const std::string &title, const std::strin
LOG((CLOG_INFO "toast notifications are not supported"));
#endif
}
void AppUtilWindows::eventLoop()
{
HANDLE hCloseEvent = CreateEventA(nullptr, TRUE, FALSE, deskflow::common::kCloseEventName);
if (!hCloseEvent) {
LOG_CRIT("failed to create event for windows event loop");
throw XArch(new XArchEvalWindows());
}
LOG_DEBUG("windows event loop running");
{
std::lock_guard<std::mutex> lock(m_eventThreadStartedMutex);
m_eventThreadRunning = true;
}
m_eventThreadStartedCond.notify_one();
while (m_eventThreadRunning) {
// Wait for 100ms at most so that we can stop the loop when the app is closing, if not already stopped.
DWORD closeEventResult = MsgWaitForMultipleObjects(1, &hCloseEvent, FALSE, 100, QS_ALLINPUT);
if (closeEventResult == WAIT_OBJECT_0) {
LOG_DEBUG("windows event loop received close event");
m_events->addEvent(Event(Event::kQuit));
m_eventThreadRunning = false;
} else if (closeEventResult == WAIT_OBJECT_0 + 1) {
MSG msg;
while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
CloseHandle(hCloseEvent);
LOG_DEBUG("windows event loop finished");
}

View File

@ -1,6 +1,6 @@
/*
* Deskflow -- mouse and keyboard sharing utility
* SPDX-FileCopyrightText: (C) 2012 - 2016 Symless Ltd.
* SPDX-FileCopyrightText: (C) 2012 - 2025 Symless Ltd.
* SPDX-FileCopyrightText: (C) 2002 Chris Schoeneman
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
*/
@ -10,7 +10,11 @@
#include "deskflow/AppUtil.h"
#define WIN32_LEAN_AND_MEAN
#include "Windows.h"
#include "Windows.h" // IWYU pragma: keep
#include <condition_variable>
#include <mutex>
#include <thread>
#define ARCH_APP_UTIL AppUtilWindows
@ -44,6 +48,12 @@ public:
private:
AppExitMode m_exitMode;
IEventQueue *m_events;
std::thread m_eventThread; // NOSONAR - No jthread on Windows
bool m_eventThreadRunning = false;
std::condition_variable m_eventThreadStartedCond;
std::mutex m_eventThreadStartedMutex;
void eventLoop();
static BOOL WINAPI consoleHandler(DWORD Event);
};

View File

@ -122,6 +122,16 @@ void MSWindowsProcess::shutdown(HANDLE handle, DWORD pid, int timeout)
{
LOG_DEBUG("shutting down process %d", pid);
LOG_DEBUG("sending close event to close process gracefully");
HANDLE hCloseEvent = OpenEvent(EVENT_MODIFY_STATE, FALSE, deskflow::common::kCloseEventName);
if (hCloseEvent) { // NOSONAR -- Readability
SetEvent(hCloseEvent);
CloseHandle(hCloseEvent);
} else {
LOG((CLOG_WARN "could not send close event to process"));
throw XArch(new XArchEvalWindows);
}
DWORD exitCode;
GetExitCodeProcess(handle, &exitCode);
if (exitCode != STILL_ACTIVE) {

View File

@ -167,6 +167,7 @@ MSWindowsWatchdog::getUserToken(LPSECURITY_ATTRIBUTES security, bool elevatedTok
void MSWindowsWatchdog::mainLoop(void *)
{
LOG_DEBUG("starting main loop");
shutdownExistingProcesses();
// the SendSAS function is used to send a sas (secure attention sequence) to the