mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-25 11:55:47 -06:00
8e398d9591
* fix(macos): reap app-owned child process groups macOS-owned SSH, Codex, and node-host descendants no longer survive terminal shutdown. Codex retains EOF-first graceful exit before bounded process-group termination and reaping. * fix(macos): correct managed cleanup wake binding * fix(macos): preserve Codex shutdown escalation Keep the app-owned EOF grace window while allowing abortive requests to interrupt it before process-group TERM and KILL.
48 lines
1.6 KiB
Swift
48 lines
1.6 KiB
Swift
import Darwin
|
|
import Foundation
|
|
import Testing
|
|
|
|
enum TestProcessSupport {
|
|
static func pollPID(in file: URL) -> pid_t? {
|
|
guard let value = try? String(contentsOf: file, encoding: .utf8)
|
|
.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
else { return nil }
|
|
return pid_t(value)
|
|
}
|
|
|
|
static func waitForPID(in file: URL, timeout: Duration = .seconds(2)) async throws -> pid_t {
|
|
let clock = ContinuousClock()
|
|
let deadline = clock.now.advanced(by: timeout)
|
|
while clock.now < deadline {
|
|
if let pid = self.pollPID(in: file) { return pid }
|
|
try await Task.sleep(for: .milliseconds(10))
|
|
}
|
|
return try #require(self.pollPID(in: file))
|
|
}
|
|
|
|
static func processIsGone(_ pid: pid_t) -> Bool {
|
|
errno = 0
|
|
return kill(pid, 0) == -1 && errno == ESRCH
|
|
}
|
|
|
|
static func waitUntilGone(_ pid: pid_t, timeout: Duration = .seconds(2)) async -> Bool {
|
|
let deadline = ContinuousClock.now.advanced(by: timeout)
|
|
while ContinuousClock.now < deadline {
|
|
if self.processIsGone(pid) { return true }
|
|
try? await Task.sleep(for: .milliseconds(10))
|
|
}
|
|
return self.processIsGone(pid)
|
|
}
|
|
|
|
static func killLeakedProcesses(in files: [URL]) {
|
|
let pids = files.compactMap { self.pollPID(in: $0) }
|
|
for pid in pids where !self.processIsGone(pid) {
|
|
_ = kill(pid, SIGKILL)
|
|
}
|
|
let deadline = Date().addingTimeInterval(2)
|
|
while Date() < deadline, pids.contains(where: { !self.processIsGone($0) }) {
|
|
usleep(10000)
|
|
}
|
|
}
|
|
}
|