From b440f58199c94144ca88f550737f4b8fb68ca38e Mon Sep 17 00:00:00 2001 From: Brent Deverman Date: Wed, 29 Jul 2026 15:41:59 +0800 Subject: [PATCH] Prevent orphaned dev processes on shutdown --- Sources/SagaCLI/DevCommand.swift | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Sources/SagaCLI/DevCommand.swift b/Sources/SagaCLI/DevCommand.swift index b12ae12..8c94faf 100644 --- a/Sources/SagaCLI/DevCommand.swift +++ b/Sources/SagaCLI/DevCommand.swift @@ -51,6 +51,9 @@ private final class DevCoordinator: @unchecked Sendable { } func start() throws { + // Recompile and shutdown both mutate siteProcess, so they must share one queue. + let lifecycleQueue = DispatchQueue(label: "Saga.Lifecycle") + // Set up SIGUSR2 handler — Saga signals us when a content rebuild completes so we can reload browsers signal(SIGUSR2, SIG_IGN) let sigusr2Source = DispatchSource.makeSignalSource(signal: SIGUSR2, queue: DispatchQueue(label: "Saga.Signal")) @@ -59,7 +62,7 @@ private final class DevCoordinator: @unchecked Sendable { // Set up SIGUSR1 handler — Saga signals us when Swift source files change so we can recompile signal(SIGUSR1, SIG_IGN) - let sigusr1Source = DispatchSource.makeSignalSource(signal: SIGUSR1, queue: DispatchQueue(label: "Saga.Recompile")) + let sigusr1Source = DispatchSource.makeSignalSource(signal: SIGUSR1, queue: lifecycleQueue) sigusr1Source.setEventHandler { [weak self] in self?.recompileAndRelaunch() } sigusr1Source.resume() @@ -110,10 +113,13 @@ private final class DevCoordinator: @unchecked Sendable { openBrowser(url: "http://localhost:\(port)/") // Handle Ctrl+C shutdown - let sigintSrc = DispatchSource.makeSignalSource(signal: SIGINT, queue: DispatchQueue(label: "Saga.Signals")) + let sigintSrc = DispatchSource.makeSignalSource(signal: SIGINT, queue: lifecycleQueue) sigintSrc.setEventHandler { [weak self] in print("\nShutting down...") - self?.siteProcess?.terminate() + if let siteProcess = self?.siteProcess, siteProcess.isRunning { + siteProcess.terminate() + siteProcess.waitUntilExit() + } self?.server?.stop() Foundation.exit(0) }