Skip to content

libkernel: readv and writev on a non-blocking pipe fail with EINVAL and ENOSPC instead of EAGAIN on Windows; fix branch ready #4874

Closed as not planned

Description

@DavidSamir

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.cpp as on main and the test kept, guest_file_vector fails at the readv check (File vector check failed at line 213). With the fix it passes 5/5 (--repeat until-fail:5), and guest_filesystem, guest_poll, guest_dup, guest_standard_streams, file_streams, guest_aio and guest_kernel_fcntl pass 3/3.
  • The probe behind the table, 33 lines, with main's libraries and the branch's: six lines change, the three readv calls on an empty pipe, the two writev calls on a full one and readv after the pipe has been drained, each to the EAGAIN result the Linux build gives. read and write, a zero-byte writev on 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 where readv fills 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 readv on 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 (readv on the empty non-blocking pipe), none in the other two.
  • Timing, 300,000 rounds, three passes in two alternating runs each: seek and readv on a file 4.1 to 4.4 µs with both sets of libraries; write three bytes to a pipe and readv them 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 poll in 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/main passes. tools/pr_overlap.py --branch reports no overlap with open pull requests, and git merge-tree of 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

  1. oneandonlydean commented on Oct 14, 2026

    @oneandonlydean
    Collaborator

    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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions