Skip to content

Commit

Permalink
fix(posix): ensure nonblocking file descriptor is nonblocking (#10080)
Browse files Browse the repository at this point in the history
The FileReader is responsible for deciding what sort of file descriptor
has been passed to it. On MacOS, when stdin is provided to it the
FileReader assumes that it is nonblocking even if the descriptor is not,
this resulted in hangs as the socket would never raise the `EAGAIN`
error. There are a couple of ways to fix this but the one that seemed to
make the most sense was to set the fd to be nonblocking when the
FileReader deemed it so.
  • Loading branch information
alexkornitzer committed Apr 16, 2024
1 parent fbe2fe0 commit fd62f58
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 0 deletions.
5 changes: 5 additions & 0 deletions src/bun.js/webcore/streams.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3518,6 +3518,11 @@ pub const FileReader = struct {
if (this.nonblocking and this.file_type == .pipe) {
this.file_type = .nonblocking_pipe;
}

// ensure non blocking otherwise hangs will occur
if (this.nonblocking) {
bun.ensureNonBlocking(fd.int());
}
}

this.fd = fd;
Expand Down
29 changes: 29 additions & 0 deletions test/regression/issue/10080.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { test, expect } from "bun:test";
import { bunEnv, bunExe, isPosix } from "harness";
import { tmpdir } from "os";
import { join } from "path";

test.if(isPosix)("10080 - ensure nonblocking file descriptor is nonblocking in FileReader", async () => {
const expected = "foobar\n";
const filename = join(tmpdir(), "bun.test.stream." + Date.now() + ".js");
const contents = "for await (const line of console) {console.log(`foo${line}`)}"
await Bun.write(filename, contents);
const shellCommand = `exec &> >(${bunExe()} ${filename}); echo "bar"; while read -r line; do echo $line; done`;

const proc = Bun.spawn(["bash", "-c", shellCommand], {
stdin: "inherit",
stdout: "pipe",
stderr: "inherit",
env: bunEnv,
});
const { value }= await proc.stdout.getReader().read();
const output = new TextDecoder().decode(value);
if (output !== expected) {
expect(output).toEqual(expected);
throw new Error("Output didn't match!\n");
}

proc.kill(9);
await proc.exited;
expect(proc.killed).toBeTrue();
}, 1000);

0 comments on commit fd62f58

Please sign in to comment.