[Client] Treat a busy single-instance primary as alive instead of stealing its socket

This commit is contained in:
Lukas Brübach 2026-09-19 07:21:51 +02:00 committed by GitHub
parent 040be0c1d3
commit 5e70a769d4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 36 additions and 16 deletions

View file

@ -28,9 +28,15 @@ bool SingleInstanceManager::tryRun(const QStringList &filesToSend)
} }
serverName = QStringLiteral("CockatriceSingleInstance-%1").arg(userName); serverName = QStringLiteral("CockatriceSingleInstance-%1").arg(userName);
// Hand off to an already-running primary instance if one exists. // Hand off to an already-running primary instance if one exists. Never steal
if (forwardToPrimary(filesToSend)) { // the socket of a busy primary: it is alive and will act on the payload.
return false; switch (forwardToPrimary(filesToSend)) {
case ForwardResult::Delivered:
return false;
case ForwardResult::PrimaryBusy:
return false;
case ForwardResult::NoPrimary:
break;
} }
// No primary instance is currently reachable, so become the primary. // No primary instance is currently reachable, so become the primary.
@ -43,12 +49,18 @@ bool SingleInstanceManager::tryRun(const QStringList &filesToSend)
// Another instance may have started while we were probing; hand off to it // Another instance may have started while we were probing; hand off to it
// instead of stealing its socket. // instead of stealing its socket.
if (forwardToPrimary(filesToSend)) { switch (forwardToPrimary(filesToSend)) {
return false; case ForwardResult::Delivered:
return false;
case ForwardResult::PrimaryBusy:
return false;
case ForwardResult::NoPrimary:
break;
} }
// The socket is stale (left over by a crashed instance): remove it and // The socket is stale (left over by a crashed instance), so no primary is
// retry. If that still fails, another instance just took the name. // holding it: remove it and retry. If that still fails, another instance
// just took the name.
QLocalServer::removeServer(serverName); QLocalServer::removeServer(serverName);
if (server->listen(serverName)) { if (server->listen(serverName)) {
return true; return true;
@ -58,12 +70,12 @@ bool SingleInstanceManager::tryRun(const QStringList &filesToSend)
return false; return false;
} }
bool SingleInstanceManager::forwardToPrimary(const QStringList &filesToSend) SingleInstanceManager::ForwardResult SingleInstanceManager::forwardToPrimary(const QStringList &filesToSend)
{ {
QLocalSocket socket; QLocalSocket socket;
socket.connectToServer(serverName); socket.connectToServer(serverName);
if (!socket.waitForConnected(200)) { if (!socket.waitForConnected(200)) {
return false; return ForwardResult::NoPrimary;
} }
// Serialize payload with length prefix // Serialize payload with length prefix
@ -81,13 +93,14 @@ bool SingleInstanceManager::forwardToPrimary(const QStringList &filesToSend)
socket.waitForBytesWritten(1000); socket.waitForBytesWritten(1000);
// Only report a successful hand-off once the primary has acknowledged that // Only report a successful hand-off once the primary has acknowledged that
// it actually read the payload. A socket that connects but never answers // it actually read the payload. A socket that connects but is still working
// belongs to a process that is dying, so the caller must not treat this as // on an earlier payload is alive but busy, not dead: give it more room
// a hand-off (otherwise it would exit without anyone handling the files). // before giving up, so a slow handler does not make a live primary look
if (!socket.waitForReadyRead(1000)) { // dead (which would lead to stealing its socket).
return false; if (!socket.waitForReadyRead(1000) && !socket.waitForReadyRead(4000)) {
return ForwardResult::PrimaryBusy;
} }
return socket.readAll() == ACK_MESSAGE; return socket.readAll() == ACK_MESSAGE ? ForwardResult::Delivered : ForwardResult::PrimaryBusy;
} }
void SingleInstanceManager::handleNewConnection() void SingleInstanceManager::handleNewConnection()

View file

@ -23,7 +23,14 @@ private slots:
void handleNewConnection(); void handleNewConnection();
private: private:
bool forwardToPrimary(const QStringList &filesToSend); enum class ForwardResult
{
Delivered, // a live primary acknowledged the payload
NoPrimary, // no connectable primary socket exists
PrimaryBusy // a primary exists but did not acknowledge in time
};
ForwardResult forwardToPrimary(const QStringList &filesToSend);
QString serverName; QString serverName;
QLocalServer *server = nullptr; QLocalServer *server = nullptr;