指定stderr管道时,由macOS任务/进程随机挂起

时间:2017-01-08 20:55:25

标签: swift macos nstask

注意:这被认为类似于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作为输入源时始终成功,则还需要将它们复制到那里。

0 个答案:

没有答案