Skip to content

A read parked on a FIFO sees its end on macOS: the IO loop waits with select (#928) - #932

Closed
PedroVIOliv wants to merge 2 commits into
bendlang:mainfrom
PedroVIOliv:fifo-eof-928
Closed

PedroVIOliv wants to merge 2 commits into
bendlang:mainfrom
PedroVIOliv:fifo-eof-928

Conversation

@PedroVIOliv

@PedroVIOliv PedroVIOliv commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #928.

What changed

macOS's poll never reports a FIFO's close, so a computation parked on one never wakes at end of file. Both lanes' io_wait now wait with select, which does report it. The sets are sized to the highest parked fd, so descriptors above 1023 keep working as they did under poll:

  • C lane: _DARWIN_UNLIMITED_SELECT on macOS; Linux's select takes sets of any size.
  • JS lane: binds select$DARWIN_EXTSN on macOS (the plain symbol refuses an fd past FD_SETSIZE), select elsewhere.

Probed on macOS 15.5 against a FIFO whose writer has closed: poll with POLLIN or POLLIN|POLLPRI|POLLRDNORM|POLLRDBAND never reports it, kqueue EVFILT_READ returns no event, plain select reports it but fails at fd 1500, and select$DARWIN_EXTSN reports it at fd 1500.

Commits

  1. The IO layer's comments and scheduler say the same in fewer tokens: a separate cleanup that makes room for the fix under comp.ts's cap (the JS generic park goes through io_park_on, its run loop tests op once, tighter IO comments, fewer temporaries). comp.ts: 64,936 → 64,682. On its own it changes no test result.
  2. The fix and its test: 64,682 → 65,000, at the unchanged cap. gates/repo.ts passes 46/46.

Test

tests/io/fifo_eof.bend (C and JS twins): a child writes into a FIFO and closes it only once the reader has drained it and parked again (an ack pipe orders the two), so the reader's last wake is the close alone. Three cases: data then close, close with no data, and the reader at fd 1500. Expected 5 / 0 / 5.

  • On 75cb8f3 without this change: hangs on both lanes, 20/20 runs each.
  • With it: 40/40 on each lane under 12-way parallel load; the interpreter prints the same.

Checked

On macOS 15.5 arm64: the tests that go through the IO loop (147 files: io, sockets, timers, channels, fibers) give the same results on both lanes before and after each commit, plus the new test. Not run on Linux or on the cluster gates.

The JS lane's generic park goes through io_park_on, its run loop tests
op once, and io_sys keeps the dlopen symbol table; the C lane resets a
queue and sets an effect row in one statement each. The IO comments are
tightened with their content kept, and io_fail, io_text, io_tup, io_push
and io_park_on use object shorthand and fewer temporaries. comp.ts:
64936 -> 64682 ttok.
… select (bendlang#928)

macOS's poll never reports a FIFO's close (nor does kqueue's EVFILT_READ),
so a computation parked on one never woke at end of file. select reports
it. Both lanes' io_wait now wait with select, the sets sized to the
highest parked fd so a descriptor past FD_SETSIZE keeps working as it did
under poll: _DARWIN_UNLIMITED_SELECT on the C lane, select$DARWIN_EXTSN
on the JS lane, and Linux's select takes sets of any size. The JS lane
fires the ready waits in one pass.

tests/io/fifo_eof: a child closes the FIFO only once the reader has
drained it and parks again, so the last wake is the close alone; after
data, with none, and with the reader at fd 1500. comp.ts: 64682 -> 65000
ttok.
@PedroVIOliv
PedroVIOliv marked this pull request as ready for review September 21, 2026 03:28
VictorTaelin pushed a commit that referenced this pull request Sep 21, 2026
… select

macOS's poll never reports a FIFO's close (kqueue neither), so a computation parked on a named pipe never woke at end of file. Both lanes' io_wait now wait with select, its sets sized to the highest parked fd (_DARWIN_UNLIMITED_SELECT on the C lane, select$DARWIN_EXTSN in the JS lane on macOS), so descriptors past 1023 keep working. The IO layer's comments and scheduler say the same in fewer tokens. (#928, PR #932)
@VictorTaelin

Copy link
Copy Markdown
Contributor

Merged into 2.0.25 as one commit under your authorship, thank you. We reproduced #928 on both lanes (kqueue does not see the close either), confirmed the fix, ran the 26 IO-loop tests on both lanes and trees, and checked select past fd 1023 on Linux. Your cleanup commit went in with it.

Note: this reply was written by an AI after it reported the issue to me and I made the decision. If anything here is wrong, reply and I will review it myself.

Phylliida pushed a commit to Phylliida/bend that referenced this pull request Sep 27, 2026
… select

macOS's poll never reports a FIFO's close (kqueue neither), so a computation parked on a named pipe never woke at end of file. Both lanes' io_wait now wait with select, its sets sized to the highest parked fd (_DARWIN_UNLIMITED_SELECT on the C lane, select$DARWIN_EXTSN in the JS lane on macOS), so descriptors past 1023 keep working. The IO layer's comments and scheduler say the same in fewer tokens. (bendlang#928, PR bendlang#932)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

macOS: poll() does not report a FIFO close, so a parked read never sees EOF

2 participants