From 2cd84012d06fbf46bc131d862f87dd40416fa416 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 29 Sep 2026 11:58:42 -0600 Subject: [PATCH] Adhoc: Fix shutdown hanging when it comes right after adhoc init The friend finder thread set friendFinderRunning itself, after a DNS lookup of the adhoc server. A shutdown in that window cleared the flag first; the thread then set it again, looped forever, and the join in NetAdhocctl_Term() never returned. Gods Eater Burst hit this in about one headless run in six. The flag is now set before the thread is created, and a finished thread is joined before a new one replaces it, which would otherwise call std::terminate. The built-in adhoc server thread had the same race, behind its check for an existing server, and gets the same fix. Co-Authored-By: Claude Opus 5.5 (1M context) --- Core/HLE/proAdhoc.cpp | 3 +-- Core/HLE/proAdhocServer.cpp | 4 +--- Core/HLE/sceNetAdhoc.cpp | 13 ++++++++++++- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/Core/HLE/proAdhoc.cpp b/Core/HLE/proAdhoc.cpp index d61f556307..802e2baf31 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(); 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..6dc1c26967 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -1921,9 +1921,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); } } @@ -2008,6 +2013,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); }