Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Synchronize reads/writes to outputData and errorData #25

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
49 changes: 33 additions & 16 deletions Sources/ShellOut.swift
Expand Up @@ -5,6 +5,7 @@
*/

import Foundation
import Dispatch

// MARK: - API

Expand Down Expand Up @@ -349,6 +350,12 @@ private extension Process {
launchPath = "/bin/bash"
arguments = ["-c", command]

// Because FileHandle's readabilityHandler might be called from a
// different queue from the calling queue, avoid a data race by
// protecting reads and writes to outputData and errorData on
// a single dispatch queue.
let outputQueue = DispatchQueue(label: "bash-output-queue")

var outputData = Data()
var errorData = Data()

Expand All @@ -360,23 +367,29 @@ private extension Process {

#if !os(Linux)
outputPipe.fileHandleForReading.readabilityHandler = { handler in
let data = handler.availableData
outputData.append(data)
outputHandle?.write(data)
outputQueue.async {
let data = handler.availableData
outputData.append(data)
outputHandle?.write(data)
}
}

errorPipe.fileHandleForReading.readabilityHandler = { handler in
let data = handler.availableData
errorData.append(data)
errorHandle?.write(data)
outputQueue.async {
let data = handler.availableData
errorData.append(data)
errorHandle?.write(data)
}
}
#endif

launch()

#if os(Linux)
outputData = outputPipe.fileHandleForReading.readDataToEndOfFile()
errorData = errorPipe.fileHandleForReading.readDataToEndOfFile()
outputQueue.sync {
outputData = outputPipe.fileHandleForReading.readDataToEndOfFile()
errorData = errorPipe.fileHandleForReading.readDataToEndOfFile()
}
#endif

waitUntilExit()
Expand All @@ -389,15 +402,19 @@ private extension Process {
errorPipe.fileHandleForReading.readabilityHandler = nil
#endif

if terminationStatus != 0 {
throw ShellOutError(
terminationStatus: terminationStatus,
errorData: errorData,
outputData: outputData
)
// Block until all writes have occurred to outputData and errorData,
// and then read the data back out.
return try outputQueue.sync {
if terminationStatus != 0 {
throw ShellOutError(
terminationStatus: terminationStatus,
errorData: errorData,
outputData: outputData
)
}

return outputData.shellOutput()
}

return outputData.shellOutput()
}
}

Expand Down