Skip to content

Commit 1a91649

Browse files
committed
Split CreatedPipe to InputPipe and OutputPipe to avoid runtime casting
1 parent 51293ff commit 1a91649

10 files changed

+202
-195
lines changed

Sources/Subprocess/AsyncBufferSequence.swift

+4-4
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,12 @@ public struct AsyncBufferSequence: AsyncSequence, Sendable {
2626
public struct Iterator: AsyncIteratorProtocol {
2727
public typealias Element = SequenceOutput.Buffer
2828

29-
private let diskIO: DiskIO
29+
private let diskIO: TrackedPlatformDiskIO
3030
private var buffer: [UInt8]
3131
private var currentPosition: Int
3232
private var finished: Bool
3333

34-
internal init(diskIO: DiskIO) {
34+
internal init(diskIO: TrackedPlatformDiskIO) {
3535
self.diskIO = diskIO
3636
self.buffer = []
3737
self.currentPosition = 0
@@ -51,9 +51,9 @@ public struct AsyncBufferSequence: AsyncSequence, Sendable {
5151
}
5252
}
5353

54-
private let diskIO: DiskIO
54+
private let diskIO: TrackedPlatformDiskIO
5555

56-
internal init(diskIO: DiskIO) {
56+
internal init(diskIO: TrackedPlatformDiskIO) {
5757
self.diskIO = diskIO
5858
}
5959

Sources/Subprocess/Configuration.swift

+50-61
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ public struct Configuration: Sendable {
101101
// Body runs in the same isolation
102102
let result = try await body(
103103
execution,
104-
.init(fileDescriptor: execution.inputPipe.writeFileDescriptor!)
104+
.init(diskIO: execution.inputPipe.writeEnd!)
105105
)
106106
return ExecutionResult(
107107
terminationStatus: try await waitingStatus,
@@ -165,7 +165,7 @@ public struct Configuration: Sendable {
165165
standardError
166166
) = try await execution.captureIOs()
167167
// Write input in the same scope
168-
guard let writeFd = execution.inputPipe.writeFileDescriptor else {
168+
guard let writeFd = execution.inputPipe.writeEnd else {
169169
fatalError("Trying to write to an input that has been closed")
170170
}
171171
try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Void, any Swift.Error>) in
@@ -256,8 +256,8 @@ public struct Configuration: Sendable {
256256
returning: ExecutionResult.self
257257
) { group in
258258
group.addTask {
259-
if let writeFd = execution.inputPipe.writeFileDescriptor {
260-
let writer = StandardInputWriter(fileDescriptor: writeFd)
259+
if let writeFd = execution.inputPipe.writeEnd {
260+
let writer = StandardInputWriter(diskIO: writeFd)
261261
try await input.write(with: writer)
262262
try await writer.finish()
263263
}
@@ -363,25 +363,25 @@ extension Configuration {
363363

364364
if childSide {
365365
inputError = captureError {
366-
try execution.inputPipe.readFileDescriptor?.safelyClose()
366+
try execution.inputPipe.readEnd?.safelyClose()
367367
}
368368
outputError = captureError {
369-
try execution.outputPipe.writeFileDescriptor?.safelyClose()
369+
try execution.outputPipe.writeEnd?.safelyClose()
370370
}
371371
errorError = captureError {
372-
try execution.errorPipe.writeFileDescriptor?.safelyClose()
372+
try execution.errorPipe.writeEnd?.safelyClose()
373373
}
374374
}
375375

376376
if parentSide {
377377
inputError = captureError {
378-
try execution.inputPipe.writeFileDescriptor?.safelyClose()
378+
try execution.inputPipe.writeEnd?.safelyClose()
379379
}
380380
outputError = captureError {
381-
try execution.outputPipe.readFileDescriptor?.safelyClose()
381+
try execution.outputPipe.readEnd?.safelyClose()
382382
}
383383
errorError = captureError {
384-
try execution.errorPipe.readFileDescriptor?.safelyClose()
384+
try execution.errorPipe.readEnd?.safelyClose()
385385
}
386386
}
387387

@@ -801,51 +801,27 @@ internal enum StringOrRawBytes: Sendable, Hashable {
801801
}
802802
}
803803

804-
/// A wrapped `FileDescriptor` or `DispatchIO` and
805-
/// whether it should beeddsw closed automactially when done.
806-
internal struct DiskIO {
807-
internal enum Storage {
808-
case fileDescriptor(FileDescriptor)
809-
#if !os(Windows) // Darwin and Linux
810-
case dispatchIO(DispatchIO)
811-
#endif
812-
}
813-
804+
/// A wrapped `FileDescriptor` and whether it should be closed
805+
/// automactially when done.
806+
internal struct TrackedFileDescriptor {
814807
internal let closeWhenDone: Bool
815-
internal let storage: Storage
808+
internal let fileDescriptor: FileDescriptor
816809

817810
internal init(
818811
_ fileDescriptor: FileDescriptor,
819812
closeWhenDone: Bool
820813
) {
821-
self.storage = .fileDescriptor(fileDescriptor)
822-
self.closeWhenDone = closeWhenDone
823-
}
824-
825-
#if !os(Windows)
826-
internal init(
827-
_ dispatchIO: DispatchIO,
828-
closeWhenDone: Bool
829-
) {
830-
self.storage = .dispatchIO(dispatchIO)
814+
self.fileDescriptor = fileDescriptor
831815
self.closeWhenDone = closeWhenDone
832816
}
833-
#endif
834817

835818
internal func safelyClose() throws {
836819
guard self.closeWhenDone else {
837820
return
838821
}
839822

840823
do {
841-
switch self.storage {
842-
case .fileDescriptor(let fileDescriptor):
843-
try fileDescriptor.close()
844-
#if !os(Windows)
845-
case .dispatchIO(let dispatchIO):
846-
dispatchIO.close()
847-
#endif
848-
}
824+
try fileDescriptor.close()
849825
} catch {
850826
guard let errno: Errno = error as? Errno else {
851827
throw error
@@ -857,38 +833,52 @@ internal struct DiskIO {
857833
}
858834

859835
internal var platformDescriptor: PlatformFileDescriptor {
860-
switch self.storage {
861-
case .fileDescriptor(let fileDescriptor):
862-
return fileDescriptor.platformDescriptor
863-
#if !os(Windows)
864-
case .dispatchIO(let dispatchIO):
865-
return dispatchIO.fileDescriptor
866-
#endif // !os(Windows)
867-
}
836+
return self.fileDescriptor.platformDescriptor
868837
}
869838
}
870839

871-
internal struct CreatedPipe {
872-
internal enum PipeEnd {
873-
case readEnd
874-
case writeEnd
840+
#if !os(Windows)
841+
/// A wrapped `DispatchIO` and whether it should be closed
842+
/// automactially when done.
843+
internal struct TrackedDispatchIO {
844+
internal let closeWhenDone: Bool
845+
internal let dispatchIO: DispatchIO
846+
847+
internal init(
848+
_ dispatchIO: DispatchIO,
849+
closeWhenDone: Bool
850+
) {
851+
self.dispatchIO = dispatchIO
852+
self.closeWhenDone = closeWhenDone
875853
}
876854

877-
internal let readFileDescriptor: DiskIO?
878-
internal let writeFileDescriptor: DiskIO?
879-
internal let parentEnd: PipeEnd
855+
internal func safelyClose() throws {
856+
guard self.closeWhenDone else {
857+
return
858+
}
859+
860+
dispatchIO.close()
861+
}
862+
863+
internal var platformDescriptor: PlatformFileDescriptor {
864+
return self.dispatchIO.fileDescriptor
865+
}
866+
}
867+
#endif
868+
869+
internal struct CreatedPipe {
870+
internal let readFileDescriptor: TrackedFileDescriptor?
871+
internal let writeFileDescriptor: TrackedFileDescriptor?
880872

881873
internal init(
882-
readFileDescriptor: DiskIO?,
883-
writeFileDescriptor: DiskIO?,
884-
parentEnd: PipeEnd
874+
readFileDescriptor: TrackedFileDescriptor?,
875+
writeFileDescriptor: TrackedFileDescriptor?,
885876
) {
886877
self.readFileDescriptor = readFileDescriptor
887878
self.writeFileDescriptor = writeFileDescriptor
888-
self.parentEnd = parentEnd
889879
}
890880

891-
internal init(closeWhenDone: Bool, parentEnd: PipeEnd) throws {
881+
internal init(closeWhenDone: Bool) throws {
892882
let pipe = try FileDescriptor.ssp_pipe()
893883
self.readFileDescriptor = .init(
894884
pipe.readEnd,
@@ -898,7 +888,6 @@ internal struct CreatedPipe {
898888
pipe.writeEnd,
899889
closeWhenDone: closeWhenDone
900890
)
901-
self.parentEnd = parentEnd
902891
}
903892
}
904893

Sources/Subprocess/Execution.swift

+13-13
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,9 @@ public final class Execution<
4242

4343
internal let output: Output
4444
internal let error: Error
45-
internal let inputPipe: CreatedPipe
46-
internal let outputPipe: CreatedPipe
47-
internal let errorPipe: CreatedPipe
45+
internal let inputPipe: InputPipe
46+
internal let outputPipe: OutputPipe
47+
internal let errorPipe: OutputPipe
4848
internal let outputConsumptionState: AtomicBox
4949

5050
#if os(Windows)
@@ -54,9 +54,9 @@ public final class Execution<
5454
processIdentifier: ProcessIdentifier,
5555
output: Output,
5656
error: Error,
57-
inputPipe: CreatedPipe,
58-
outputPipe: CreatedPipe,
59-
errorPipe: CreatedPipe,
57+
inputPipe: InputPipe,
58+
outputPipe: OutputPipe,
59+
errorPipe: OutputPipe,
6060
consoleBehavior: PlatformOptions.ConsoleBehavior
6161
) {
6262
self.processIdentifier = processIdentifier
@@ -73,9 +73,9 @@ public final class Execution<
7373
processIdentifier: ProcessIdentifier,
7474
output: Output,
7575
error: Error,
76-
inputPipe: CreatedPipe,
77-
outputPipe: CreatedPipe,
78-
errorPipe: CreatedPipe
76+
inputPipe: InputPipe,
77+
outputPipe: OutputPipe,
78+
errorPipe: OutputPipe
7979
) {
8080
self.processIdentifier = processIdentifier
8181
self.output = output
@@ -103,7 +103,7 @@ extension Execution where Output == SequenceOutput {
103103
)
104104

105105
guard consumptionState.contains(.standardOutputConsumed),
106-
let readFd = self.outputPipe.readFileDescriptor
106+
let readFd = self.outputPipe.readEnd
107107
else {
108108
fatalError("The standard output has already been consumed")
109109
}
@@ -126,7 +126,7 @@ extension Execution where Error == SequenceOutput {
126126
)
127127

128128
guard consumptionState.contains(.standardErrorConsumed),
129-
let readFd = self.errorPipe.readFileDescriptor
129+
let readFd = self.errorPipe.readEnd
130130
else {
131131
fatalError("The standard output has already been consumed")
132132
}
@@ -170,13 +170,13 @@ extension Execution {
170170
) { group in
171171
group.addTask {
172172
let stdout = try await self.output.captureOutput(
173-
from: self.outputPipe.readFileDescriptor
173+
from: self.outputPipe.readEnd
174174
)
175175
return .standardOutputCaptured(stdout)
176176
}
177177
group.addTask {
178178
let stderr = try await self.error.captureOutput(
179-
from: self.errorPipe.readFileDescriptor
179+
from: self.errorPipe.readEnd
180180
)
181181
return .standardErrorCaptured(stderr)
182182
}

Sources/Subprocess/IO/Input.swift

+24-10
Original file line numberDiff line numberDiff line change
@@ -50,15 +50,13 @@ public struct NoInput: InputProtocol {
5050
// to signal no input
5151
return CreatedPipe(
5252
readFileDescriptor: nil,
53-
writeFileDescriptor: nil,
54-
parentEnd: .writeEnd
53+
writeFileDescriptor: nil
5554
)
5655
#else
5756
let devnull: FileDescriptor = try .openDevNull(withAcessMode: .readOnly)
5857
return CreatedPipe(
5958
readFileDescriptor: .init(devnull, closeWhenDone: true),
60-
writeFileDescriptor: nil,
61-
parentEnd: .writeEnd
59+
writeFileDescriptor: nil
6260
)
6361
#endif
6462
}
@@ -85,8 +83,7 @@ public struct FileDescriptorInput: InputProtocol {
8583
self.fileDescriptor,
8684
closeWhenDone: self.closeAfterSpawningProcess
8785
),
88-
writeFileDescriptor: nil,
89-
parentEnd: .writeEnd
86+
writeFileDescriptor: nil
9087
)
9188
}
9289

@@ -206,7 +203,7 @@ extension InputProtocol {
206203
return try fdInput.createPipe()
207204
}
208205
// Base implementation
209-
return try CreatedPipe(closeWhenDone: true, parentEnd: .writeEnd)
206+
return try CreatedPipe(closeWhenDone: true)
210207
}
211208
}
212209

@@ -215,10 +212,10 @@ extension InputProtocol {
215212
/// A writer that writes to the standard input of the subprocess.
216213
public final actor StandardInputWriter: Sendable {
217214

218-
internal let diskIO: DiskIO
215+
internal let diskIO: TrackedPlatformDiskIO
219216

220-
init(fileDescriptor: DiskIO) {
221-
self.diskIO = fileDescriptor
217+
init(diskIO: TrackedPlatformDiskIO) {
218+
self.diskIO = diskIO
222219
}
223220

224221
/// Write an array of UInt8 to the standard input of the subprocess.
@@ -261,6 +258,23 @@ public final actor StandardInputWriter: Sendable {
261258
}
262259
}
263260

261+
262+
// MARK: - InputPipe
263+
internal struct InputPipe {
264+
// On Darwin and Linux, parent end (write end) should be
265+
// wrapped as `DispatchIO` for writing
266+
internal let readEnd: TrackedFileDescriptor?
267+
internal let writeEnd: TrackedPlatformDiskIO?
268+
269+
internal init(
270+
readEnd: TrackedFileDescriptor?,
271+
writeEnd: TrackedPlatformDiskIO?
272+
) {
273+
self.readEnd = readEnd
274+
self.writeEnd = writeEnd
275+
}
276+
}
277+
264278
extension StringProtocol {
265279
#if SubprocessFoundation
266280
private func convertEncoding<Encoding: Unicode.Encoding>(

0 commit comments

Comments
 (0)