注意:这被认为类似于cURL through NSTask not terminating if a pipe is present,我最近获得了200点赏金。我在这里发帖是为了提供我自己案例的其他细节。
当粘贴到swift
REPL(使用Swift 3)时,以下代码会随MacOS文件系统上的相关文件随机挂起,我的机器上的概率大于50%:
import Foundation
func test() {
func innerTest(filenames: String...) {
let task = Process()
let pipe = Pipe()
let choice = true ? 1 : Int(arc4random_uniform(2))
let dir = ["/tmp", "/tmp/a/b/c/d/e/f/g/"][choice]
print(dir)
task.launchPath = "/usr/bin/swiftc"
let inputPaths = filenames.map { dir + $0 + ".swift" }
let outputFile = "/tmp/" + filenames.joined() + ".ast"
task.arguments = ["-dump-ast"] + inputPaths
task.standardError = pipe
task.launch()
task.waitUntilExit()
try! String(data: pipe.fileHandleForReading.readDataToEndOfFile(), encoding: String.Encoding.utf8)!.write(toFile: outputFile, atomically: true, encoding: String.Encoding.utf8)
}
innerTest(filenames: "AST", "Token")
}
test()
与其他stackoverflow问题一样,除非在任务/进程上设置管道,否则永远不会发生挂起。
存在用于在/tmp
和/tmp/a/b/c/d/e/f/g
之间选择作为输入文件源的代码,因为在使用/tmp
作为输入时它永远不会挂起,并且在使用较长目录时会随机挂起路径。我把它留在那里让一个人进行实验。
AST.swift的内容是:
struct AST {
let type: String
let implicit: Bool
let name: String?
let attributes: [String:String]
let elements: [AST]
init(type: String, implicit: Bool = false, name: String? = nil, attributes: [String:String] = [:], elements: [AST] = []) {
self.type = type
self.implicit = implicit
self.name = name
self.attributes = attributes
self.elements = elements
}
}
Token.swift的内容是:
class Token: Equatable {
private class LeftParen: Token {}
static let leftParen: Token = LeftParen()
private class RightParen: Token {}
static let rightParen: Token = RightParen()
class String: Token {
let value: Swift.String
init(_ value: Swift.String) { self.value = value }
}
class Symbol: Token {
let value: Swift.String
init(_ value: Swift.String) { self.value = value }
}
class KeyValue: Token {
let key: Swift.String
let value: Swift.String
init(_ key: Swift.String, _ value: Swift.String) {
self.key = key
self.value = value
}
}
static func symbol(_ s: Swift.String) -> Symbol { return Symbol(s) }
static func string(_ s: Swift.String) -> String { return String(s) }
static func keyValue(_ k: Swift.String, _ v: Swift.String) -> KeyValue { return KeyValue(k, v) }
func toString() -> Swift.String {
switch self {
case Token.leftParen:
return "("
case Token.rightParen:
return ")"
case let token as Token.String:
return token.value
case let token as Token.Symbol:
return token.value
case let token as Token.KeyValue:
return "\(token.key)=\(token.value)"
default:
fatalError()
}
}
}
func ==(lhs: Token, rhs: Token) -> Bool {
if let lhs = lhs as? Token.String, let rhs = rhs as? Token.String {
return lhs.value == rhs.value
}
if let lhs = lhs as? Token.Symbol, let rhs = rhs as? Token.Symbol {
return lhs.value == rhs.value
}
if let lhs = lhs as? Token.KeyValue, let rhs = rhs as? Token.KeyValue {
return lhs.key == rhs.key && lhs.value == rhs.value
}
return lhs === rhs
}
以上两个.swift文件需要放在/tmp/a/b/c/d/e/f/g
目录中才能运行测试。如果您希望在使用/tmp
作为输入源时始终成功,则还需要将它们复制到那里。