Context
GitService.waitForProcess(_:timeout:) currently polls process.isRunning in a loop with Thread.sleep(0.05) on a global concurrent queue. That burns a CPU slot and ties up a worker thread for the entire duration of every git command (which adds up: every refresh fans out to status, log, branches, currentBranch, stash list).
Suggested by feedback on r/swift.
Proposal
Bridge Process.terminationHandler directly into a withCheckedContinuation. Process fires the handler the instant the child exits, so no polling is needed. Race it against a Task.sleep-based timeout that calls process.terminate() and resumes with false.
Sketch:
func waitForProcess(_ process: Process, timeout: TimeInterval) async -> Bool {
await withCheckedContinuation { continuation in
let lock = NSLock()
var resumed = false
func resumeOnce(_ value: Bool) {
lock.lock(); defer { lock.unlock() }
guard !resumed else { return }
resumed = true
continuation.resume(returning: value)
}
let timeoutTask = Task {
try? await Task.sleep(for: .seconds(timeout))
if process.isRunning { process.terminate() }
resumeOnce(false)
}
process.terminationHandler = { _ in
timeoutTask.cancel()
resumeOnce(true)
}
}
}
The locking is needed because terminationHandler and the timeout task can race.
Acceptance criteria
- Replace the polling loop in
Services/GitService.swift
- All existing tests / build still pass
- Manual smoke test: open a repo, perform a few commits / pushes, observe
Activity Monitor no longer shows extra busy threads while git is running
Context
GitService.waitForProcess(_:timeout:)currently pollsprocess.isRunningin a loop withThread.sleep(0.05)on a global concurrent queue. That burns a CPU slot and ties up a worker thread for the entire duration of every git command (which adds up: every refresh fans out to status, log, branches, currentBranch, stash list).Suggested by feedback on r/swift.
Proposal
Bridge
Process.terminationHandlerdirectly into awithCheckedContinuation. Process fires the handler the instant the child exits, so no polling is needed. Race it against aTask.sleep-based timeout that callsprocess.terminate()and resumes withfalse.Sketch:
The locking is needed because
terminationHandlerand the timeout task can race.Acceptance criteria
Services/GitService.swiftActivity Monitorno longer shows extra busy threads while git is running