Skip to content

fix(libSceNet): report EAGAIN when a stream send or receive times out on Windows - #4758

Closed
EagleBearerYS wants to merge 2 commits into
boykopovar:mainfrom
EagleBearerYS:fix/libscenet-stream-timeouts-windows
Closed

EagleBearerYS wants to merge 2 commits into
boykopovar:mainfrom
EagleBearerYS:fix/libscenet-stream-timeouts-windows

Conversation

@EagleBearerYS

@EagleBearerYS EagleBearerYS commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

What

SO_RCVTIMEO and SO_SNDTIMEO on a libSceNet socket are set as the host socket's timeouts. On Windows a blocking receive or send on a stream socket that times out then fails with ETIMEDOUT (0x8041013c), where FreeBSD 11.4 returns EAGAIN:

Winsock reports its own timeout as WSAETIMEDOUT with no partial count, and documents that the connection is then in an indeterminate state (SOL_SOCKET options). #4092 fixed the datagram case, where the host timeout is safe. The stream case was left in TechnicalDebt for this reason.

On Windows, a blocking sceNetRecv, sceNetRecvfrom, sceNetRecvmsg, sceNetSend or sceNetSendto (and so sceNetSendmsg) on a stream socket with a timeout now waits for the socket with WSAPoll for that timeout first. It fails with EAGAIN when the wait ends, so the host timeout does not fire and the connection stays usable. A zero-length transfer does not wait, as in soreceive_generic().

A socket the host reports as unconnected (getpeername fails) does not wait either, because WSAPoll reports no event for it until the timeout. This covers a never-connected socket and a listening one, so their receive still fails at once with ENOTCONN.

The host option stays set for one case: a receive whose data another thread takes between the wait and the receive. That timeout now also maps to EAGAIN for stream sockets.

Linux and macOS are unchanged; their host timeouts already behave as FreeBSD's. The TechnicalDebt entry is rewritten. What remains is a send that starts while the socket is writable and still blocks past the timeout: it fails with ETIMEDOUT, where FreeBSD returns the partial count. No title is claimed.

The dependencies on #4669 and #4673 are only about merge order. They change the lines next to this change: the libSceNet TechnicalDebt entry above it, and sceNetSend's send call. This branch follows them once they merge.

Tested

Windows 11 (10.0.26300), WinLibs MinGW-w64 GCC 15.2.0 r7 (UCRT), Release, -DBUILD_TESTING=ON, on main at c952c2b:

  • New CheckStreamTimeouts in guest_sce_net, on a connected loopback TCP pair:
    • Receives: with a 50 ms SO_RCVTIMEO, sceNetRecv (without and with MSG_PEEK), sceNetRecvfrom and sceNetRecvmsg fail with EAGAIN after at least 40 ms. Data sent afterwards still arrives.
    • Unconnected sockets: sceNetRecv on a never-connected socket and on the listening socket, both with the same timeout, fails with ENOTCONN.
    • Sends: with a 4096-byte SO_SNDBUF and a 50 ms SO_SNDTIMEO, 64 KiB sends to a peer that does not read succeed until one fails with EAGAIN after at least 40 ms.
    • Afterwards: the peer reads exactly the bytes the successful sends reported, so the failed send sent nothing. A further receive times out, and the connection still carries data.
  • Before and after:
    • With main's Export.cpp, the test aborts at the first timed-out sceNetRecv, which fails with 0x8041013c (ETIMEDOUT) after 50 ms.
    • With only the receive-side wait, the receives return EAGAIN, but the send that fills the peer fails with ETIMEDOUT after 49 ms, 393216 bytes in.
    • Without the getpeername check, the receive on the unconnected socket waits and fails with EAGAIN.
    • With the change, guest_sce_net passes 20 runs in a row.
  • 147 tests pass: guest_sce_net, guest_memory_pool_stats, guest_resolver, the guest_datagram_* and guest_socket_host_write_* tests, and the tests registered in core/libs/prx/libc/CMakeLists.txt and core/libs/prx/libkernel/CMakeLists.txt that build on Windows.
  • tools/check_conventions.py --base origin/main passes.
  • Linux: Ubuntu 24.04 (WSL2), GCC 13.3.0, Release with CI's configure flags (-DANYPS5_ENABLE_SPIRV_TOOLS=ON), on the same commit. guest_sce_net, including CheckStreamTimeouts, passes 20 runs in a row. The code change does not apply there; the test checks that Linux's own timeouts already give EAGAIN, the sent count and ENOTCONN.
  • Not tested: macOS, any title.

Checklist

@xXPBSXx

xXPBSXx commented Oct 13, 2026

Copy link
Copy Markdown
Contributor

Windows test: head 0a1c30d58 merged onto current main 43e3a70f8 (clean merge, no conflicts). Windows 10, MinGW GCC (WinLibs), Ninja Release, -j2: full build 0 errors; ctest with the filter guest_sce_net: guest_sce_net passed (1 test(s), 0 failed); python tools/check_conventions.py --base upstream/main passed. I did not run the new tests against main without the change.

AI-assisted (Claude Code).

…lTimeouts

boykopovar#4924 adds a test after CheckKernelTimeouts and its call after CheckKernelTimeouts(), so placing the new check there made the two pull requests conflict.
@github-actions

github-actions Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Other open pull requests touch the same code. Merging one will break the other, or both implement the same thing. If this one builds on them, list them in Depends on.

Pull request Conflicting files (lines on main) Same exports Same new files
#4988 docs/dev/debt/libSceNet.md L11

@oneandonlydean

Copy link
Copy Markdown
Collaborator

Closing: no title is known to reach this. With the queue at 700+ open PRs, please hold off on new PRs that don't name a title needing the change until it's down. If a title stops on this on current main, reopen 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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants