feat(gui): Retry mechanism for Windows Daemon IPC client
Some checks failed
CodeQL Analysis / Analyze (cpp) (push) Has been cancelled
Continuous Integration / reuse-lint (push) Has been cancelled
Continuous Integration / pr-comment-flags (push) Has been cancelled
Continuous Integration / ci-passed (push) Has been cancelled
Continuous Integration / test-results (push) Has been cancelled
Continuous Integration / lint-check (push) Has been cancelled
Continuous Integration / analyse-valgrind (push) Has been cancelled
Continuous Integration / analyse-sonarcloud (push) Has been cancelled
Continuous Integration / ${{ matrix.target.name }} (map[config-args:-DCMAKE_OSX_ARCHITECTURES="arm64" -DCMAKE_OSX_SYSROOT=/Applications/Xcode_15.0.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk name:macos-14-arm64 qt-install-dir:/Users/runner runs-o… (push) Has been cancelled
Continuous Integration / ${{ matrix.target.name }} (map[config-args:-DCMAKE_OSX_ARCHITECTURES="x86_64" -DCMAKE_OSX_SYSROOT=/Applications/Xcode_15.0.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk name:macos-13-x64 qt-install-dir:/Users/runner runs-on… (push) Has been cancelled
Continuous Integration / ${{ matrix.target.name }} (map[config-args:-G Ninja -DCMAKE_INSTALL_PREFIX=/usr container:archlinux:latest like:arch name:archlinux-x86_84 runs-on:ubuntu-latest timeout:20]) (push) Has been cancelled
Continuous Integration / ${{ matrix.target.name }} (map[config-args:-G Ninja -DCMAKE_INSTALL_PREFIX=/usr container:debian:trixie-slim like:debian name:debian-13-arm64 runs-on:ubuntu-24.04-arm timeout:20]) (push) Has been cancelled
Continuous Integration / ${{ matrix.target.name }} (map[config-args:-G Ninja -DCMAKE_INSTALL_PREFIX=/usr container:debian:trixie-slim like:debian name:debian-13-x86_64 runs-on:ubuntu-latest timeout:20]) (push) Has been cancelled
Continuous Integration / ${{ matrix.target.name }} (map[config-args:-G Ninja -DCMAKE_INSTALL_PREFIX=/usr container:fedora:40 like:fedora name:fedora-40-arm64 runs-on:ubuntu-24.04-arm timeout:20]) (push) Has been cancelled
Continuous Integration / ${{ matrix.target.name }} (map[config-args:-G Ninja -DCMAKE_INSTALL_PREFIX=/usr container:fedora:40 like:fedora name:fedora-40-x86_84 runs-on:ubuntu-latest timeout:20]) (push) Has been cancelled
Continuous Integration / ${{ matrix.target.name }} (map[config-args:-G Ninja -DCMAKE_INSTALL_PREFIX=/usr container:fedora:41 like:fedora name:fedora-41-arm64 runs-on:ubuntu-24.04-arm timeout:20]) (push) Has been cancelled
Continuous Integration / ${{ matrix.target.name }} (map[config-args:-G Ninja -DCMAKE_INSTALL_PREFIX=/usr container:fedora:41 like:fedora name:fedora-41-x86_64 runs-on:ubuntu-latest timeout:20]) (push) Has been cancelled
Continuous Integration / ${{ matrix.target.name }} (map[config-args:-G Ninja -DCMAKE_INSTALL_PREFIX=/usr container:fedora:42 like:fedora name:fedora-42-arm64 runs-on:ubuntu-24.04-arm timeout:20]) (push) Has been cancelled
Continuous Integration / ${{ matrix.target.name }} (map[config-args:-G Ninja -DCMAKE_INSTALL_PREFIX=/usr container:fedora:42 like:fedora name:fedora-42-x86_64 runs-on:ubuntu-latest timeout:20]) (push) Has been cancelled
Continuous Integration / ${{ matrix.target.name }} (map[config-args:-G Ninja -DCMAKE_INSTALL_PREFIX=/usr container:opensuse/tumbleweed:latest like:suse name:opensuse-arm64 runs-on:ubuntu-24.04-arm timeout:20]) (push) Has been cancelled
Continuous Integration / ${{ matrix.target.name }} (map[config-args:-G Ninja -DCMAKE_INSTALL_PREFIX=/usr container:opensuse/tumbleweed:latest like:suse name:opensuse-x86_84 runs-on:ubuntu-latest timeout:20]) (push) Has been cancelled
Continuous Integration / ${{ matrix.target.name }} (map[config-args:-G Ninja -DCMAKE_INSTALL_PREFIX=/usr container:ubuntu:25.04 like:debian name:ubuntu-25.04-arm64 runs-on:ubuntu-24.04-arm timeout:20]) (push) Has been cancelled
Continuous Integration / ${{ matrix.target.name }} (map[config-args:-G Ninja -DCMAKE_INSTALL_PREFIX=/usr container:ubuntu:25.04 like:debian name:ubuntu-25.04-x86_64 runs-on:ubuntu-latest timeout:20]) (push) Has been cancelled
Continuous Integration / ${{ matrix.target.name }} (map[config-args:-G Ninja name:windows-2022-x64 qt-install-dir:C: runs-on:windows-2022 timeout:30]) (push) Has been cancelled
Continuous Integration / unix-${{ matrix.distro.name }} (map[name:freebsd]) (push) Has been cancelled
Continuous Integration / flatpak-${{matrix.flatpak.arch}} (map[arch:aarch64 runs-on:ubuntu-24.04-arm]) (push) Has been cancelled
Continuous Integration / flatpak-${{matrix.flatpak.arch}} (map[arch:x86_64 runs-on:ubuntu-latest]) (push) Has been cancelled
Continuous Integration / release (push) Has been cancelled
Continuous Integration / winget-publish (push) Has been cancelled

This commit is contained in:
Nick Bolton 2025-04-18 13:56:37 +01:00
parent 3b3d9e14fd
commit 35f0e9e6e4
7 changed files with 94 additions and 39 deletions

View File

@ -289,7 +289,9 @@ void MainWindow::connectSlots()
connect(&m_coreProcess, &CoreProcess::processStateChanged, this, &MainWindow::coreProcessStateChanged);
connect(&m_coreProcess, &CoreProcess::connectionStateChanged, this, &MainWindow::coreConnectionStateChanged);
connect(&m_coreProcess, &CoreProcess::secureSocket, this, &MainWindow::secureSocket);
connect(&m_coreProcess, &CoreProcess::daemonIpcClientConnectFailed, this, &MainWindow::daemonIpcClientConnectFailed);
connect(
&m_coreProcess, &CoreProcess::daemonIpcClientConnectionFailed, this, &MainWindow::daemonIpcClientConnectionFailed
);
connect(m_actionAbout, &QAction::triggered, this, &MainWindow::openAboutDialog);
connect(m_actionClearSettings, &QAction::triggered, this, &MainWindow::clearSettings);
@ -1145,7 +1147,7 @@ bool MainWindow::regenerateLocalFingerprints()
return true;
}
void MainWindow::daemonIpcClientConnectFailed()
void MainWindow::daemonIpcClientConnectionFailed()
{
if (deskflow::gui::messages::showDaemonOffline(this)) {
m_coreProcess.retryDaemon();

View File

@ -158,7 +158,7 @@ private:
void showAndActivate();
void showHostNameEditor();
void setHostName();
void daemonIpcClientConnectFailed();
void daemonIpcClientConnectionFailed();
void toggleCanRunCore(bool enableButtons);
void remoteHostChanged(const QString newRemoteHost);

View File

@ -140,7 +140,9 @@ CoreProcess::CoreProcess(const IServerConfig &serverConfig, std::shared_ptr<Deps
m_daemonIpcClient{new ipc::DaemonIpcClient(this)}
{
connect(m_daemonIpcClient, &ipc::DaemonIpcClient::connected, this, &CoreProcess::daemonIpcClientConnected);
connect(m_daemonIpcClient, &ipc::DaemonIpcClient::connectFailed, this, &CoreProcess::daemonIpcClientConnectFailed);
connect(
m_daemonIpcClient, &ipc::DaemonIpcClient::connectionFailed, this, &CoreProcess::daemonIpcClientConnectionFailed
);
connect(&m_pDeps->process(), &QProcessProxy::finished, this, &CoreProcess::onProcessFinished);

View File

@ -120,7 +120,7 @@ signals:
void connectionStateChanged(ConnectionState state);
void processStateChanged(ProcessState state);
void secureSocket(bool enabled);
void daemonIpcClientConnectFailed();
void daemonIpcClientConnectionFailed();
private slots:
void onProcessFinished(int exitCode, QProcess::ExitStatus);

View File

@ -16,6 +16,7 @@
namespace deskflow::gui::ipc {
const auto kTimeout = 1000;
const auto kRetryLimit = 3;
DaemonIpcClient::DaemonIpcClient(QObject *parent)
: QObject(parent),
@ -27,54 +28,95 @@ DaemonIpcClient::DaemonIpcClient(QObject *parent)
bool DaemonIpcClient::connectToServer()
{
if (m_connecting) {
qDebug() << "daemon ipc client already connecting to server";
if (m_state == State::Connecting) {
qWarning() << "daemon ipc client already connecting to server";
return false;
}
qDebug() << "daemon ipc client connecting to server:" << kDaemonIpcName;
m_connecting = true;
m_socket->connectToServer(kDaemonIpcName);
if (!m_socket->waitForConnected(kTimeout)) {
qWarning() << "daemon ipc client failed to connect";
m_connecting = false;
Q_EMIT connectFailed();
return false;
if (m_state != State::Unconnected) {
qDebug() << "daemon ipc client not in unconnected state, disconnecting";
disconnectFromServer();
}
if (!sendMessage("hello", "hello", false)) {
qWarning() << "daemon ipc client failed to send hello";
m_connecting = false;
Q_EMIT connectFailed();
return false;
if (m_socket->state() != QLocalSocket::UnconnectedState) {
qWarning() << "daemon ipc client socket not in unconnected state, disconnecting";
disconnectFromServer();
}
m_connecting = false;
m_connected = true;
for (int i = 0; i < kRetryLimit; ++i) {
if (i == 0) {
qDebug() << "daemon ipc client connecting to server:" << kDaemonIpcName;
} else {
qDebug() << "daemon ipc client retrying connection, attempt:" << i + 1;
}
qDebug() << "daemon ipc client connected";
Q_EMIT connected();
m_state = State::Connecting;
m_socket->connectToServer(kDaemonIpcName);
return true;
if (!m_socket->waitForConnected(kTimeout)) {
qWarning() << "daemon ipc client failed to connect";
disconnectFromServer();
continue;
}
if (!sendMessage("hello", "hello", false)) {
qWarning() << "daemon ipc client failed to send hello";
disconnectFromServer();
continue;
}
m_state = State::Connected;
qDebug() << "daemon ipc client connected";
Q_EMIT connected();
return true;
}
qWarning() << "daemon ipc client failed to connect after" << kRetryLimit << "attempts";
disconnectFromServer();
Q_EMIT connectionFailed();
return false;
}
void DaemonIpcClient::disconnectFromServer()
{
m_state = State::Disconnecting;
qDebug() << "daemon ipc client disconnecting from server";
m_socket->disconnectFromServer();
if (m_socket->state() != QLocalSocket::UnconnectedState) {
qDebug() << "daemon ipc client waiting for socket to disconnect";
m_socket->waitForDisconnected(kTimeout);
qDebug() << "daemon ipc client disconnected from server";
} else {
qDebug() << "daemon ipc client socket already disconnected";
}
m_state = State::Unconnected;
}
void DaemonIpcClient::handleDisconnected()
{
qWarning() << "daemon ipc client disconnected from server";
m_connected = false;
Q_EMIT connectFailed();
qDebug() << "daemon ipc client disconnected from server";
if (m_state == State::Connected) {
Q_EMIT connectionFailed();
}
m_state = State::Unconnected;
}
void DaemonIpcClient::handleErrorOccurred()
{
qWarning() << "daemon ipc client error:" << m_socket->errorString();
m_connected = false;
disconnectFromServer();
if (m_state == State::Connected) {
Q_EMIT connectionFailed();
}
}
bool DaemonIpcClient::sendMessage(const QString &message, const QString &expectAck, const bool expectConnected)
{
if (expectConnected && !m_connected) {
if (expectConnected && !isConnected()) {
qWarning() << "cannot send command, ipc client not connected";
return false;
}
@ -90,7 +132,7 @@ bool DaemonIpcClient::sendMessage(const QString &message, const QString &expectA
qDebug() << "daemon ipc client waiting for ack: " << expectAck;
if (!m_socket->waitForReadyRead(kTimeout)) {
qWarning() << "daemon ipc client failed to read response";
qWarning() << "daemon ipc client socket ready read timed out";
return false;
}
@ -124,8 +166,8 @@ bool DaemonIpcClient::keepAlive()
}
if (!sendMessage("noop")) {
qWarning() << "daemon ipc client keep alive ping failed";
m_connected = false;
qWarning() << "daemon ipc client keep alive ping failed, reconnecting";
connectToServer();
return false;
}

View File

@ -16,9 +16,19 @@ class DaemonIpcClient : public QObject
{
Q_OBJECT
// Represents underlying socket state and whether the server responded to the hello message.
enum class State
{
Unconnected,
Connecting,
Connected,
Disconnecting,
};
public:
explicit DaemonIpcClient(QObject *parent = nullptr);
bool connectToServer();
void disconnectFromServer();
bool sendLogLevel(const QString &logLevel);
bool sendStartProcess(const QString &command, bool elevate);
bool sendStopProcess();
@ -27,12 +37,12 @@ public:
bool isConnected() const
{
return m_connected;
return m_state == State::Connected;
}
signals:
void connected();
void connectFailed();
void connectionFailed();
private slots:
void handleDisconnected();
@ -44,8 +54,7 @@ private:
private:
QLocalSocket *m_socket;
bool m_connected{false};
bool m_connecting{false};
State m_state{State::Unconnected};
};
} // namespace deskflow::gui::ipc

View File

@ -246,7 +246,7 @@ void MSWindowsWatchdog::mainLoop(void *)
LOG_DEBUG("watchdog main loop finished");
if (m_process != nullptr) {
LOG_DEBUG("terminated running process on exit");
LOG_DEBUG("terminating running process on exit");
m_process->shutdown();
m_process.reset();
}