diff options
| author | Tommaso Negri <me@tommasongr.com> | 2026-08-12 14:41:33 +0200 |
|---|---|---|
| committer | Tommaso Negri <me@tommasongr.com> | 2026-08-12 14:41:33 +0200 |
| commit | 9fc36c60439cc9d3f0094be1183e2f511a568a87 (patch) | |
| tree | 362ea211f6b64f9b047273a9c16ae43be895976b /Sources | |
| parent | 7283e86d42d55d84fbeae02a98e9b252c83f6c56 (diff) | |
| download | bar-9fc36c60439cc9d3f0094be1183e2f511a568a87.tar.gz bar-9fc36c60439cc9d3f0094be1183e2f511a568a87.zip | |
make sure Runner is killed after a 30 seconds timeout
Diffstat (limited to 'Sources')
| -rw-r--r-- | Sources/bar/Models/Runner.swift | 18 |
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"] |
