summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTommaso Negri <me@tommasongr.com>2026-08-12 14:41:33 +0200
committerTommaso Negri <me@tommasongr.com>2026-08-12 14:41:33 +0200
commit9fc36c60439cc9d3f0094be1183e2f511a568a87 (patch)
tree362ea211f6b64f9b047273a9c16ae43be895976b
parent7283e86d42d55d84fbeae02a98e9b252c83f6c56 (diff)
downloadbar-9fc36c60439cc9d3f0094be1183e2f511a568a87.tar.gz
bar-9fc36c60439cc9d3f0094be1183e2f511a568a87.zip
make sure Runner is killed after a 30 seconds timeout
-rw-r--r--Sources/bar/Models/Runner.swift18
1 files changed, 18 insertions, 0 deletions
diff --git a/Sources/bar/Models/Runner.swift b/Sources/bar/Models/Runner.swift
index be4dbff..dc88049 100644
--- a/Sources/bar/Models/Runner.swift
+++ b/Sources/bar/Models/Runner.swift
@@ -3,7 +3,14 @@ import Foundation
/// Runs a shell command and returns its stdout as separate lines.
///
/// Blocking — always call from a background context (e.g. `Task.detached`).
+/// A 30-second timeout prevents hanging processes from consuming
+/// cooperative pool threads indefinitely.
enum Runner {
+ /// The maximum time (in seconds) a command is allowed to run before
+ /// being killed. This prevents a single hung process from consuming
+ /// a cooperative thread indefinitely.
+ private static let timeout: Double = 30
+
/// Run a command and returns stdout as an array of separated lines.
/// - Parameters:
/// - command: The command to run.
@@ -29,10 +36,21 @@ enum Runner {
return ["ERROR"]
}
+ // Schedule a kill after the timeout expires.
+ // If the process finishes before that, the timer is cancelled.
+ let timer = DispatchSource.makeTimerSource()
+ timer.schedule(deadline: .now() + timeout)
+ timer.setEventHandler { [weak process] in
+ process?.terminate()
+ }
+ timer.resume()
+
let data = stdout.fileHandleForReading.readDataToEndOfFile()
let errData = stderr.fileHandleForReading.readDataToEndOfFile()
process.waitUntilExit()
+ timer.cancel()
+
guard process.terminationStatus == 0 else {
write(errData, to: .standardError)
return ["ERROR"]