Stop answering before the replacement starts, on a restart

execv leaves nothing behind to answer, so this never came up on Linux or a Mac.
A Windows restart is two processes for a moment: the new one looks for an
instance to hand its command to, and if it finds the old one still listening it
takes itself for the second copy and exits, leaving nothing running at all. The
server is closed before the replacement is started rather than after.

Not reproduced, because it is a race and it lost: found by reading the order of
the two calls, and worth a restart or twenty on a real machine.
This commit is contained in:
2026-08-16 10:28:52 +03:00
parent ca559e3961
commit bbb9bccda4
+11
View File
@@ -88,6 +88,9 @@ class Dikte:
self.meeting_base = "" self.meeting_base = ""
self.meeting_message = "" self.meeting_message = ""
self.settings_window = None self.settings_window = None
# The single-instance server, handed over once run_app has opened it, so
# that a restart can stop answering before the replacement starts.
self.server = None
self._quitting = False self._quitting = False
# A request that asked to be told how its run ended waits in here until # A request that asked to be told how its run ended waits in here until
# the run gets there, keyed by which of the three it was waiting on. # the run gets there, keyed by which of the three it was waiting on.
@@ -901,6 +904,13 @@ class Dikte:
if self.settings_window is not None: if self.settings_window is not None:
self.settings_window.close() self.settings_window.close()
self.shutdown() self.shutdown()
# Stop answering before the replacement is started, not just afterwards.
# execv leaves nothing behind to answer, but a Windows restart is two
# processes for a moment, and a new one that reached a server still
# listening would take itself for the second copy, hand its command over
# and exit, leaving nothing running at all.
if self.server is not None:
self.server.close()
QLocalServer.removeServer(SERVER_NAME) QLocalServer.removeServer(SERVER_NAME)
if sys.platform == "win32": if sys.platform == "win32":
# execv on Windows mangles arguments with spaces and leaves the two # execv on Windows mangles arguments with spaces and leaves the two
@@ -1066,6 +1076,7 @@ def run_app(args):
QLocalServer.removeServer(SERVER_NAME) QLocalServer.removeServer(SERVER_NAME)
if not server.listen(SERVER_NAME): if not server.listen(SERVER_NAME):
print(f"dikte: could not open the IPC socket: {server.errorString()}") print(f"dikte: could not open the IPC socket: {server.errorString()}")
dikte.server = server
def on_connection(): def on_connection():
conn = server.nextPendingConnection() conn = server.nextPendingConnection()