Repository navigation
libkernel: readv and writev on a non-blocking pipe fail with EINVAL and ENOSPC instead of EAGAIN on Windows; fix branch ready #4874
Description
On Windows, readv on an empty pipe that fcntl has set non-blocking fails with EINVAL, and writev on a full one fails with ENOSPC. read and write on the same pipe fail with EAGAIN, as FreeBSD and the Linux build do for all four. A loop that calls readv or writev until it gets EAGAIN takes either error for a real failure and gives up on the pipe.
Cause. sceKernelRead and sceKernelWrite go through NativeRead and NativeWrite, which turn the C runtime's result for a PIPE_NOWAIT pipe into EAGAIN (ERROR_NO_DATA on a read; ENOSPC with no Win32 error on a write to a pipe). The vector calls go through NativeTransfer in Stdio.cpp, which calls _read and _write without it. guest_file_vector already checks EAGAIN from readv on a non-blocking pipe, but only on Linux (#ifndef _WIN32).
Observed on main 0428328, Windows 11 (10.0.26300), WinLibs GCC 15.2.0 r7, Release, on a pipe from pipe() with both ends set non-blocking through fcntl(F_SETFL, O_NONBLOCK):
| call | Windows | Linux build |
|---|---|---|
sceKernelRead, empty pipe |
0x80020023 (EAGAIN) |
0x80020023 |
sceKernelReadv, empty pipe |
0x80020016 (EINVAL) |
0x80020023 |
readv, empty pipe |
-1, errno 22 | -1, errno 35 |
sceKernelWrite, full pipe |
0x80020023 |
0x80020023 |
sceKernelWritev, full pipe |
0x8002001C (ENOSPC) |
0x80020023 |
writev, full pipe |
-1, errno 28 | -1, errno 35 |
I have not found a title that stops on this. I found it while testing #4858.
Fix, ready to cherry-pick. DavidSamir:fix/libkernel-readv-writev-eagain-windows (f08a584, one commit on main 0428328, two files, +17/-4). I can't open it as a PR while #4532 is open (one open PR without a merged one).
Windows only. NativeTransfer gets the two translations NativeRead and NativeWrite have, with the same conditions. Nothing is called again: it is the error number of the one _read or _write that is translated. Blocking pipes, files and sockets never produce those two results, so they are not affected.
This does not depend on #4858 and does not touch the lines it changes; the two can land in either order.
guest_file_vector: the non-blocking check now runs on Windows too, with the pipe set non-blocking through the guest's fcntl on both hosts, and is extended to the write side. sceKernelWritev of 16 bytes is repeated until it stops; the next sceKernelWritev must return 0x80020023 and writev -1 with errno 35, and a read must then succeed.
Checked on Windows (f08a584 on main 0428328, Windows 11 10.0.26300, WinLibs GCC 15.2.0 r7, Release, full builds):
- With
Stdio.cppas onmainand the test kept,guest_file_vectorfails at thereadvcheck (File vector check failed at line 213). With the fix it passes 5/5 (--repeat until-fail:5), andguest_filesystem,guest_poll,guest_dup,guest_standard_streams,file_streams,guest_aioandguest_kernel_fcntlpass 3/3. - The probe behind the table, 33 lines, with
main's libraries and the branch's: six lines change, the threereadvcalls on an empty pipe, the twowritevcalls on a full one andreadvafter the pipe has been drained, each to theEAGAINresult the Linux build gives.readandwrite, a zero-bytewritevon a full pipe, the reads and writes that have data or room, the pipe switched back to blocking and the pipe whose write end is closed are identical in both. Against the Linux build the branch still differs wherereadvfills only its first buffer, which is libkernel: readv from a pipe fills only the first buffer on Windows; fix branch ready #4858. - A probe of 33 lines over
readvon blocking pipes, files and/dev/urandom, one of 188 lines over the file, directory, vector and mapping functions and one of 122 lines over the descriptor functions, run with both sets of libraries: one line differs in the first (readvon the empty non-blocking pipe), none in the other two. - Timing, 300,000 rounds, three passes in two alternating runs each: seek and
readvon a file 4.1 to 4.4 µs with both sets of libraries; write three bytes to a pipe andreadvthem 1.15 to 1.26 µs with both; write ten bytes and read them back 2.83 to 3.13 µs with both. - Titles from the homebrew catalog, 35 s each with both sets of libraries: ccleste (PPSA59322), RommPS (PPSA76677), SwanStationPS5 (PPSA98510), ProsperoTV (PPSA99003), PSXS5 (PPSA97510), DOOM (PPSA99666), ScreenTester5 (PPSA69420), Prospero Vibrate (PPSA99920), SymphonyStation5 (PPSA17641), ProsperoStore (PPSA99000) and OpenNOW (PPSA99082) end at the same place in both, with the same number of stderr lines; PSXS5 stops on
pollin both (fix(libkernel): poll pipe descriptors #3644). I do not know whether any of them uses the vector calls on a non-blocking pipe. - Full
ctest --parallel 4: 1001 of 1001 pass (7 skipped, no display). tools/check_conventions.py --base origin/mainpasses.tools/pr_overlap.py --branchreports no overlap with open pull requests, andgit merge-treeof this branch with libkernel: readv from a pipe fills only the first buffer on Windows; fix branch ready #4858's merges without conflict.
Checked on Linux (same commit, WSL2 Ubuntu 24.04, GCC 13.3.0, Release): guest_file_vector passes 3/3, so the extended check matches the existing Linux behaviour. Only that target and libs were built there.
Not checked on a console or with a commercial title.
AI-assisted: yes
Activity
oneandonlydean commentedon Oct 14, 2026
Closing: no title is known to reach this, and functions are only changed when a title needs them. The catalog titles in the report run the same with and without the change. If a title stops on or misbehaves because of it on current main, comment here with the title ID and the log line.