refactor(macos): centralize process pipe draining

This commit is contained in:
Peter Steinberger
2026-01-17 08:53:03 +00:00
parent 1002c74d9c
commit 869ef0c5ba
7 changed files with 20 additions and 24 deletions

View File

@@ -278,10 +278,7 @@ enum GatewayEnvironment {
process.standardOutput = pipe
process.standardError = pipe
do {
try process.run()
// Read pipe before waitUntilExit to avoid potential deadlock
let data = pipe.fileHandleForReading.readToEndSafely()
process.waitUntilExit()
let data = try process.runAndReadToEnd(from: pipe)
let elapsedMs = Int(Date().timeIntervalSince(start) * 1000)
if elapsedMs > 500 {
self.logger.warning(

View File

@@ -72,11 +72,11 @@ enum LaunchAgentManager {
let process = Process()
process.launchPath = "/bin/launchctl"
process.arguments = args
process.standardOutput = Pipe()
process.standardError = Pipe()
let pipe = Pipe()
process.standardOutput = pipe
process.standardError = pipe
do {
try process.run()
process.waitUntilExit()
_ = try process.runAndReadToEnd(from: pipe)
return process.terminationStatus
} catch {
return -1

View File

@@ -16,12 +16,7 @@ enum Launchctl {
process.standardOutput = pipe
process.standardError = pipe
do {
try process.run()
// Read pipe output BEFORE waitUntilExit to avoid deadlock.
// If the process writes enough to fill the pipe buffer (~64KB),
// it will block until someone reads. Reading first prevents this.
let data = pipe.fileHandleForReading.readToEndSafely()
process.waitUntilExit()
let data = try process.runAndReadToEnd(from: pipe)
let output = String(data: data, encoding: .utf8) ?? ""
return Result(status: process.terminationStatus, output: output)
} catch {

View File

@@ -580,11 +580,10 @@ final class NodePairingApprovalPrompter {
process.standardError = pipe
do {
try process.run()
_ = try process.runAndReadToEnd(from: pipe)
} catch {
return false
}
process.waitUntilExit()
return process.terminationStatus == 0
}.value
}

View File

@@ -203,10 +203,7 @@ actor PortGuardian {
proc.standardOutput = pipe
proc.standardError = Pipe()
do {
try proc.run()
// Read pipe before waitUntilExit to avoid potential deadlock
let data = pipe.fileHandleForReading.readToEndSafely()
proc.waitUntilExit()
let data = try proc.runAndReadToEnd(from: pipe)
guard !data.isEmpty else { return nil }
return String(data: data, encoding: .utf8)?
.trimmingCharacters(in: .whitespacesAndNewlines)

View File

@@ -0,0 +1,11 @@
import Foundation
extension Process {
/// Runs the process and drains the given pipe before waiting to avoid blocking on full buffers.
func runAndReadToEnd(from pipe: Pipe) throws -> Data {
try self.run()
let data = pipe.fileHandleForReading.readToEndSafely()
self.waitUntilExit()
return data
}
}

View File

@@ -133,10 +133,7 @@ enum RuntimeLocator {
process.standardError = pipe
do {
try process.run()
// Read pipe before waitUntilExit to avoid potential deadlock
let data = pipe.fileHandleForReading.readToEndSafely()
process.waitUntilExit()
let data = try process.runAndReadToEnd(from: pipe)
let elapsedMs = Int(Date().timeIntervalSince(start) * 1000)
if elapsedMs > 500 {
self.logger.warning(