It stopped being about memory when CPU_Shutdown started holding it across the
whole teardown - it's what keeps kernel objects, the symbol map and the memory
map from being freed while another thread reads them. The old name invited the
reading that it locks memory *access*, which it has never done.
Memory::Reinit() now holds it across both halves rather than relying on
Memory::Shutdown()'s own acquire: between Shutdown() and Init() there is no
memory map at all, and a reader could slip into that gap.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GZq8ZtJmFY7bkX5FVkr3P9
It was only held across Memory::Shutdown(), but everything else in there frees
state the debugger UIs read from other threads - kernel objects
(__KernelShutdown), the symbol map, replacements - so a Win32 debugger window
painting while a game is reset could read freed memory. It's recursive, so the
nested acquire in Memory::Shutdown() is unaffected.
No lock-order risk: on the paths where CPU_Shutdown already runs under
g_frameMutex it now takes these in the same frame-then-shutdown order the GUI
side uses, and on the paths where it doesn't (EmuScreen::sendMessage,
ProcessScreenSwitches - both above where NativeFrame takes the guard) it takes
only this one.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GZq8ZtJmFY7bkX5FVkr3P9
Three things still touched CPU-thread-owned state from the WebSocket thread:
- Breakpoint conditions were compiled in Parse(), and resolving symbols in an
expression goes through g_symbolMap, which is destroyed on shutdown. Compiled
inside the queued callback now, before anything is mutated, so a bad
expression still fails without leaving a breakpoint behind.
- gpu.record.dump dereferenced the gpu pointer, which is created and destroyed
on the CPU thread.
- gpu.stats.feed bumped PSP_ForceDebugStats' plain counter.
Also makes g_bootState atomic - it's read as a fast-fail from the debugger
thread all over while the CPU and loader threads move it along.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GZq8ZtJmFY7bkX5FVkr3P9
C:/src/ppsspp/Core/System.cpp: In function 'void DumpFileIfEnabled(const u8*, u32, std::string_view, DumpFileType)':
C:/src/ppsspp/Core/System.cpp:938:29: warning: 'void free(void*)' called on pointer returned from a mismatched allocation function [-Wmismatched-new-delete]
938 | free(path);
| ~~~~^~~~~~
C:/src/ppsspp/Core/System.cpp:924:67: note: returned from 'void* operator new [](std::size_t)'
924 | char *path = new char[strlen(fullPath.c_str()) + 1];
|