Fix doReadv/doWritev discarding partial count on EAGAIN - #27284
Merged
Conversation
sbc100
approved these changes
Jul 8, 2026
sbc100
approved these changes
Jul 8, 2026
sbc100
enabled auto-merge (squash)
July 8, 2026 22:01
guybedford
force-pushed
the
fix-readv-partial-eagain
branch
from
July 8, 2026 22:18
4f287c0 to
a29171c
Compare
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
force-pushed
the
fix-readv-partial-eagain
branch
from
July 8, 2026 23:02
a29171c to
23be384
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
$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 theif (curr < len) breakdoesn't fire), the loop advances to the next iovec, whoseFS.readthrows EAGAIN. The exception escapeddoReadv, discarding the accumulatedret, so the wholereadv(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_vectoredwith 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.doReadvanddoWritevnow catchEAGAIN/EWOULDBLOCKFS.ErrnoErrorfrom a subsequent iovec and return the accumulated count whenret > 0, only rethrowing whenret == 0(nothing transferred yet, so the would-block belongs to the first iovec).Adds
test/fs/test_readv_eagain.c(wired up astest_fs_readv_eagain), which registers custom devices that satisfy the first iovec fully then throw EAGAIN, asserting bothreadvandwritevreturn the partial count rather than failing. The test aborts without the fix and passes with it.Made with AI assistance under my review