@@ -162,26 +162,43 @@ def _find_ccc_executable() -> str | None:
162162
163163
164164def stop_daemon () -> None :
165- """Stop the daemon gracefully."""
165+ """Stop the daemon gracefully.
166+
167+ Sends a StopRequest, waits for the process to exit, falls back to SIGTERM.
168+ """
169+ # Step 1: try sending StopRequest
166170 try :
167171 client = DaemonClient .connect ()
168172 client .handshake ()
169173 client .stop ()
170174 client .close ()
171- except (ConnectionRefusedError , OSError ):
175+ except (ConnectionRefusedError , OSError , RuntimeError ):
172176 pass
173177
174- # If daemon doesn't respond, try SIGTERM via PID
178+ # Step 2: wait for process to exit (up to 5s)
175179 pid_path = daemon_pid_path ()
180+ deadline = time .monotonic () + 5.0
181+ while time .monotonic () < deadline and pid_path .exists ():
182+ time .sleep (0.1 )
183+
184+ if not pid_path .exists ():
185+ return # Clean exit
186+
187+ # Step 3: if still running, try SIGTERM
176188 if pid_path .exists ():
177189 try :
178190 pid = int (pid_path .read_text ().strip ())
179- if pid != os .getpid (): # Never kill ourselves (happens when daemon runs in a thread)
191+ if pid != os .getpid ():
180192 os .kill (pid , signal .SIGTERM )
181193 except (ValueError , ProcessLookupError , PermissionError ):
182194 pass
183195
184- # Clean up stale files (named pipes on Windows clean up automatically)
196+ # Wait a bit more
197+ deadline = time .monotonic () + 2.0
198+ while time .monotonic () < deadline and pid_path .exists ():
199+ time .sleep (0.1 )
200+
201+ # Step 4: clean up stale files
185202 if sys .platform != "win32" :
186203 sock = daemon_socket_path ()
187204 try :
0 commit comments