diff --git a/Core/HLE/NetAdhocCommon.cpp b/Core/HLE/NetAdhocCommon.cpp index 242735a3c6..9b5ea7a63c 100644 --- a/Core/HLE/NetAdhocCommon.cpp +++ b/Core/HLE/NetAdhocCommon.cpp @@ -45,3 +45,4 @@ u32 matchingThreadHackAddr = 0; u32_le matchingThreadCode[3]; bool g_adhocServerConnected = false; +std::atomic g_adhocServerLoginFailed(false); diff --git a/Core/HLE/NetAdhocCommon.h b/Core/HLE/NetAdhocCommon.h index 1e0b532b06..125f492764 100644 --- a/Core/HLE/NetAdhocCommon.h +++ b/Core/HLE/NetAdhocCommon.h @@ -20,6 +20,7 @@ #include "CommonTypes.h" #include "Swap.h" +#include #include extern u32 dummyThreadHackAddr; @@ -52,5 +53,8 @@ extern u32 matchingThreadHackAddr; extern u32_le matchingThreadCode[3]; extern bool g_adhocServerConnected; +// Set by the friend finder when its attempt to connect and log in for the latest login request +// failed, so a caller waiting on that login can stop waiting. +extern std::atomic g_adhocServerLoginFailed; constexpr u32 defaultLastRecvDelta = 10000; //10000 usec worked well for games published by Falcom (ie. Ys vs Sora Kiseki, Vantage Master Portable) diff --git a/Core/HLE/proAdhoc.cpp b/Core/HLE/proAdhoc.cpp index d61f556307..1ed893dda8 100644 --- a/Core/HLE/proAdhoc.cpp +++ b/Core/HLE/proAdhoc.cpp @@ -1401,8 +1401,7 @@ int friendFinder() { } g_adhocServerIP.in.sin_port = htons(SERVER_PORT); - // Finder Loop - friendFinderRunning = true; + // Finder Loop. The flag was set by whoever started us, and cleared to stop us. while (friendFinderRunning) { // Acquire Network Lock //_acquireNetworkLock(); @@ -1421,6 +1420,7 @@ int friendFinder() { } else { g_adhocServerConnected = false; + g_adhocServerLoginFailed = true; shutdown((int)metasocket, SD_BOTH); closesocket((int)metasocket); metasocket = (int)INVALID_SOCKET; @@ -2252,6 +2252,16 @@ int initNetwork(SceNetAdhocctlAdhocId *adhoc_id){ return iResult; done = (IsSocketReady((int)metasocket, false, true) > 0); + if (done) { + // Writable can also mean the attempt failed (refused, say). Then there's no point + // waiting out the timeout. + int soError = 0; + socklen_t soErrorLen = sizeof(soError); + if (getsockopt((int)metasocket, SOL_SOCKET, SO_ERROR, (char *)&soError, &soErrorLen) == 0 && soError != 0) { + errorcode = soError; + break; + } + } struct sockaddr_in sin; socklen_t sinlen = sizeof(sin); memset(&sin, 0, sinlen); diff --git a/Core/HLE/proAdhocServer.cpp b/Core/HLE/proAdhocServer.cpp index 4aca7822dc..6e5d53d5ce 100644 --- a/Core/HLE/proAdhocServer.cpp +++ b/Core/HLE/proAdhocServer.cpp @@ -1865,9 +1865,7 @@ int create_listen_socket(uint16_t port) */ int server_loop(int server) { - // Set Running Status - //_status = 1; - adhocServerRunning = true; + // adhocServerRunning was set by whoever started the thread, and is cleared to stop it. // Create Empty Status Logfile update_status(); diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index 8a737c8cdd..74e475c577 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -560,7 +560,8 @@ static void __AdhocctlNotify(u64 userdata, int cyclesLate) { } // Retry until successfully sent. Login packet sent after successfully connected to Adhoc Server (indicated by networkInited), so we're not sending Login again here - if ((req.opcode == OPCODE_LOGIN && !g_adhocServerConnected) || (ret == SOCKET_ERROR && (sockerr == EAGAIN || sockerr == EWOULDBLOCK))) { + // A login stops waiting early once the friend finder has tried and failed to connect. + if ((req.opcode == OPCODE_LOGIN && !g_adhocServerConnected && !g_adhocServerLoginFailed) || (ret == SOCKET_ERROR && (sockerr == EAGAIN || sockerr == EWOULDBLOCK))) { u64 now = (u64)(time_now_d() * 1000000.0); if (now - adhocctlStartTime <= static_cast(adhocDefaultTimeout) + 500) { // Try again in another 0.5ms until timedout. @@ -1921,9 +1922,14 @@ void __NetAdhocInit() { __AdhocNotifInit(); __AdhocServerInit(); - // Create built-in AdhocServer Thread + // Create built-in AdhocServer Thread. The flag is set here rather than by the thread, so that a + // shutdown that clears it before the thread gets going can't be undone (see friendFinder). adhocServerRunning = false; + if (adhocServerThread.joinable()) { + adhocServerThread.join(); + } if (g_Config.bEnableWlan && g_Config.bEnableAdhocServer) { + adhocServerRunning = true; adhocServerThread = std::thread(proAdhocServerThread, SERVER_PORT); } } @@ -1996,6 +2002,7 @@ int sceNetAdhocctlInit(int stackSize, int prio, u32 productAddr) { adhocctlEvents.clear(); netAdhocctlInited = true; //needed for cleanup during AdhocctlTerm even when it failed to connect to Adhoc Server (since it's being faked as success) + g_adhocServerLoginFailed = false; isAdhocctlNeedLogin = true; // Create fake PSP Thread for callback @@ -2008,6 +2015,12 @@ int sceNetAdhocctlInit(int stackSize, int prio, u32 productAddr) { // TODO: Merging friendFinder (real) thread to AdhocThread (fake) thread on PSP side if (!friendFinderRunning) { + // Set before the thread starts, not by it: shutting down before it got going used to clear + // the flag first, and then the thread set it again and never stopped, hanging the join. + if (friendFinderThread.joinable()) { + friendFinderThread.join(); + } + friendFinderRunning = true; friendFinderThread = std::thread(friendFinder); } @@ -3180,6 +3193,7 @@ int sceNetAdhocctlScan() { // Only scan when in Disconnected state, otherwise AdhocServer will kick you out if (adhocctlState == ADHOCCTL_STATE_DISCONNECTED && !isAdhocctlBusy) { isAdhocctlBusy = true; + g_adhocServerLoginFailed = false; isAdhocctlNeedLogin = true; adhocctlState = ADHOCCTL_STATE_SCANNING; adhocctlCurrentMode = ADHOCCTL_MODE_NORMAL; @@ -3673,6 +3687,7 @@ int NetAdhocctl_Create(const char *groupName) { // Disconnected State if (adhocctlState == ADHOCCTL_STATE_DISCONNECTED && !isAdhocctlBusy) { isAdhocctlBusy = true; + g_adhocServerLoginFailed = false; isAdhocctlNeedLogin = true; // Set Network Name diff --git a/headless/Headless.cpp b/headless/Headless.cpp index d9ccb37a7f..df38b243ed 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -903,6 +903,8 @@ int main(int argc, const char* argv[]) { g_Config.bMemStickInserted = true; g_Config.iMemStickSizeGB = 16; g_Config.bEnableWlan = true; + // The net tests want WLAN on, but a test run shouldn't depend on reaching a real adhoc server. + g_Config.sProAdhocServer = "localhost"; g_Config.sMACAddress = "12:34:56:78:9A:BC"; g_Config.iFirmwareVersion = PSP_DEFAULT_FIRMWARE; g_Config.iPSPModel = PSP_MODEL_SLIM;