SymbolMap: route remaining GUI-thread callers, remove internal locking

Windows/MainWindowMenu.cpp's Load/Save/Clear Symbol Map menu actions
mutated g_symbolMap directly from the WinMain thread; route them through
Core_RunOnCPUThread like the rest of the debugger. Windows/MainWindow.cpp's
WM_USER_GET_MODULE_INFO handler read it directly too; guard with
g_frameMutex. Windows/main.cpp's SortSymbols() calls turn out to already be
safe as-is - both notifications that trigger them (BOOT_DONE,
SYMBOL_MAP_UPDATED) are only ever fired from the CPU/NativeFrame thread -
so just fix the stale comment claiming reliance on the (now removed)
internal lock.

With those covered, every remaining caller is either on the CPU/NativeFrame
thread already or routes through Core_RunOnCPUThread/g_frameMutex, so
SymbolMap's internal recursive_mutex is redundant - remove it and all ~40
lock_guard call sites. Qt's mainwindow.cpp still pokes at g_symbolMap
directly and unguarded (a pre-existing issue, out of scope - Qt isn't a
maintained backend), but removing the lock doesn't change its public API,
so it still builds.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Hqm11k99viLfbJm2MkH4BH
This commit is contained in:
Henrik RydgårdandClaude Sonnet 5 committed 2026-08-08 17:22:12 +02:00
1 parent 5df8c14f1e
commit 3500293402
5 files changed
+21 -64

No files matched your search

+6 -2
View File
@@ -725,8 +725,12 @@ namespace MainWindow {
{
return 0;
}
// Get all modules from symbol map
auto modules = g_symbolMap->getAllModules();
// Get all modules from symbol map. Reading it 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<LoadedModuleInfo> modules = g_symbolMap->getAllModules();
for (const auto& module : modules)
{
if (module.name == moduleName)
+12 -5
View File
@@ -769,34 +769,41 @@ namespace MainWindow {
break;
}
// g_symbolMap access below is routed through Core_RunOnCPUThread() instead of poking at it
// directly from this GUI thread - see Core_RunOnCPUThread() in Core.h. The file-browse
// dialogs are modal, so they stay outside the queued callback; NotifyDebuggerMapLoaded()
// also stays outside, since it locks g_frameMutex internally (CDisasm/CMemoryDlg's
// NotifyMapLoaded()) and Core_RunOnCPUThread's queue can be drained from inside a
// g_frameMutex-locked NativeFrame() span, so calling it from within the lambda could
// deadlock.
case ID_DEBUG_LOADMAPFILE:
if (W32Util::BrowseForFileName(true, hWnd, L"Load .ppmap", nullptr, nullptr, L"Maps\0*.ppmap\0All files\0*.*\0\0", L"ppmap", fn)) {
g_symbolMap->LoadSymbolMap(Path(fn));
Core_RunOnCPUThread([&] { g_symbolMap->LoadSymbolMap(Path(fn)); });
NotifyDebuggerMapLoaded();
}
break;
case ID_DEBUG_SAVEMAPFILE:
if (W32Util::BrowseForFileName(false, hWnd, L"Save .ppmap", nullptr, L"map.ppmap", L"Maps\0*.ppmap\0All files\0*.*\0\0", L"ppmap", fn)) {
g_symbolMap->SaveSymbolMap(Path(fn));
Core_RunOnCPUThread([&] { g_symbolMap->SaveSymbolMap(Path(fn)); });
}
break;
case ID_DEBUG_LOADSYMFILE:
if (W32Util::BrowseForFileName(true, hWnd, L"Load .sym", nullptr, nullptr, L"Symbols\0*.sym\0All files\0*.*\0\0", L"sym", fn)) {
g_symbolMap->LoadNocashSym(Path(fn));
Core_RunOnCPUThread([&] { g_symbolMap->LoadNocashSym(Path(fn)); });
NotifyDebuggerMapLoaded();
}
break;
case ID_DEBUG_SAVESYMFILE:
if (W32Util::BrowseForFileName(false, hWnd, L"Save .sym", nullptr, L"symbols.sym", L"Symbols\0*.sym\0All files\0*.*\0\0", L"sym", fn)) {
g_symbolMap->SaveNocashSym(Path(fn));
Core_RunOnCPUThread([&] { g_symbolMap->SaveNocashSym(Path(fn)); });
}
break;
case ID_DEBUG_RESETSYMBOLTABLE:
g_symbolMap->Clear();
Core_RunOnCPUThread([&] { g_symbolMap->Clear(); });
NotifyDebuggerMapLoaded();
break;
+2 -2
View File
@@ -508,7 +508,7 @@ void System_Notify(SystemNotification notification) {
case SystemNotification::BOOT_DONE:
{
if (g_symbolMap)
g_symbolMap->SortSymbols(); // internal locking is performed here
g_symbolMap->SortSymbols(); // Always fired from the CPU/NativeFrame thread, see AGENTS.md
PostMessage(MainWindow::GetHWND(), WM_USER + 1, 0, 0);
if (Achievements::HardcoreModeActive()) {
@@ -548,7 +548,7 @@ void System_Notify(SystemNotification notification) {
case SystemNotification::SYMBOL_MAP_UPDATED:
if (g_symbolMap)
g_symbolMap->SortSymbols(); // internal locking is performed here
g_symbolMap->SortSymbols(); // Always fired from the CPU/NativeFrame thread, see AGENTS.md
PostMessage(MainWindow::GetHWND(), WM_USER + 1, 0, 0);
break;