Skip to content

Commit f1c253a

Browse files
authored
Merge pull request JohnSundell#25 from harlanhaskins/on-your-mark-get-set-go
Synchronize reads/writes to outputData and errorData
2 parents 0208caa + 6e44fc1 commit f1c253a

File tree

1 file changed

+33
-16
lines changed

1 file changed

+33
-16
lines changed

Sources/ShellOut.swift

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
*/
66

77
import 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

Comments
 (0)