Skip to content

NIO on NIOPipeBootstrap can synthesize IOError with errnoCode: EBADF (EPIPE at other times) which should probably be ChannelError.ioOnClosedChannel #3292

@weissi

Description

@weissi

throw IOError(errnoCode: EBADF, reason: "SelectablePipeHandle already closed [in wUH]")

When writing into a NIOPipeChannel that has just been closed on the other side, then it's possible to receive a NIO.IOError with errnoCode = EBADF. This is fortunately not an actual EBADF but it's still probably incorrect. The Channel should throw a ChannelError.ioOnClosedChannel and not this weird internal IOError.

What exact error you get is a bit racy, sometimes it's EPIPE too. Also questionable whether that's correct.

Repro:

Run this test

    func testStandardInputStreamWriteErrorsBlowUpSpawnOnProcessExit() async throws {
        do {
            let result = try await ProcessExecutor.runCollectingOutput(
                executable: "/bin/sh",
                ["-c",
                 #"""
                 set -e
                 read -r line
                 if [ "$line" = "go" ]; then
                     echo "GO"
                     exit 0 # We're just exiting here which will have the effect of stdin closing
                 fi
                 echo "PROBLEM"
                 while read -r line; do
                     echo "unexpected input $line"
                 done
                 exit 1
                 """#
                ],
                standardInput: sequence(
                    first: ByteBuffer(string: "go\n"),
                    next: { _ in ByteBuffer(string: "extra line\n") } // infinite sequence
                ).async,
                collectStandardOutput: true,
                collectStandardError: true
            )
            XCTFail("unexpected result: \(result)")
        } catch let error as NIO.IOError {
            XCTAssert(
                [
                    EPIPE,
                    EBADF /* don't worry, this is a NIO-synthesised (already closed) EBADF */
                ].contains(error.errnoCode),
                "unexpected error: \(error)"
            )
        }
    }

with ProcessExecutor from https://github.com/swiftlang/swift-sdk-generator/tree/main/Sources/AsyncProcess

Metadata

Metadata

Assignees

No one assigned

    Labels

    kind/bugFeature doesn't work as expected.size/MMedium task. (A couple of days of work.)status/needs-designNeeds further discussion and a concrete proposal.

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions