From bd56e07bb043283d9bad56a61be5840e8dab97b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 12 Oct 2026 00:04:04 +0200 Subject: [PATCH] sceNetInet: Fix the blocking socket waits on Windows Headless only started Winsock (net::Init) for the debugger, so on Windows every socket call failed with WSANOTINITIALISED. Start it always. Winsock's select() fails on empty sets, where POSIX waits out the timeout, so a select() used as a sleep (the VSH's Skype) returned an error at once. With no host sockets to check, select and poll now just report nothing ready. Look up host sockets without GetHostSocketFromInetSocket, which asserts on a socket closed while a call waits for it, and whose -1 can't be caught with < 0 on Windows, where SOCKET is unsigned. Co-Authored-By: Claude Opus 5.5 --- Core/HLE/sceNetInet.cpp | 46 ++++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/Core/HLE/sceNetInet.cpp b/Core/HLE/sceNetInet.cpp index 0c4c2366cb..5bc4a6c33e 100644 --- a/Core/HLE/sceNetInet.cpp +++ b/Core/HLE/sceNetInet.cpp @@ -278,6 +278,18 @@ static InetTry TrySendTo(SceUID threadID, const u32 *args); static InetTry TryAccept(SceUID threadID, const u32 *args); static InetTry TryConnectDone(SceUID threadID, const u32 *args); +// The host socket behind a guest one, or false if there's none. Unlike GetHostSocketFromInetSocket, +// doesn't assert, since a socket can be closed while a call waits for it, and works on Windows, +// where SOCKET is unsigned and can't be checked for -1 with < 0. +static bool GetHostSocket(int socket, SOCKET *hostSocket) { + InetSocket *inetSock; + if (!g_socketManager.GetInetSocket(socket, &inetSock)) { + return false; + } + *hostSocket = inetSock->sock; + return true; +} + static bool IsWouldBlock(int hostErrno) { return hostErrno == EAGAIN || hostErrno == EWOULDBLOCK; } @@ -337,17 +349,16 @@ static InetTry TrySelect(SceUID threadID, const u32 *args, bool firstTry) { if (!set.set || !NetInetFD_ISSET(i, set.set)) { continue; } - SOCKET sock = g_socketManager.GetHostSocketFromInetSocket(i); - _dbg_assert_(sock != 0); - if (sock < 0) { + SOCKET sock; + if (!GetHostSocket(i, &sock)) { if (firstTry) { ERROR_LOG(Log::sceNet, "Bad socket #%d in %s fds", i, set.name); } continue; } hostSockets[i] = sock; - if (sock > maxHostSocket) - maxHostSocket = sock; + if ((int)sock > maxHostSocket) + maxHostSocket = (int)sock; if (*set.count < FD_SETSIZE) { FD_SET(sock, set.hostSet); // This might pointed to a non-existing socket or sockets belonged to other programs on Windows, because most of the time Windows socket have an id above 1k instead of 0-255 (*set.count)++; @@ -362,6 +373,11 @@ static InetTry TrySelect(SceUID threadID, const u32 *args, bool firstTry) { _dbg_assert_(wrcnt < FD_SETSIZE); _dbg_assert_(excnt < FD_SETSIZE); + if (rdcnt + wrcnt + excnt == 0) { + // Nothing to check: a select() used as a sleep. Winsock's select() fails on empty sets. + return { 0, true, EAGAIN }; + } + timeval tmout = { 0, 0 }; int retval = select(maxHostSocket + 1, readfds ? &rdfds : nullptr, writefds ? &wrfds : nullptr, exceptfds ? &exfds : nullptr, &tmout); if (retval == 0) { @@ -402,24 +418,27 @@ static InetTry TryPoll(SceUID threadID, const u32 *args) { fd_set readfds{}, writefds{}, exceptfds{}; FD_ZERO(&readfds); FD_ZERO(&writefds); FD_ZERO(&exceptfds); for (int i = 0; i < nfds; i++) { + SOCKET hostSocket; fdarray[i].revents = 0; - InetSocket *inetSock; - if (!g_socketManager.GetInetSocket(fdarray[i].fd, &inetSock)) { + if (!GetHostSocket(fdarray[i].fd, &hostSocket)) { // Not open, reported as POLLNVAL below. closedCount++; continue; } - SOCKET hostSocket = inetSock->sock; hostCount++; - if (hostSocket > maxHostFd) { - maxHostFd = hostSocket; + if ((int)hostSocket > maxHostFd) { + maxHostFd = (int)hostSocket; } - _dbg_assert_(hostSocket != 0); FD_SET(hostSocket, &readfds); FD_SET(hostSocket, &writefds); FD_SET(hostSocket, &exceptfds); } + if (hostCount == 0 && closedCount == 0) { + // Winsock's select() fails on empty sets. + return { 0, true, EAGAIN }; + } + if (hostCount > 0) { timeval tmout = { 0, 0 }; if (select(maxHostFd + 1, &readfds, &writefds, &exceptfds, &tmout) < 0) { @@ -430,14 +449,13 @@ static InetTry TryPoll(SceUID threadID, const u32 *args) { int retval = 0; for (int i = 0; i < nfds; i++) { - InetSocket *inetSock; - if (!g_socketManager.GetInetSocket(fdarray[i].fd, &inetSock)) { + SOCKET hostSocket; + if (!GetHostSocket(fdarray[i].fd, &hostSocket)) { // As on hardware (pspautotests net/inet/poll). fdarray[i].revents = INET_POLLNVAL; retval++; continue; } - SOCKET hostSocket = inetSock->sock; if ((fdarray[i].events & (INET_POLLRDNORM | INET_POLLIN)) && FD_ISSET(hostSocket, &readfds)) fdarray[i].revents |= (INET_POLLRDNORM | INET_POLLIN); //POLLIN_SET if ((fdarray[i].events & (INET_POLLWRNORM | INET_POLLOUT)) && FD_ISSET(hostSocket, &writefds))