Win32 debugger: take the shutdown lock everywhere core state is read

Seven GUI-thread readers held only g_frameMutex, which guards against the CPU
thread mutating state but not against the core being torn down - and two of
PSP_Shutdown's paths (EmuScreen::sendMessage, ProcessScreenSwitches) run outside
that span entirely. CtrlThreadList::reloadThreads walking kernel objects that
__KernelShutdown had freed was the concrete crash; the symbol map readers
(CDisasm::Show/NotifyMapLoaded, CtrlModuleList, CtrlWatchList) had the same
shape.

Always g_frameMutex first, then Memory::Lock().

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01GZq8ZtJmFY7bkX5FVkr3P9
This commit is contained in:
Henrik RydgårdandClaude Opus 5 committed 2026-08-17 13:11:16 +02:00
1 parent 4a10fa2808
commit f57a102027
4 files changed
+11 -2

No files matched your search

+3 -2
View File
@@ -61,9 +61,10 @@ void CtrlDisAsmView::deinit()
void CtrlDisAsmView::scanVisibleFunctions()
{
// Reads live memory/symbol state to detect function boundaries - hold g_frameMutex for the
// duration, which NativeFrame() also holds while it's actually touching that state. See
// g_frameMutex in Core.h.
// duration, which NativeFrame() also holds while it's actually touching that state, and
// Memory::Lock() so the core can't be torn down mid-read. See g_frameMutex in Core.h.
std::lock_guard<std::mutex> frameGuard(g_frameMutex);
Memory::MemoryInitedLock memLock = Memory::Lock();
g_disassemblyManager.analyze(windowStart, g_disassemblyManager.getNthNextAddress(windowStart,visibleRows)-windowStart);
}
+1
View File
@@ -203,6 +203,7 @@ void CtrlRegisterList::onPaint(WPARAM wParam, LPARAM lParam)
// with the CPU thread - hold g_frameMutex for the duration of the read, which NativeFrame()
// also holds while it's actually touching that state. See g_frameMutex in Core.h.
std::lock_guard<std::mutex> frameGuard(g_frameMutex);
Memory::MemoryInitedLock memLock = Memory::Lock();
// The values are a moving target while the core is running - gray them out rather than trying
// to highlight "changes" that are really just noise at that point.
bool running = !Core_IsStepping();
+2
View File
@@ -730,6 +730,7 @@ void CDisasm::Show(bool bShow, bool includeToTop) {
// thread - hold g_frameMutex for the duration of the read, which NativeFrame() also
// holds while it's actually touching that state. See g_frameMutex in Core.h.
std::lock_guard<std::mutex> frameGuard(g_frameMutex);
Memory::MemoryInitedLock memLock = Memory::Lock();
g_symbolMap->FillSymbolListBox(GetDlgItem(m_hDlg, IDC_FUNCTIONLIST), ST_FUNCTION);
deferredSymbolFill_ = false;
}
@@ -740,6 +741,7 @@ void CDisasm::Show(bool bShow, bool includeToTop) {
void CDisasm::NotifyMapLoaded() {
if (m_bShowState != SW_HIDE && g_symbolMap) {
std::lock_guard<std::mutex> frameGuard(g_frameMutex);
Memory::MemoryInitedLock memLock = Memory::Lock();
g_symbolMap->FillSymbolListBox(GetDlgItem(m_hDlg, IDC_FUNCTIONLIST), ST_FUNCTION);
} else {
deferredSymbolFill_ = true;
+5
View File
@@ -12,6 +12,7 @@
#include "Windows/main.h"
#include "Common/Data/Encoding/Utf8.h"
#include "Core/Core.h"
#include "Core/MemMap.h"
#include "Core/HLE/sceKernelThread.h"
enum { TL_NAME, TL_PROGRAMCOUNTER, TL_ENTRYPOINT, TL_PRIORITY, TL_STATE, TL_WAITTYPE, TL_COLUMNCOUNT };
@@ -260,6 +261,7 @@ void CtrlThreadList::reloadThreads()
// while it's actually touching that state. See g_frameMutex in Core.h.
{
std::lock_guard<std::mutex> frameGuard(g_frameMutex);
Memory::MemoryInitedLock memLock = Memory::Lock();
threads = GetThreadsInfo();
}
Update();
@@ -335,6 +337,7 @@ void CtrlBreakpointList::reloadBreakpoints()
// with the CPU thread - see g_frameMutex in Core.h.
{
std::lock_guard<std::mutex> frameGuard(g_frameMutex);
Memory::MemoryInitedLock memLock = Memory::Lock();
displayedBreakPoints_ = g_breakpoints.GetBreakpoints();
displayedMemChecks_= g_breakpoints.GetMemChecks();
}
@@ -845,6 +848,7 @@ void CtrlModuleList::loadModules()
// actually touching that state. See g_frameMutex in Core.h.
{
std::lock_guard<std::mutex> frameGuard(g_frameMutex);
Memory::MemoryInitedLock memLock = Memory::Lock();
if (g_symbolMap) {
modules = g_symbolMap->getAllModules();
} else {
@@ -870,6 +874,7 @@ void CtrlWatchList::RefreshValues() {
// otherwise race with the CPU thread - hold g_frameMutex for the duration, which NativeFrame()
// also holds while it's actually touching that state. See g_frameMutex in Core.h.
std::lock_guard<std::mutex> frameGuard(g_frameMutex);
Memory::MemoryInitedLock memLock = Memory::Lock();
int steppingCounter = Core_GetSteppingCounter();
int changes = false;