Skip to content

Commit af10e3e

Browse files
committed
Introduce process file descriptor (pidfd) based process monitoring for Linux
1 parent 5715ed4 commit af10e3e

File tree

10 files changed

+419
-365
lines changed

10 files changed

+419
-365
lines changed

Sources/Subprocess/API.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -753,7 +753,7 @@ public func runDetached(
753753
errorPipe: try processError.createPipe()
754754
).execution
755755
}
756-
execution.release()
756+
execution.processIdentifier.close()
757757
return execution.processIdentifier
758758
}
759759

Sources/Subprocess/Configuration.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,12 @@ public struct Configuration: Sendable {
101101
// even if `body` throws, and we are not leaving zombie processes in the
102102
// process table which will cause the process termination monitoring thread
103103
// to effectively hang due to the pid never being awaited
104-
let terminationStatus = try await Subprocess.monitorProcessTermination(forExecution: _spawnResult.execution)
104+
let terminationStatus = try await Subprocess.monitorProcessTermination(
105+
for: execution.processIdentifier
106+
)
107+
108+
// Close process file descriptor now we finished monitoring
109+
execution.processIdentifier.close()
105110

106111
return try ExecutionResult(terminationStatus: terminationStatus, value: result.get())
107112
}

Sources/Subprocess/Execution.swift

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,13 @@ public struct Execution: Sendable {
3535
public let processIdentifier: ProcessIdentifier
3636

3737
#if os(Windows)
38-
internal nonisolated(unsafe) let processInformation: PROCESS_INFORMATION
3938
internal let consoleBehavior: PlatformOptions.ConsoleBehavior
4039

4140
init(
4241
processIdentifier: ProcessIdentifier,
43-
processInformation: PROCESS_INFORMATION,
4442
consoleBehavior: PlatformOptions.ConsoleBehavior
4543
) {
4644
self.processIdentifier = processIdentifier
47-
self.processInformation = processInformation
4845
self.consoleBehavior = consoleBehavior
4946
}
5047
#else
@@ -54,17 +51,6 @@ public struct Execution: Sendable {
5451
self.processIdentifier = processIdentifier
5552
}
5653
#endif // os(Windows)
57-
58-
internal func release() {
59-
#if os(Windows)
60-
guard CloseHandle(processInformation.hThread) else {
61-
fatalError("Failed to close thread HANDLE: \(SubprocessError.UnderlyingError(rawValue: GetLastError()))")
62-
}
63-
guard CloseHandle(processInformation.hProcess) else {
64-
fatalError("Failed to close process HANDLE: \(SubprocessError.UnderlyingError(rawValue: GetLastError()))")
65-
}
66-
#endif
67-
}
6854
}
6955

7056
// MARK: - Output Capture

Sources/Subprocess/Platforms/Subprocess+Darwin.swift

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -477,26 +477,41 @@ extension Configuration {
477477
}
478478
}
479479

480-
// Special keys used in Error's user dictionary
481-
extension String {
482-
static let debugDescriptionErrorKey = "NSDebugDescription"
480+
// MARK: - ProcessIdentifier
481+
482+
/// A platform independent identifier for a Subprocess.
483+
public struct ProcessIdentifier: Sendable, Hashable {
484+
/// The platform specific process identifier value
485+
public let value: pid_t
486+
487+
public init(value: pid_t) {
488+
self.value = value
489+
}
490+
491+
internal func close() { /* No-op on Darwin */ }
492+
}
493+
494+
extension ProcessIdentifier: CustomStringConvertible, CustomDebugStringConvertible {
495+
public var description: String { "\(self.value)" }
496+
497+
public var debugDescription: String { "\(self.value)" }
483498
}
484499

485500
// MARK: - Process Monitoring
486501
@Sendable
487502
internal func monitorProcessTermination(
488-
forExecution execution: Execution
503+
for processIdentifier: ProcessIdentifier
489504
) async throws -> TerminationStatus {
490505
return try await withCheckedThrowingContinuation { continuation in
491506
let source = DispatchSource.makeProcessSource(
492-
identifier: execution.processIdentifier.value,
507+
identifier: processIdentifier.value,
493508
eventMask: [.exit],
494509
queue: .global()
495510
)
496511
source.setEventHandler {
497512
source.cancel()
498513
var siginfo = siginfo_t()
499-
let rc = waitid(P_PID, id_t(execution.processIdentifier.value), &siginfo, WEXITED)
514+
let rc = waitid(P_PID, id_t(processIdentifier.value), &siginfo, WEXITED)
500515
guard rc == 0 else {
501516
continuation.resume(
502517
throwing: SubprocessError(

0 commit comments

Comments
 (0)