You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Report POLLHUP/POLLERR readiness for pipe and socket hangups (emscripten-core#27298)
Makes poll/epoll surface the hangup and error conditions that Linux does
when the far end of a pipe or a connecting socket goes away. Without
these edges a reader (or writer) blocked in poll/epoll (e.g. mio's
`is_write_closed()`/`is_error()`) never wakes and hangs.
Pipe (`libpipefs.js`): on Linux a `pipe(2)` signals both directions of
hangup, which PIPEFS never modelled — `poll()` returned readiness only
while unread bytes remained and `close()` just decremented a shared
refcount without waking anyone.
* Once every write end is closed, poll on the read end reports
`POLLHUP|POLLIN` (read returns EOF). We track open write ends via a
`writerCount` (incremented in `dup`); when the last one closes we set
`writeClosed`, `notifyListeners(POLLHUP | POLLRDNORM | POLLIN)` on the
read end's wait-queue, and OR `POLLHUP|POLLIN` into `poll()`.
* Symmetrically, once every read end is closed, poll on the write end
reports `POLLERR` (a further write would get `EPIPE`), while staying
writable per Linux. Mirrored via `readerCount`/`readClosed` and a
`pipe.writeNode` wait-queue.
The old `refcnt` field is dropped — it always equalled `readerCount +
writerCount`, so buckets are freed once both reach 0.
Socket (`libsockfs_node.js`): a refused connect resolves with
`sock.error = ECONNREFUSED`, but `poll()` only reported `POLLOUT`, so
`is_write_closed()` and `is_error()` were both false under
NODERAWSOCKETS. We now report `POLLOUT | POLLERR | POLLHUP` on a pending
error, matching Linux's connect-failure readiness. `POLLOUT|POLLERR`
satisfies epoll's `is_write_closed()` mapping. The `SO_ERROR`
read-and-clear semantics are untouched.
Before (pipe read end after writer close): `poll()` returned `0` once
the buffer drained.
After: `poll()` returns `POLLHUP | POLLIN`.
Before (refused connect): `poll()` revents `0x11` (`POLLIN|POLLHUP` from
the CLOSED transition, no `POLLERR`).
After: `0x1d` (`POLLIN|POLLOUT|POLLERR|POLLHUP`).
Test coverage:
* `test/core/test_pipe_pollhup.c` — writes, closes the writer, and
asserts `POLLHUP|POLLIN` both while data is buffered and after draining
to EOF; also closes the reader and asserts the write end reports
`POLLOUT|POLLERR`.
* Extends `test/sockets/test_tcp_refused.c` to `poll()` for
`POLLERR|POLLHUP` *before* reading (and thereby clearing) `SO_ERROR`.
_Made with AI assistance under my review_
0 commit comments