From f2e7a2a9babb798c8f48c9f50f221bf059c15cc9 Mon Sep 17 00:00:00 2001 From: ANR2ME Date: Thu, 4 Feb 2021 10:15:29 +0700 Subject: [PATCH 1/2] Fix possible lock issue during AdhocMatchingStart --- Core/HLE/sceNetAdhoc.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index e14e05946c..e1db176c13 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -4661,8 +4661,10 @@ int NetAdhocMatching_Start(int matchingId, int evthPri, int evthPartitionId, int // Create PDP Socket int sock = sceNetAdhocPdpCreate((const char*)&item->mac, static_cast(item->port), item->rxbuflen, 0); item->socket = sock; - if (sock < 1) + if (sock < 1) { + peerlock.unlock(); return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_PORT_IN_USE, "adhoc matching port in use"); + } // Create & Start the Fake PSP Thread ("matching_ev%d" and "matching_io%d") netAdhocValidateLoopMemory(); From 849fe4ade17463b7ea33c21f153071c73530eafa Mon Sep 17 00:00:00 2001 From: ANR2ME Date: Thu, 4 Feb 2021 10:22:22 +0700 Subject: [PATCH 2/2] Updated PdpStat to prevent rcv_sb_cc from exceeding the buffer size arg (since we use larger buffer size to prevent micro stutters or disconnection issue due to too many dropped packets with small buffer size). TODO: May need to improve it to be able to calculate the correct size if there are multiple datagram messages --- Core/HLE/proAdhoc.cpp | 28 +++++++++++++++++++++++++--- Core/HLE/proAdhoc.h | 4 +++- Core/HLE/sceNetAdhoc.cpp | 7 +++---- 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/Core/HLE/proAdhoc.cpp b/Core/HLE/proAdhoc.cpp index 83667278d4..10f89df145 100644 --- a/Core/HLE/proAdhoc.cpp +++ b/Core/HLE/proAdhoc.cpp @@ -1896,13 +1896,35 @@ uint16_t getLocalPort(int sock) { return ntohs(localAddr.sin_port); } -u_long getAvailToRecv(int sock) { +u_long getAvailToRecv(int sock, int udpBufferSize) { u_long n = 0; // Typical MTU size is 1500 + int err = -1; #if defined(_WIN32) // May not be available on all platform - ioctlsocket(sock, FIONREAD, &n); + err = ioctlsocket(sock, FIONREAD, &n); #else - ioctl(sock, FIONREAD, &n); + err = ioctl(sock, FIONREAD, &n); #endif + if (err < 0) + return 0; + + if (udpBufferSize > 0 && n > 0) { + // Cap number of bytes of full DGRAM message(s?) up to buffer size + static int lastUdpBufSize = 0; + static char* buf = NULL; + if (udpBufferSize > lastUdpBufSize) { + // Reusing temp buffer to prevent causing too many fragmentation due to repeated alloc -> free (was getting out of memory issue) + char *tmp = (char*)realloc(buf, udpBufferSize); + if (tmp) { + buf = tmp; + lastUdpBufSize = udpBufferSize; + } + } + // Does each recv can only received one message? + err = recvfrom(sock, buf, udpBufferSize, MSG_PEEK | MSG_NOSIGNAL | MSG_TRUNC, NULL, NULL); + //free(buf); // Repeated alloc -> free seems to cause too many fragmentation and ended getting out of memory due to too many alloc -> free + if (err >= 0) + return (u_long)err; + } return n; } diff --git a/Core/HLE/proAdhoc.h b/Core/HLE/proAdhoc.h index eb41dbd76d..ad2b1a626c 100644 --- a/Core/HLE/proAdhoc.h +++ b/Core/HLE/proAdhoc.h @@ -1271,8 +1271,10 @@ bool isPrivateIP(uint32_t ip); /* * Get Number of bytes available in buffer to be Received + * @param sock fd + * @param udpBufferSize (UDP only) */ -u_long getAvailToRecv(int sock); +u_long getAvailToRecv(int sock, int udpBufferSize = 0); /* * Get UDP Socket Max Message Size diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index e1db176c13..06dd821f33 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -2943,10 +2943,9 @@ static int sceNetAdhocGetPdpStat(u32 structSize, u32 structAddr) { auto sock = adhocSockets[j]; if (sock != NULL && sock->type == SOCK_PDP) { // Set available bytes to be received. With FIONREAD There might be ghosting 1 byte in recv buffer when remote peer's socket got closed (ie. Warriors Orochi 2) Attempting to recv this ghost 1 byte will result to socket error 10054 (may need to disable SIO_UDP_CONNRESET error) - sock->data.pdp.rcv_sb_cc = getAvailToRecv(sock->data.pdp.id); - // TODO: It seems real PSP respecting the socket buffer size arg, so we may need to cap the value to the buffer size arg since we use larger buffer, but may cause Warriors Orochi 2 to get slower performance. - // Note: Capping available data on UDP/PDP can causes data loss if the game tried to read the whole capped buffer size while containing partial/truncated message. - //sock->data.pdp.rcv_sb_cc = std::min(sock->data.pdp.rcv_sb_cc, (u32_le)sock->buffer_size); + // It seems real PSP respecting the socket buffer size arg, so we may need to cap the value up to the buffer size arg since we use larger buffer, for PDP/UDP the total size must not contains partial/truncated message to avoid data loss. + // TODO: We may need to manage PDP messages ourself by reading each msg 1-by-1 and moving it to our internal buffer(msg array) in order to calculate the correct messages size that can fit into buffer size when there are more than 1 messages in the recv buffer (simulate FIONREAD) + sock->data.pdp.rcv_sb_cc = getAvailToRecv(sock->data.pdp.id, sock->buffer_size); // Copy Socket Data from Internal Memory memcpy(&buf[i], &sock->data.pdp, sizeof(SceNetAdhocPdpStat));