@@ -181,7 +181,8 @@ public final class Process: ObjectIdentifierProtocol {
181
181
// process execution mutable state
182
182
private enum State {
183
183
case idle
184
- case readingOutput( stdout: Thread , stderr: Thread ? )
184
+ case readingOutputThread( stdout: Thread , stderr: Thread ? )
185
+ case readingOutputPipe( sync: DispatchGroup )
185
186
case outputReady( stdout: Result < [ UInt8 ] , Swift . Error > , stderr: Result < [ UInt8 ] , Swift . Error > )
186
187
case complete( ProcessResult )
187
188
}
@@ -403,36 +404,44 @@ public final class Process: ObjectIdentifierProtocol {
403
404
let stdinPipe = Pipe ( )
404
405
_process? . standardInput = stdinPipe
405
406
407
+ let group = DispatchGroup ( )
408
+
409
+ var stdout : [ UInt8 ] = [ ]
410
+ let stdoutLock = Lock ( )
411
+
412
+ var stderr : [ UInt8 ] = [ ]
413
+ let stderrLock = Lock ( )
414
+
406
415
if outputRedirection. redirectsOutput {
407
416
let stdoutPipe = Pipe ( )
408
417
let stderrPipe = Pipe ( )
409
418
410
- var pending : [ UInt8 ] ?
411
- let pendingLock = Lock ( )
412
-
419
+ group. enter ( )
413
420
stdoutPipe. fileHandleForReading. readabilityHandler = { ( fh : FileHandle ) -> Void in
414
- let contents = fh. readDataToEndOfFile ( )
415
- self . outputRedirection . outputClosures ? . stdoutClosure ( [ UInt8 ] ( contents ) )
416
- pendingLock . withLock {
417
- if let stderr = pending {
418
- self . stateLock . withLock {
419
- self . state = . outputReady ( stdout : . success ( contents . withUnsafeBytes { Array < UInt8 > ( $0) } ) , stderr : . success ( stderr ) )
420
- }
421
- } else {
422
- pending = contents. withUnsafeBytes { Array < UInt8 > ( $0 ) }
421
+ let data = fh. availableData
422
+ if ( data . count == 0 ) {
423
+ stdoutPipe . fileHandleForReading . readabilityHandler = nil
424
+ group . leave ( )
425
+ } else {
426
+ let contents = data . withUnsafeBytes { Array < UInt8 > ( $0) }
427
+ self . outputRedirection . outputClosures ? . stdoutClosure ( contents )
428
+ stdoutLock . withLock {
429
+ stdout + = contents
423
430
}
424
431
}
425
432
}
433
+
434
+ group. enter ( )
426
435
stderrPipe. fileHandleForReading. readabilityHandler = { ( fh : FileHandle ) -> Void in
427
- let contents = fh. readDataToEndOfFile ( )
428
- self . outputRedirection . outputClosures ? . stderrClosure ( [ UInt8 ] ( contents ) )
429
- pendingLock . withLock {
430
- if let stdout = pending {
431
- self . stateLock . withLock {
432
- self . state = . outputReady ( stdout : . success ( stdout ) , stderr : . success ( contents . withUnsafeBytes { Array < UInt8 > ( $0) } ) )
433
- }
434
- } else {
435
- pending = contents. withUnsafeBytes { Array < UInt8 > ( $0 ) }
436
+ let data = fh. availableData
437
+ if ( data . count == 0 ) {
438
+ stderrPipe . fileHandleForReading . readabilityHandler = nil
439
+ group . leave ( )
440
+ } else {
441
+ let contents = data . withUnsafeBytes { Array < UInt8 > ( $0) }
442
+ self . outputRedirection . outputClosures ? . stderrClosure ( contents )
443
+ stderrLock . withLock {
444
+ stderr + = contents
436
445
}
437
446
}
438
447
}
@@ -441,6 +450,20 @@ public final class Process: ObjectIdentifierProtocol {
441
450
_process? . standardError = stderrPipe
442
451
}
443
452
453
+ // first set state then start reading threads
454
+ let sync = DispatchGroup ( )
455
+ sync. enter ( )
456
+ self . stateLock. withLock {
457
+ self . state = . readingOutputPipe( sync: sync)
458
+ }
459
+
460
+ group. notify ( queue: . global( ) ) {
461
+ self . stateLock. withLock {
462
+ self . state = . outputReady( stdout: . success( stdout) , stderr: . success( stderr) )
463
+ }
464
+ sync. leave ( )
465
+ }
466
+
444
467
try _process? . run ( )
445
468
return stdinPipe. fileHandleForWriting
446
469
#else
@@ -640,7 +663,7 @@ public final class Process: ObjectIdentifierProtocol {
640
663
}
641
664
// first set state then start reading threads
642
665
self . stateLock. withLock {
643
- self . state = . readingOutput ( stdout: stdoutThread, stderr: stderrThread)
666
+ self . state = . readingOutputThread ( stdout: stdoutThread, stderr: stderrThread)
644
667
}
645
668
stdoutThread. start ( )
646
669
stderrThread? . start ( )
@@ -653,22 +676,6 @@ public final class Process: ObjectIdentifierProtocol {
653
676
/// Blocks the calling process until the subprocess finishes execution.
654
677
@discardableResult
655
678
public func waitUntilExit( ) throws -> ProcessResult {
656
- #if os(Windows)
657
- precondition ( _process != nil , " The process is not yet launched. " )
658
- let p = _process!
659
- p. waitUntilExit ( )
660
- stdout. thread? . join ( )
661
- stderr. thread? . join ( )
662
-
663
- let executionResult = ProcessResult (
664
- arguments: arguments,
665
- environment: environment,
666
- exitStatusCode: p. terminationStatus,
667
- output: stdout. result,
668
- stderrOutput: stderr. result
669
- )
670
- return executionResult
671
- #else
672
679
self . stateLock. lock ( )
673
680
switch self . state {
674
681
case . idle:
@@ -677,15 +684,25 @@ public final class Process: ObjectIdentifierProtocol {
677
684
case . complete( let result) :
678
685
defer { self . stateLock. unlock ( ) }
679
686
return result
680
- case . readingOutput ( let stdoutThread, let stderrThread) :
687
+ case . readingOutputThread ( let stdoutThread, let stderrThread) :
681
688
self . stateLock. unlock ( ) // unlock early since output read thread need to change state
682
689
// If we're reading output, make sure that is finished.
683
690
stdoutThread. join ( )
684
691
stderrThread? . join ( )
685
692
return try self . waitUntilExit ( )
693
+ case . readingOutputPipe( let sync) :
694
+ self . stateLock. unlock ( ) // unlock early since output read thread need to change state
695
+ sync. wait ( )
696
+ return try self . waitUntilExit ( )
686
697
case . outputReady( let stdoutResult, let stderrResult) :
687
698
defer { self . stateLock. unlock ( ) }
688
699
// Wait until process finishes execution.
700
+ #if os(Windows)
701
+ precondition ( _process != nil , " The process is not yet launched. " )
702
+ let p = _process!
703
+ p. waitUntilExit ( )
704
+ let exitStatusCode = p. terminationStatus
705
+ #else
689
706
var exitStatusCode : Int32 = 0
690
707
var result = waitpid ( processID, & exitStatusCode, 0 )
691
708
while result == - 1 && errno == EINTR {
@@ -694,6 +711,7 @@ public final class Process: ObjectIdentifierProtocol {
694
711
if result == - 1 {
695
712
throw SystemError . waitpid ( errno)
696
713
}
714
+ #endif
697
715
698
716
// Construct the result.
699
717
let executionResult = ProcessResult (
@@ -706,7 +724,6 @@ public final class Process: ObjectIdentifierProtocol {
706
724
self . state = . complete( executionResult)
707
725
return executionResult
708
726
}
709
- #endif
710
727
}
711
728
712
729
#if !os(Windows)
0 commit comments