Fix mutex2 deadlock and ResumeEvent spam with conditional breakpoints#1051
Fix mutex2 deadlock and ResumeEvent spam with conditional breakpoints#1051
Conversation
…#1037) When a conditional breakpoint evaluates to false, the auto-resume path skips callback dispatch. If a Pause/Quit/Detach operation is pending, its callback never fires, its semaphore never releases, and m_adapterMutex2 is permanently locked — breaking all subsequent Pause/Quit/Detach operations. Fix by checking m_userRequestedBreak before auto-resuming so that an explicit Pause is honored over the breakpoint condition. Also suppress the unnecessary ResumeEventType that adapters post synchronously inside Go() during auto-resume, eliminating the dispatcher-thread re-entrant warning spam. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Actually had Codex review and it may have found something: Suppressing ResumeEventType during the conditional-breakpoint auto-resume leaves the controller in the paused state while the target is actually running. In the auto-resume path, DebuggerMainThread explicitly sets m_state->SetExecutionStatus(DebugAdapterPausedStatus) before evaluating the condition core/debuggercontroller.cpp, around line 2044), and the only normal path back to DebugAdapterRunningStatus is EventHandler on ResumeEventType (core/debuggercontroller.cpp, around lines 1850-1856). This PR drops that event in PostDebuggerEvent, so DebuggerState::IsRunning() stays false (core/debuggerstate.h, line 311). That breaks logic that depends on the running bit, for example CanResumeTarget() (core/debuggercontroller.cpp, lines 525-527) and QuitAndWait()'s decision to pause first. The warning goes away, but the controller/UI state can now say "paused" while the process is running. In other words the specific problem is that auto-resume starts from a forced paused state during conditional-breakpoint evaluation, and ResumeEventType is what normally flips the controller back to running. If this PR suppresses that event without also controller state another way, the debugger can think the target is paused while it is actually running. That is a behavioral regression, not just a style concern. A safer fix would need one of these:
|
…nal breakpoint and decide to resume the adapter
Nice catch! This indeed breaks the running/stopped status. In my testing, I did not see it because the various widgets are not updated if the controller does not report a true stop. However, if I close and open the debugger sidebar, I can see it reports the target status as stopped, which is unintended. I have added code to set the target status as running when we decide to resume the target at the conditional breakpoint site |
|
I think Codex is right here: Suppressing ResumeEventType during conditional-breakpoint auto-resume still looks incorrect because it drops the Suggestions on how to fix: Right now the PR suppresses the entire ResumeEventType, but EventHandler is what flips the controller back to running. The
m_state->SetConnectionStatus(DebugAdapterConnectedStatus);
In practice, the cleanest version is usually:
That preserves:
What I would not do is keep using ResumeEventType as both:
That coupling is what made this patch fragile in the first place. |
Summary
Fixes #1037
continueat line 2069) skips callback dispatch. If a Pause/Quit/Detach operation is pending, its callback never fires, its semaphore never releases, andm_adapterMutex2is permanently locked. Fix: checkm_userRequestedBreakbefore auto-resuming so an explicit Pause is honored over the breakpoint condition.ResumeEventTypesynchronously insideGo()(WindowsNativeAdapter, GdbAdapter, EsrevenAdapter, CorelliumAdapter) trigger re-entrant warnings when called from the dispatcher thread. Fix: suppress the unnecessaryResumeEventTypeduring auto-resume via a flag checked inPostDebuggerEvent. Adapters that post asynchronously (LLDB, DbgEng) are unaffected.Test plan
rax == 0x1234🤖 Generated with Claude Code