This commit is contained in:
Peter Steinberger 2025-06-25 01:47:16 +02:00
parent b3299e020a
commit 566e7f62b3
2 changed files with 81 additions and 34 deletions

View file

@ -1,34 +0,0 @@
import Foundation
extension Process {
/// Configure process to automatically terminate when parent dies
/// This sets up proper process group handling on macOS
func configureForParentTermination() {
// Set quality of service to tie lifecycle to parent
self.qualityOfService = .userInitiated
// On macOS, we can use process groups to ensure child termination
// When the parent dies, all processes in the same process group receive SIGHUP
#if os(macOS)
// This will be called just before the process launches
// We'll use posix_spawn attributes to set up the process group
if #available(macOS 10.15, *) {
// Modern approach: let the system handle it
// NSTask/Process on modern macOS automatically handles parent death
// when qualityOfService is set
}
#endif
}
/// Enhanced run method that ensures proper process group setup
func runWithParentTermination() throws {
configureForParentTermination()
try run()
}
/// Async version of runWithParentTermination
func runWithParentTerminationAsync() async throws {
configureForParentTermination()
try await runAsync()
}
}

View file

@ -0,0 +1,81 @@
//
// Process+ParentTermination.swift
// VibeTunnel
//
// Simple extension to run processes asynchronously
//
import Foundation
extension Process {
/// Async version that starts the process and returns immediately
@available(macOS 14.0, *)
func runAsync() async throws {
try await withCheckedThrowingContinuation { continuation in
DispatchQueue.global(qos: .userInitiated).async {
do {
try self.run()
continuation.resume()
} catch {
continuation.resume(throwing: error)
}
}
}
}
/// Run process with parent termination handling
/// (The actual parent monitoring is handled by the shell wrapper)
func runWithParentTermination() throws {
try run()
}
/// Async version of runWithParentTermination
@available(macOS 14.0, *)
func runWithParentTerminationAsync() async throws {
try await runAsync()
}
/// Wait for the process to exit asynchronously
func waitUntilExitAsync() async {
await withCheckedContinuation { continuation in
DispatchQueue.global(qos: .userInitiated).async {
self.waitUntilExit()
continuation.resume()
}
}
}
/// Terminate the process asynchronously
func terminateAsync() async {
await withCheckedContinuation { continuation in
DispatchQueue.global(qos: .userInitiated).async {
if self.isRunning {
self.terminate()
}
continuation.resume()
}
}
}
/// Wait for exit with timeout
func waitUntilExitWithTimeout(seconds: TimeInterval) async -> Bool {
await withTaskGroup(of: Bool.self) { group in
group.addTask {
await self.waitUntilExitAsync()
return true
}
group.addTask {
try? await Task.sleep(for: .seconds(seconds))
return false
}
for await result in group {
group.cancelAll()
return result
}
return false
}
}
}