Skip to content

Commit

Permalink
Separate handling for stdError pipe and logging multiple lines at onc…
Browse files Browse the repository at this point in the history
…e i.e. performant logging.
  • Loading branch information
SoaurabhK committed Aug 31, 2020
1 parent b6ad404 commit 4a374ab
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 21 deletions.
25 changes: 17 additions & 8 deletions XCTool/REPLBuffer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,20 @@ struct REPLBuffer {
private var buffer = Data()
private let delimData = String("\n").data(using: .utf8)!

mutating func append(_ data: Data) -> String? {
mutating func append(_ data: Data) -> [String]? {
buffer.append(data)
return getLine()
return getLines()
}

mutating func outstandingText() -> [String]? {
defer {
buffer.removeAll()
buffer.count = 0
}
guard buffer.count > 0 else {
return nil
}

var result = [String]()
while let line = getLine() {
result.append(line)
// Get all complete lines from buffer
guard var result = getLines() else {
return nil
}

// Get remaining buffer text(if any)
Expand All @@ -39,6 +36,18 @@ struct REPLBuffer {
return result
}

private mutating func getLines() -> [String]? {
guard buffer.count > 0 else {
return nil
}

var result = [String]()
while let line = getLine() {
result.append(line)
}
return result.isEmpty ? nil : result
}

private mutating func getLine() -> String? {
guard let range = buffer.range(of: delimData) else { return nil }

Expand Down
45 changes: 32 additions & 13 deletions XCTool/REPLCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ struct REPLCommand {
let arguments: [String]

func run() -> (result: Data?, exitStatus: Int32) {
let (task, pipe) = launchProcess(at: launchPath, with: arguments)
let (task, stdOutPipe, _) = launchProcess(at: launchPath, with: arguments)
let outdata = autoreleasepool { () -> Data? in
pipe.fileHandleForReading.readDataToEndOfFile()
stdOutPipe.fileHandleForReading.readDataToEndOfFile()
}
task.waitUntilExit()
let status = task.terminationStatus
Expand All @@ -24,22 +24,37 @@ struct REPLCommand {

@discardableResult
func run(readabilityHandler: @escaping (([String]?) -> Void)) -> Int32 {
let (task, pipe) = launchProcess(at: launchPath, with: arguments)
let (task, stdOutPipe, stdErrPipe) = launchProcess(at: launchPath, with: arguments)

let outHandle = pipe.fileHandleForReading
var buffer = REPLBuffer()
let outHandle = stdOutPipe.fileHandleForReading
let errHandle = stdErrPipe.fileHandleForReading
var outBuffer = REPLBuffer()
var errBuffer = REPLBuffer()
let group = DispatchGroup()

group.enter()
outHandle.readabilityHandler = { fileHandle in
let availableOutData = fileHandle.availableData
if availableOutData.isEmpty {
//EOF reached
readabilityHandler(buffer.outstandingText())
readabilityHandler(outBuffer.outstandingText())
outHandle.readabilityHandler = nil
group.leave()
} else if let line = buffer.append(availableOutData) {
readabilityHandler([line])
} else if let lines = outBuffer.append(availableOutData) {
readabilityHandler(lines)
}
}

group.enter()
errHandle.readabilityHandler = { fileHandle in
let availableOutData = fileHandle.availableData
if availableOutData.isEmpty {
//EOF reached
readabilityHandler(errBuffer.outstandingText())
errHandle.readabilityHandler = nil
group.leave()
} else if let lines = errBuffer.append(availableOutData) {
readabilityHandler(lines)
}
}

Expand All @@ -52,16 +67,20 @@ struct REPLCommand {
return status
}

private func launchProcess(at launchPath: String, with arguments: [String]) -> (task: Process, pipe: Pipe) {
return autoreleasepool { () -> (Process, Pipe) in
private func launchProcess(at launchPath: String, with arguments: [String]) -> (task: Process, stdOutPipe: Pipe, stdErrPipe: Pipe) {
return autoreleasepool { () -> (Process, Pipe, Pipe) in
let task = Process()
task.executableURL = URL(fileURLWithPath: launchPath)
task.arguments = arguments

let pipe = Pipe()
task.standardOutput = pipe
let stdOutPipe = Pipe()
task.standardOutput = stdOutPipe

let stdErrPipe = Pipe()
task.standardError = stdErrPipe

try? task.run()
return (task, pipe)
return (task, stdOutPipe, stdErrPipe)
}
}
}

0 comments on commit 4a374ab

Please sign in to comment.