Don't let the ThreadPlanSingleStepTimeout interrupt an already stopped process (#227898) When the ThreadPlanSingleThreadTimeout timeout fires, first check whether the process is stopped before sending the interrupt. This is a simpler way to address the problem that was identified in: https://github.com/llvm/llvm-project/pull/224272 The problem solved there is that if the stop processing is still going on when the timer fires, we send the interrupt request event which gets enqueued and then handled after the stop event processing has restarted the inferior to continue the thread plan work. That patch involved trying to figure out, when you receive the event, whether it was stale or not. It was harder to reason about - and not all the way right, though that probably could be fixed. But when you get a stop event, we first set the private state to stopped and then start the handling. So if instead we check whether the process is stopped when the timer fires, and don't send the interrupt if it is, we fix the same problem in a much more obviously correct way. This addresses the flakiness of the test: TestSingleThreadStepTimeout.py GitOrigin-RevId: e0d351f24d0afe03a077d19b358363142e876ea7
diff --git a/include/lldb/Target/Process.h b/include/lldb/Target/Process.h index 08defc5..226c580 100644 --- a/include/lldb/Target/Process.h +++ b/include/lldb/Target/Process.h
@@ -375,6 +375,7 @@ friend class Target; friend class ThreadList; friend class MemoryCache; + friend class ThreadPlanSingleThreadTimeout; public: /// Broadcaster event bits definitions.
diff --git a/source/Target/Process.cpp b/source/Target/Process.cpp index 0be2d92..c1343fa 100644 --- a/source/Target/Process.cpp +++ b/source/Target/Process.cpp
@@ -4279,6 +4279,16 @@ const StateType new_state = Process::ProcessEventData::GetStateFromEvent(event_sp.get()); +#ifdef LLDB_SIMULATE_SLOW_STOPS + // Simulate a slow machine that takes a long time to process an internal + // stop, so that ThreadPlanSingleThreadTimeout's 10ms timer fires while we + // are still in here. This is helpful for diagnosing problems in + // the thread plans - particular the interaction with the + // ThreadPlanSingleThreadTimeout. + if (StateIsStoppedState(new_state, /*must_exist=*/true)) + std::this_thread::sleep_for(std::chrono::milliseconds(10)); +#endif + // First check to see if anybody wants a shot at this event: if (m_next_event_action_up) { NextEventAction::EventActionResult action_result =
diff --git a/source/Target/ThreadPlanSingleThreadTimeout.cpp b/source/Target/ThreadPlanSingleThreadTimeout.cpp index 71be813..39c9a79 100644 --- a/source/Target/ThreadPlanSingleThreadTimeout.cpp +++ b/source/Target/ThreadPlanSingleThreadTimeout.cpp
@@ -246,12 +246,18 @@ void ThreadPlanSingleThreadTimeout::HandleTimeout() { Log *log = GetLog(LLDBLog::Step); - LLDB_LOGF( - log, - "ThreadPlanSingleThreadTimeout::HandleTimeout() send async interrupt."); - m_state = State::AsyncInterrupt; - // Private state thread will only send async interrupt // in running state so no need to check state here. - m_process.SendAsyncInterrupt(&GetThread()); + if (m_process.GetPrivateState() == eStateRunning) { + LLDB_LOGF( + log, + "ThreadPlanSingleThreadTimeout::HandleTimeout() send async interrupt."); + m_process.SendAsyncInterrupt(&GetThread()); + + m_state = State::AsyncInterrupt; + } else { + LLDB_LOGF(log, "ThreadPlanSingleThreadTimeout::HandleTimeout() process " + "already stopped - didn't send interrupt"); + } + }