55 */
66
77import Foundation
8+ import Dispatch
89
910// MARK: - API
1011
@@ -349,6 +350,12 @@ private extension Process {
349350 launchPath = " /bin/bash "
350351 arguments = [ " -c " , command]
351352
353+ // Because FileHandle's readabilityHandler might be called from a
354+ // different queue from the calling queue, avoid a data race by
355+ // protecting reads and writes to outputData and errorData on
356+ // a single dispatch queue.
357+ let outputQueue = DispatchQueue ( label: " bash-output-queue " )
358+
352359 var outputData = Data ( )
353360 var errorData = Data ( )
354361
@@ -360,23 +367,29 @@ private extension Process {
360367
361368 #if !os(Linux)
362369 outputPipe. fileHandleForReading. readabilityHandler = { handler in
363- let data = handler. availableData
364- outputData. append ( data)
365- outputHandle? . write ( data)
370+ outputQueue. async {
371+ let data = handler. availableData
372+ outputData. append ( data)
373+ outputHandle? . write ( data)
374+ }
366375 }
367376
368377 errorPipe. fileHandleForReading. readabilityHandler = { handler in
369- let data = handler. availableData
370- errorData. append ( data)
371- errorHandle? . write ( data)
378+ outputQueue. async {
379+ let data = handler. availableData
380+ errorData. append ( data)
381+ errorHandle? . write ( data)
382+ }
372383 }
373384 #endif
374385
375386 launch ( )
376387
377388 #if os(Linux)
378- outputData = outputPipe. fileHandleForReading. readDataToEndOfFile ( )
379- errorData = errorPipe. fileHandleForReading. readDataToEndOfFile ( )
389+ outputQueue. sync {
390+ outputData = outputPipe. fileHandleForReading. readDataToEndOfFile ( )
391+ errorData = errorPipe. fileHandleForReading. readDataToEndOfFile ( )
392+ }
380393 #endif
381394
382395 waitUntilExit ( )
@@ -389,15 +402,19 @@ private extension Process {
389402 errorPipe. fileHandleForReading. readabilityHandler = nil
390403 #endif
391404
392- if terminationStatus != 0 {
393- throw ShellOutError (
394- terminationStatus: terminationStatus,
395- errorData: errorData,
396- outputData: outputData
397- )
405+ // Block until all writes have occurred to outputData and errorData,
406+ // and then read the data back out.
407+ return try outputQueue. sync {
408+ if terminationStatus != 0 {
409+ throw ShellOutError (
410+ terminationStatus: terminationStatus,
411+ errorData: errorData,
412+ outputData: outputData
413+ )
414+ }
415+
416+ return outputData. shellOutput ( )
398417 }
399-
400- return outputData. shellOutput ( )
401418 }
402419}
403420
0 commit comments