refactor: Implement Windows event loop in AppUtilWindows for graceful shutdown handling
This commit is contained in:
parent
e967944c1e
commit
e07a2efbcc
7 changed files with 82 additions and 8 deletions
|
|
@ -41,3 +41,11 @@ enum
|
||||||
kExitArgs = 3, // bad arguments
|
kExitArgs = 3, // bad arguments
|
||||||
kExitConfig = 4, // cannot read configuration
|
kExitConfig = 4, // cannot read configuration
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if WINAPI_MSWINDOWS
|
||||||
|
namespace deskflow::common {
|
||||||
|
|
||||||
|
const auto kCloseEventName = "Global\\DeskflowCloseEvent";
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@
|
||||||
#include "deskflow/protocol_types.h"
|
#include "deskflow/protocol_types.h"
|
||||||
#include "ipc/IpcMessage.h"
|
#include "ipc/IpcMessage.h"
|
||||||
#include "ipc/IpcServerProxy.h"
|
#include "ipc/IpcServerProxy.h"
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
#if SYSAPI_WIN32
|
#if SYSAPI_WIN32
|
||||||
#include "arch/win32/ArchMiscWindows.h"
|
#include "arch/win32/ArchMiscWindows.h"
|
||||||
|
|
@ -165,7 +166,6 @@ void App::loggingFilterWarning()
|
||||||
|
|
||||||
void App::initApp(int argc, const char **argv)
|
void App::initApp(int argc, const char **argv)
|
||||||
{
|
{
|
||||||
|
|
||||||
std::string configFilename;
|
std::string configFilename;
|
||||||
CLI::App cliApp{kAppDescription};
|
CLI::App cliApp{kAppDescription};
|
||||||
cliApp.add_option("--config-toml", configFilename, "Use TOML configuration file");
|
cliApp.add_option("--config-toml", configFilename, "Use TOML configuration file");
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
namespace deskflow {
|
namespace deskflow {
|
||||||
class Screen;
|
class Screen;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Deskflow -- mouse and keyboard sharing utility
|
* 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-FileCopyrightText: (C) 2002 Chris Schoeneman
|
||||||
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
||||||
*/
|
*/
|
||||||
|
|
@ -10,11 +10,10 @@
|
||||||
#include "arch/win32/ArchMiscWindows.h"
|
#include "arch/win32/ArchMiscWindows.h"
|
||||||
#include "arch/win32/XArchWindows.h"
|
#include "arch/win32/XArchWindows.h"
|
||||||
#include "base/Event.h"
|
#include "base/Event.h"
|
||||||
#include "base/EventQueue.h"
|
|
||||||
#include "base/IEventQueue.h"
|
#include "base/IEventQueue.h"
|
||||||
#include "base/Log.h"
|
#include "base/Log.h"
|
||||||
#include "base/log_outputters.h"
|
#include "base/log_outputters.h"
|
||||||
#include "common/constants.h"
|
#include "common/common.h"
|
||||||
#include "deskflow/App.h"
|
#include "deskflow/App.h"
|
||||||
#include "deskflow/ArgsBase.h"
|
#include "deskflow/ArgsBase.h"
|
||||||
#include "deskflow/Screen.h"
|
#include "deskflow/Screen.h"
|
||||||
|
|
@ -24,9 +23,7 @@
|
||||||
#include <VersionHelpers.h>
|
#include <VersionHelpers.h>
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#include <conio.h>
|
#include <conio.h>
|
||||||
#include <iostream>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
#if HAVE_WINTOAST
|
#if HAVE_WINTOAST
|
||||||
#include "wintoastlib.h"
|
#include "wintoastlib.h"
|
||||||
|
|
@ -37,10 +34,21 @@ AppUtilWindows::AppUtilWindows(IEventQueue *events) : m_events(events), m_exitMo
|
||||||
if (SetConsoleCtrlHandler((PHANDLER_ROUTINE)consoleHandler, TRUE) == FALSE) {
|
if (SetConsoleCtrlHandler((PHANDLER_ROUTINE)consoleHandler, TRUE) == FALSE) {
|
||||||
throw XArch(new XArchEvalWindows());
|
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()
|
AppUtilWindows::~AppUtilWindows()
|
||||||
{
|
{
|
||||||
|
m_eventThreadRunning = false;
|
||||||
|
m_eventThread.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL WINAPI AppUtilWindows::consoleHandler(DWORD)
|
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"));
|
LOG((CLOG_INFO "toast notifications are not supported"));
|
||||||
#endif
|
#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");
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Deskflow -- mouse and keyboard sharing utility
|
* 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-FileCopyrightText: (C) 2002 Chris Schoeneman
|
||||||
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
||||||
*/
|
*/
|
||||||
|
|
@ -10,7 +10,11 @@
|
||||||
#include "deskflow/AppUtil.h"
|
#include "deskflow/AppUtil.h"
|
||||||
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#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
|
#define ARCH_APP_UTIL AppUtilWindows
|
||||||
|
|
||||||
|
|
@ -44,6 +48,12 @@ public:
|
||||||
private:
|
private:
|
||||||
AppExitMode m_exitMode;
|
AppExitMode m_exitMode;
|
||||||
IEventQueue *m_events;
|
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);
|
static BOOL WINAPI consoleHandler(DWORD Event);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -122,6 +122,16 @@ void MSWindowsProcess::shutdown(HANDLE handle, DWORD pid, int timeout)
|
||||||
{
|
{
|
||||||
LOG_DEBUG("shutting down process %d", pid);
|
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;
|
DWORD exitCode;
|
||||||
GetExitCodeProcess(handle, &exitCode);
|
GetExitCodeProcess(handle, &exitCode);
|
||||||
if (exitCode != STILL_ACTIVE) {
|
if (exitCode != STILL_ACTIVE) {
|
||||||
|
|
|
||||||
|
|
@ -167,6 +167,7 @@ MSWindowsWatchdog::getUserToken(LPSECURITY_ATTRIBUTES security, bool elevatedTok
|
||||||
void MSWindowsWatchdog::mainLoop(void *)
|
void MSWindowsWatchdog::mainLoop(void *)
|
||||||
{
|
{
|
||||||
LOG_DEBUG("starting main loop");
|
LOG_DEBUG("starting main loop");
|
||||||
|
|
||||||
shutdownExistingProcesses();
|
shutdownExistingProcesses();
|
||||||
|
|
||||||
// the SendSAS function is used to send a sas (secure attention sequence) to the
|
// the SendSAS function is used to send a sas (secure attention sequence) to the
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue