mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-09-04 03:35:19 +02:00
Fix deadlock when stopping a game with a debugger request in flight
The WebSocket thread holds lifecycleLock across a whole event handler, and handlers do their real work through Core_RunOnCPUThread(), which blocks until the CPU thread drains the queue. Meanwhile PSP_Shutdown() -> Core_NotifyLifecycle(STOPPING) took that same lock on the CPU thread. So the debugger thread waited for the CPU thread while the CPU thread waited for the lock the debugger thread was holding, and neither ever moved. Drain the CPU queue while waiting for the lock instead of blocking on it. Core state is still alive at STOPPING (it's notified before CPU_Shutdown), so running those queued callbacks then is safe, and it lets the debugger thread finish and release the lock. Verified with a temporary instrumented build - a 3s sleep inside a handler while holding lifecycleLock, arranged to overlap the game's shutdown - which hangs reliably on the old code and exits cleanly with this change. lifecycleLock stays for now: roughly half the subscribers and all the broadcasters still read core state directly on the WebSocket thread instead of going through the queue, and this is what keeps that from racing with teardown. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GZq8ZtJmFY7bkX5FVkr3P9
This commit is contained in:
co-authored by
Claude Opus 5
parent
81511e7a08
commit
707275b46a
@@ -19,6 +19,8 @@
|
||||
#include <condition_variable>
|
||||
|
||||
#include "Common/Thread/ThreadUtil.h"
|
||||
#include "Common/TimeUtil.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/Debugger/WebSocket.h"
|
||||
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
|
||||
|
||||
@@ -108,7 +110,15 @@ static void WebSocketNotifyLifecycle(CoreLifecycle stage) {
|
||||
if (debuggersConnected > 0) {
|
||||
DEBUG_LOG(Log::System, "Waiting for debugger to complete on shutdown");
|
||||
}
|
||||
lifecycleLock.lock();
|
||||
// Keep draining the CPU queue while we wait, instead of a plain blocking lock(). We're on
|
||||
// the CPU thread here, and a debugger thread holding this lock may be parked inside
|
||||
// Core_RunOnCPUThread() waiting for us to run its callback - so blocking outright means
|
||||
// neither side can ever move. Core state is still fully alive at this point (STOPPING is
|
||||
// notified before CPU_Shutdown), so running those callbacks now is safe.
|
||||
while (!lifecycleLock.try_lock()) {
|
||||
Core_ProcessCPUQueue();
|
||||
sleep_ms(1, "debugger-lifecycle");
|
||||
}
|
||||
break;
|
||||
|
||||
case CoreLifecycle::START_COMPLETE:
|
||||
|
||||
Reference in New Issue
Block a user