Files
ppsspp/Core/HLE/SocketManager.cpp
T
Henrik RydgårdandClaude Opus 5.5 e3bb5f1ada SaveState: Fixes from a review of the network modules' savestates
- sceNet kept the current session's "inited" flags across a load, while
  sceNetAdhoc takes them from the state. A state saved after
  sceNetApctlInit loaded into a fresh boot had no apctl, and an earlier
  state loaded mid-session found apctl already inited with no thread to
  deliver its events. They now come from the state, and sceNetInet's host
  sockets are closed on load instead of leaking ports.
- Adhoc matching contexts aren't saved, but survived a load while their
  sockets were deleted under their host threads. They're deleted first now,
  leaving their fake PSP threads (ids from before the load) alone.
- sceHttp had no state at all. Its inited flags are saved now, and its
  objects, which hold host connections, are dropped on load.
- The achievements data size from the file was skipped unchecked, which can
  wrap the pointer on 32-bit, and a MemSlabMap slab count could size a huge
  allocation.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-20 00:20:24 +02:00

137 lines
3.9 KiB
C++

#include "Common/Net/SocketCompat.h"
#include "Core/HLE/NetInetConstants.h"
#include "Core/HLE/SocketManager.h"
#include "Common/Log.h"
#include <mutex>
SocketManager g_socketManager;
static std::mutex g_socketMutex; // TODO: Remove once the adhoc thread is gone
InetSocket *SocketManager::CreateSocket(int *index, int *returned_errno, SocketState state, int domain, int type, int protocol) {
_dbg_assert_(state != SocketState::Unused);
int hostDomain = convertSocketDomainPSP2Host(domain);
int hostType = convertSocketTypePSP2Host(type);
int hostProtocol = convertSocketProtoPSP2Host(protocol);
SOCKET hostSock = ::socket(hostDomain, hostType, hostProtocol);
if (hostSock < 0) {
*returned_errno = socket_errno;
return nullptr;
}
std::lock_guard<std::mutex> guard(g_socketMutex);
for (int i = MIN_VALID_INET_SOCKET; i < ARRAY_SIZE(inetSockets_); i++) {
if (inetSockets_[i].state == SocketState::Unused) {
*index = i;
InetSocket *inetSock = inetSockets_ + i;
*inetSock = {}; // Reset to default.
inetSock->sock = hostSock;
inetSock->state = state;
inetSock->domain = domain;
inetSock->type = type;
inetSock->protocol = protocol;
inetSock->nonblocking = false;
*returned_errno = 0;
return inetSock;
}
}
_dbg_assert_(false);
ERROR_LOG(Log::sceNet, "Ran out of socket handles! This is BAD.");
closesocket(hostSock);
*index = 0;
*returned_errno = ENOMEM; // or something..
return nullptr;
}
InetSocket *SocketManager::AdoptSocket(int *index, SOCKET hostSocket, const InetSocket *derive) {
std::lock_guard<std::mutex> guard(g_socketMutex);
for (int i = MIN_VALID_INET_SOCKET; i < ARRAY_SIZE(inetSockets_); i++) {
if (inetSockets_[i].state == SocketState::Unused) {
*index = i;
InetSocket *inetSock = inetSockets_ + i;
inetSock->sock = hostSocket;
inetSock->state = derive->state;
inetSock->domain = derive->domain;
inetSock->type = derive->type;
inetSock->protocol = derive->protocol;
inetSock->nonblocking = derive->nonblocking; // should we inherit blocking state?
return inetSock;
}
}
// No space? Return nullptr and let the caller handle it. Shouldn't ever happen.
*index = 0;
return nullptr;
}
bool SocketManager::Close(InetSocket *inetSocket) {
_dbg_assert_(inetSocket->state != SocketState::Unused);
if (closesocket(inetSocket->sock) != 0) {
ERROR_LOG(Log::sceNet, "closesocket(%d) failed", inetSocket->sock);
return false;
}
inetSocket->state = SocketState::Unused;
inetSocket->sock = 0;
return true;
}
bool SocketManager::GetInetSocket(int sock, InetSocket **inetSocket) {
std::lock_guard<std::mutex> guard(g_socketMutex);
if (sock < MIN_VALID_INET_SOCKET || sock >= ARRAY_SIZE(inetSockets_) || inetSockets_[sock].state == SocketState::Unused) {
*inetSocket = nullptr;
return false;
}
*inetSocket = inetSockets_ + sock;
return true;
}
// Simplified mappers, only really useful in select/poll
SOCKET SocketManager::GetHostSocketFromInetSocket(int sock) {
std::lock_guard<std::mutex> guard(g_socketMutex);
if (sock < MIN_VALID_INET_SOCKET || sock >= ARRAY_SIZE(inetSockets_) || inetSockets_[sock].state == SocketState::Unused) {
_dbg_assert_(false);
return -1;
}
if (sock == 0) {
// Map 0 to 0, special case.
return 0;
}
return inetSockets_[sock].sock;
}
void SocketManager::CloseAll(SocketState state) {
for (auto &sock : inetSockets_) {
if (sock.state == state) {
closesocket(sock.sock);
sock.state = SocketState::Unused;
sock.sock = 0;
}
}
}
void SocketManager::CloseAll() {
for (auto &sock : inetSockets_) {
if (sock.state != SocketState::Unused) {
closesocket(sock.sock);
}
sock.state = SocketState::Unused;
sock.sock = 0;
}
}
const char *SocketStateToString(SocketState state) {
switch (state) {
case SocketState::Unused: return "unused";
case SocketState::UsedNetInet: return "netInet";
case SocketState::UsedProAdhoc: return "proAdhoc";
default:
return "N/A";
}
}