Skip to content

Commit

Permalink
Use a concurrent queue to wait for process completion (#450)
Browse files Browse the repository at this point in the history
Calling blocking `waitpid` on Swift concurrency threads can cause freezes for `async` code when spawning too many processes with the existing `Process` API.
  • Loading branch information
MaxDesiatov authored Dec 12, 2023
1 parent 35afcbf commit 91d1a0b
Show file tree
Hide file tree
Showing 4 changed files with 16 additions and 22 deletions.
6 changes: 3 additions & 3 deletions Sources/TSCBasic/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ add_library(TSCBasic
WritableByteStream.swift
Path.swift
PathShims.swift
Process.swift
ProcessEnv.swift
ProcessSet.swift
Process/Process.swift
Process/ProcessEnv.swift
Process/ProcessSet.swift
RegEx.swift
Result.swift
SortedArray.swift
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,11 +130,15 @@ public struct ProcessResult: CustomStringConvertible, Sendable {
}
}

#if swift(<5.6)
extension Process: UnsafeSendable {}
#else
extension Process: @unchecked Sendable {}
#endif

extension DispatchQueue {
// a shared concurrent queue for running concurrent asynchronous operations
static let processConcurrent = DispatchQueue(
label: "swift.org.swift-tsc.process.concurrent",
attributes: .concurrent
)
}

/// Process allows spawning new subprocesses and working with them.
///
Expand Down Expand Up @@ -829,25 +833,15 @@ public final class Process {
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
@discardableResult
public func waitUntilExit() async throws -> ProcessResult {
#if compiler(>=5.6)
return try await withCheckedThrowingContinuation { continuation in
waitUntilExit(continuation.resume(with:))
}
#else
if #available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *) {
return try await withCheckedThrowingContinuation { continuation in
waitUntilExit(continuation.resume(with:))
try await withCheckedThrowingContinuation { continuation in
DispatchQueue.processConcurrent.async {
self.waitUntilExit(continuation.resume(with:))
}
} else {
preconditionFailure("Unsupported with Swift 5.5 on this OS version")
}
#endif
}

/// Blocks the calling process until the subprocess finishes execution.
// #if compiler(>=5.8)
// @available(*, noasync)
// #endif
@available(*, noasync)
@discardableResult
public func waitUntilExit() throws -> ProcessResult {
let group = DispatchGroup()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import Foundation
import TSCLibc

/// Provides functionality related a process's enviorment.
/// Provides functionality related a process's environment.
public enum ProcessEnv {

/// Returns a dictionary containing the current environment.
Expand Down
File renamed without changes.

0 comments on commit 91d1a0b

Please sign in to comment.