Skip to content

Fix doReadv/doWritev discarding partial count on EAGAIN - #27284

Merged
sbc100 merged 3 commits into
emscripten-core:mainfrom
guybedford:fix-readv-partial-eagain
Jul 8, 2026
Merged

Fix doReadv/doWritev discarding partial count on EAGAIN#27284
sbc100 merged 3 commits into
emscripten-core:mainfrom
guybedford:fix-readv-partial-eagain

Conversation

@guybedford

Copy link
Copy Markdown
Collaborator

$doReadv reads iovec-by-iovec via FS.read. On a non-blocking socket, FS.read (nodeSockOps.recvmsg) throws EAGAIN when the queue is empty, unlike a regular file which returns 0. If an earlier iovec already consumed data and filled exactly to its length (so the if (curr < len) break doesn't fire), the loop advances to the next iovec, whose FS.read throws EAGAIN. The exception escaped doReadv, discarding the accumulated ret, so the whole readv(2) failed with EAGAIN even though bytes were read.

POSIX readv/writev are single gather operations: they return the available byte count and never fail after partial success.

Observed with an mio TcpStream (NODERAWSOCKETS + PROXY_TO_PTHREAD + JSPI) where an echo returned as two TCP chunks: read_vectored with two iovecs hit recvmsg on iovec #1 (12 bytes, drains recvq) then recvmsg EMPTY on iovec #2 (next chunk not yet arrived), throwing WouldBlock for the whole call. A single-buffer read never hits it — matching "read works, readv fails". It is timing-sensitive: if both chunks are queued before the read, iovec #2 succeeds.

  • doReadv and doWritev now catch EAGAIN/EWOULDBLOCK FS.ErrnoError from a subsequent iovec and return the accumulated count when ret > 0, only rethrowing when ret == 0 (nothing transferred yet, so the would-block belongs to the first iovec).

Adds test/fs/test_readv_eagain.c (wired up as test_fs_readv_eagain), which registers custom devices that satisfy the first iovec fully then throw EAGAIN, asserting both readv and writev return the partial count rather than failing. The test aborts without the fix and passes with it.

Made with AI assistance under my review

Comment thread src/lib/libwasi.js Outdated
@sbc100
sbc100 enabled auto-merge (squash) July 8, 2026 22:01
@guybedford
guybedford force-pushed the fix-readv-partial-eagain branch from 4f287c0 to a29171c Compare July 8, 2026 22:18
On a non-blocking stream a later iovec can would-block after earlier
iovecs already transferred data. FS.read/FS.write throw EAGAIN, which
escaped doReadv/doWritev and failed the whole readv/writev even though
bytes were transferred. POSIX gather I/O returns the transferred count
and never fails after partial success. Catch EAGAIN/EWOULDBLOCK from a
subsequent iovec and return the accumulated count when ret > 0.
@guybedford
guybedford force-pushed the fix-readv-partial-eagain branch from a29171c to 23be384 Compare July 8, 2026 23:02
@sbc100
sbc100 merged commit 2d95c52 into emscripten-core:main Jul 8, 2026
38 of 39 checks passed
@guybedford
guybedford deleted the fix-readv-partial-eagain branch July 8, 2026 23:57
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.

2 participants