mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-09-05 04:05:27 +02:00
Legacy Win32 debugger: fix the painting problem with a frame-scoped mutex
Debugger windows (register list, disassembly view, memory view, breakpoint/ thread/module/stack lists, watch list) read CPU-thread-owned state directly from the GUI thread's WM_PAINT/list-fill handlers, racing against the CPU thread. Routing every read through Core_RunOnCPUThread would be too slow for something invoked continuously on paint/list-refresh. Add g_frameMutex (Core.h/Core.cpp), held by NativeFrame() only across the span where it actually touches that state (running the CPU, processing breakpoints, running the ImGui debugger) - not across input handling or the present/frame-pacing waits. Debugger windows now hold the same mutex while reading, giving synchronized reads without the round-trip cost of queuing to the CPU thread. CtrlRegisterList::onPaint() goes back to always reading live values (now safe under the lock) and grays them out by color alone while the core is running, rather than the earlier snapshot-caching approach. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hqm11k99viLfbJm2MkH4BH
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
03dcfd3931
commit
f72709feb0
@@ -255,7 +255,13 @@ void CtrlThreadList::OnRightClick(int itemIndex, int column, const POINT& point)
|
||||
|
||||
void CtrlThreadList::reloadThreads()
|
||||
{
|
||||
threads = GetThreadsInfo();
|
||||
// Reading live kernel thread state here on the GUI thread would otherwise race 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);
|
||||
threads = GetThreadsInfo();
|
||||
}
|
||||
Update();
|
||||
}
|
||||
|
||||
@@ -325,9 +331,13 @@ bool CtrlBreakpointList::WindowMessage(UINT msg, WPARAM wParam, LPARAM lParam, L
|
||||
|
||||
void CtrlBreakpointList::reloadBreakpoints()
|
||||
{
|
||||
// Update the items we're displaying from the debugger.
|
||||
displayedBreakPoints_ = g_breakpoints.GetBreakpoints();
|
||||
displayedMemChecks_= g_breakpoints.GetMemChecks();
|
||||
// Update the items we're displaying from the debugger. g_frameMutex guards this against races
|
||||
// with the CPU thread - see g_frameMutex in Core.h.
|
||||
{
|
||||
std::lock_guard<std::mutex> frameGuard(g_frameMutex);
|
||||
displayedBreakPoints_ = g_breakpoints.GetBreakpoints();
|
||||
displayedMemChecks_= g_breakpoints.GetMemChecks();
|
||||
}
|
||||
|
||||
for (int i = 0; i < GetRowCount(); i++)
|
||||
{
|
||||
@@ -744,11 +754,16 @@ void CtrlStackTraceView::OnDoubleClick(int itemIndex, int column)
|
||||
}
|
||||
|
||||
void CtrlStackTraceView::loadStackTrace() {
|
||||
auto memLock = Memory::Lock();
|
||||
Memory::MemoryInitedLock memLock = Memory::Lock();
|
||||
if (!PSP_IsInited())
|
||||
return;
|
||||
|
||||
auto threads = GetThreadsInfo();
|
||||
// Reading live thread/register/stack state here on the GUI thread would otherwise race 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);
|
||||
|
||||
std::vector<DebugThreadInfo> threads = GetThreadsInfo();
|
||||
|
||||
u32 entry = 0, stackTop = 0;
|
||||
for (size_t i = 0; i < threads.size(); i++)
|
||||
@@ -835,10 +850,16 @@ void CtrlModuleList::OnDoubleClick(int itemIndex, int column)
|
||||
|
||||
void CtrlModuleList::loadModules()
|
||||
{
|
||||
if (g_symbolMap) {
|
||||
modules = g_symbolMap->getAllModules();
|
||||
} else {
|
||||
modules.clear();
|
||||
// Reading the live symbol map here on the GUI thread would otherwise race 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);
|
||||
if (g_symbolMap) {
|
||||
modules = g_symbolMap->getAllModules();
|
||||
} else {
|
||||
modules.clear();
|
||||
}
|
||||
}
|
||||
Update();
|
||||
}
|
||||
@@ -855,6 +876,11 @@ CtrlWatchList::CtrlWatchList(HWND hwnd, DebugInterface *cpu)
|
||||
}
|
||||
|
||||
void CtrlWatchList::RefreshValues() {
|
||||
// Evaluating watch expressions reads live registers/memory here on the GUI thread, which would
|
||||
// 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);
|
||||
|
||||
int steppingCounter = Core_GetSteppingCounter();
|
||||
int changes = false;
|
||||
for (auto &watch : watches_) {
|
||||
|
||||
Reference in New Issue
Block a user