Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 17 additions & 12 deletions programaTests/WorkspaceRemoteConnectionTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,18 @@ final class WorkspaceRemoteConnectionTests: XCTestCase {
process.standardOutput = stdoutPipe
process.standardError = stderrPipe

// Signalled from Foundation's own termination callback rather than by parking a
// `DispatchQueue.global()` worker in `waitUntilExit()`. The old shape blocked one
// pooled thread for the entire lifetime of every child process; run enough of these
// concurrently (the suite runs in parallel) and the global pool saturates, so the
// block that signals this semaphore can sit unscheduled long past the child's actual
// exit. The test then reports a timeout at exactly its configured limit for a
// subprocess that finished in milliseconds -- observed repeatedly on CI as 90s
// failures of scripts that run in ~15ms locally. `terminationHandler` is delivered on
// Foundation's private queue and blocks nothing. Must be set before `run()`.
let exitSignal = DispatchSemaphore(value: 0)
process.terminationHandler = { _ in exitSignal.signal() }

do {
try process.run()
} catch {
Expand All @@ -39,12 +51,6 @@ final class WorkspaceRemoteConnectionTests: XCTestCase {
)
}

let exitSignal = DispatchSemaphore(value: 0)
DispatchQueue.global(qos: .userInitiated).async {
process.waitUntilExit()
exitSignal.signal()
}

let timedOut = exitSignal.wait(timeout: .now() + timeout) == .timedOut
if timedOut {
process.terminate()
Expand Down Expand Up @@ -1486,6 +1492,11 @@ final class CLINotifyProcessIntegrationTests: XCTestCase {
process.standardOutput = stdoutPipe
process.standardError = stderrPipe

// See the sibling `runProcess` above for why this uses `terminationHandler` instead of
// parking a pooled thread in `waitUntilExit()`.
let exitSignal = DispatchSemaphore(value: 0)
process.terminationHandler = { _ in exitSignal.signal() }

do {
try process.run()
} catch {
Expand All @@ -1497,12 +1508,6 @@ final class CLINotifyProcessIntegrationTests: XCTestCase {
)
}

let exitSignal = DispatchSemaphore(value: 0)
DispatchQueue.global(qos: .userInitiated).async {
process.waitUntilExit()
exitSignal.signal()
}

let timedOut = exitSignal.wait(timeout: .now() + timeout) == .timedOut
if timedOut {
process.terminate()
Expand Down
Loading