Index: lldb/include/lldb/Target/Process.h =================================================================== --- lldb/include/lldb/Target/Process.h +++ lldb/include/lldb/Target/Process.h @@ -479,11 +479,7 @@ static void SetInterruptedInEvent(Event *event_ptr, bool new_value); - static bool SetUpdateStateOnRemoval(Event *event_ptr); - private: - void SetUpdateStateOnRemoval() { m_update_state++; } - void SetRestarted(bool new_value) { m_restarted = new_value; } void SetInterrupted(bool new_value) { m_interrupted = new_value; } @@ -497,7 +493,8 @@ std::vector m_restarted_reasons; bool m_restarted; // For "eStateStopped" events, this is true if the target // was automatically restarted. - int m_update_state; + std::mutex m_do_on_removal_mutex; + bool m_do_on_removal_executed; bool m_interrupted; ProcessEventData(const ProcessEventData &) = delete; Index: lldb/source/Target/Process.cpp =================================================================== --- lldb/source/Target/Process.cpp +++ lldb/source/Target/Process.cpp @@ -3774,7 +3774,6 @@ StateAsCString(GetState()), is_hijacked ? "hijacked" : "public"); } - Process::ProcessEventData::SetUpdateStateOnRemoval(event_sp.get()); if (StateIsRunningState(new_state)) { // Only push the input handler if we aren't fowarding events, as this // means the curses GUI is in use... Or don't push it if we are launching @@ -3987,12 +3986,12 @@ Process::ProcessEventData::ProcessEventData() : EventData(), m_process_wp(), m_state(eStateInvalid), m_restarted(false), - m_update_state(0), m_interrupted(false) {} + m_do_on_removal_executed(false), m_interrupted(false) {} Process::ProcessEventData::ProcessEventData(const ProcessSP &process_sp, StateType state) : EventData(), m_process_wp(), m_state(state), m_restarted(false), - m_update_state(0), m_interrupted(false) { + m_do_on_removal_executed(false), m_interrupted(false) { if (process_sp) m_process_wp = process_sp; } @@ -4117,20 +4116,34 @@ } void Process::ProcessEventData::DoOnRemoval(Event *event_ptr) { - ProcessSP process_sp(m_process_wp.lock()); + // This function gets called each time an event gets pulled off of some + // listener's event queue. However, all the code below must be executed only + // once and after an event is already handled by the private state listener. + // So, here are two guards: the first of them checks that this event has been + // broadcasted by the public broadcaster and the second checks that this code + // hasn't been executed yet. + + Broadcaster *broadcaster = event_ptr->GetBroadcaster(); + if (!broadcaster || + broadcaster->GetBroadcasterName() != Process::GetStaticBroadcasterClass()) + return; - if (!process_sp) + // Since different listeners are usually monitored by different threads, two + // threads may enter this function simultaneously. This lock ensures that + // only one thread executes all the code below in this case. This lock must + // be taken during the whole DoOnRemoval execution to ensure that other + // threads (which don't execute the code below) won't resume while DoOnRemoval + // does all the stuff about public process state and thus until the public + // state becomes correct. + std::lock_guard do_on_removal_lock(m_do_on_removal_mutex); + if (m_do_on_removal_executed) return; - // This function gets called twice for each event, once when the event gets - // pulled off of the private process event queue, and then any number of - // times, first when it gets pulled off of the public event queue, then other - // times when we're pretending that this is where we stopped at the end of - // expression evaluation. m_update_state is used to distinguish these three - // cases; it is 0 when we're just pulling it off for private handling, and > - // 1 for expression evaluation, and we don't want to do the breakpoint - // command handling then. - if (m_update_state != 1) + m_do_on_removal_executed = true; + + ProcessSP process_sp(m_process_wp.lock()); + + if (!process_sp) return; process_sp->SetPublicState( @@ -4287,16 +4300,6 @@ data->SetInterrupted(new_value); } -bool Process::ProcessEventData::SetUpdateStateOnRemoval(Event *event_ptr) { - ProcessEventData *data = - const_cast(GetEventDataFromEvent(event_ptr)); - if (data) { - data->SetUpdateStateOnRemoval(); - return true; - } - return false; -} - lldb::TargetSP Process::CalculateTarget() { return m_target_wp.lock(); } void Process::CalculateExecutionContext(ExecutionContext &exe_ctx) { Index: lldb/unittests/Process/ProcessEventDataTest.cpp =================================================================== --- lldb/unittests/Process/ProcessEventDataTest.cpp +++ lldb/unittests/Process/ProcessEventDataTest.cpp @@ -169,7 +169,7 @@ ProcessEventDataSP event_data_sp = std::make_shared(process_sp, eStateStopped); EventSP event_sp = std::make_shared(0, event_data_sp); - event_data_sp->SetUpdateStateOnRemoval(event_sp.get()); + process_sp->BroadcastEvent(event_sp); event_data_sp->DoOnRemoval(event_sp.get()); bool result = static_cast(event_data_sp.get()) ->m_should_stop_hit_count == 1; @@ -181,7 +181,7 @@ event_data_sp = std::make_shared(process_sp, eStateStepping); event_sp = std::make_shared(0, event_data_sp); - event_data_sp->SetUpdateStateOnRemoval(event_sp.get()); + process_sp->BroadcastEvent(event_sp); event_data_sp->DoOnRemoval(event_sp.get()); result = static_cast(event_data_sp.get()) ->m_should_stop_hit_count == 0;