Index: source/Plugins/Process/Linux/ThreadStateCoordinator.h =================================================================== --- source/Plugins/Process/Linux/ThreadStateCoordinator.h +++ source/Plugins/Process/Linux/ThreadStateCoordinator.h @@ -29,12 +29,6 @@ // Typedefs. typedef std::unordered_set ThreadIDSet; - enum EventLoopResult - { - eventLoopResultContinue, - eventLoopResultStop - }; - // Callback/block definitions. typedef std::function ThreadIDFunction; typedef std::function LogFunction; @@ -133,19 +127,6 @@ private: - // Typedefs. - class EventBase; - - class EventCallAfterThreadsStop; - class EventThreadStopped; - class EventThreadCreate; - class EventThreadDeath; - class EventRequestResume; - - class EventReset; - - typedef std::shared_ptr EventBaseSP; - enum class ThreadState { Running, @@ -160,26 +141,95 @@ }; typedef std::unordered_map TIDContextMap; + struct PendingNotification + { + public: + PendingNotification (lldb::tid_t triggering_tid, + const ThreadIDSet &wait_for_stop_tids, + const StopThreadFunction &request_thread_stop_function, + const ThreadIDFunction &call_after_function, + const ErrorFunction &error_function): + triggering_tid (triggering_tid), + wait_for_stop_tids (wait_for_stop_tids), + original_wait_for_stop_tids (wait_for_stop_tids), + request_thread_stop_function (request_thread_stop_function), + call_after_function (call_after_function), + error_function (error_function), + request_stop_on_all_unstopped_threads (false), + skip_stop_request_tids () + { + } + + PendingNotification (lldb::tid_t triggering_tid, + const StopThreadFunction &request_thread_stop_function, + const ThreadIDFunction &call_after_function, + const ErrorFunction &error_function) : + triggering_tid (triggering_tid), + wait_for_stop_tids (), + original_wait_for_stop_tids (), + request_thread_stop_function (request_thread_stop_function), + call_after_function (call_after_function), + error_function (error_function), + request_stop_on_all_unstopped_threads (true), + skip_stop_request_tids () + { + } + + PendingNotification (lldb::tid_t triggering_tid, + const StopThreadFunction &request_thread_stop_function, + const ThreadIDFunction &call_after_function, + const ThreadIDSet &skip_stop_request_tids, + const ErrorFunction &error_function) : + triggering_tid (triggering_tid), + wait_for_stop_tids (), + original_wait_for_stop_tids (), + request_thread_stop_function (request_thread_stop_function), + call_after_function (call_after_function), + error_function (error_function), + request_stop_on_all_unstopped_threads (true), + skip_stop_request_tids (skip_stop_request_tids) + { + } + + const lldb::tid_t triggering_tid; + ThreadIDSet wait_for_stop_tids; + const ThreadIDSet original_wait_for_stop_tids; + StopThreadFunction request_thread_stop_function; + ThreadIDFunction call_after_function; + ErrorFunction error_function; + const bool request_stop_on_all_unstopped_threads; + ThreadIDSet skip_stop_request_tids; + }; + + // Fire pending notification if no pending thread stops remain. + void SignalIfRequirementsSatisfied(); + + bool + RequestStopOnAllSpecifiedThreads(); - std::mutex m_event_mutex; // Serializes execution of ProcessEvent. + void + RequestStopOnAllRunningThreads(); void - ProcessEvent (const EventBaseSP &event_sp); + RequestThreadStop (lldb::tid_t tid, ThreadContext& context); + + std::mutex m_event_mutex; // Serializes execution of TSC operations. void - SetPendingNotification (const EventBaseSP &event_sp); + ThreadDidStop (lldb::tid_t tid, bool initiated_by_llgs, const ErrorFunction &error_function); void - ThreadDidStop (lldb::tid_t tid, bool initiated_by_llgs, ErrorFunction &error_function); + DoResume(lldb::tid_t tid, ResumeThreadFunction request_thread_resume_function, + ErrorFunction error_function, bool error_when_already_running); void - ThreadWasCreated (lldb::tid_t tid, bool is_stopped, ErrorFunction &error_function); + DoCallAfterThreadsStop(std::unique_ptr &¬ification_up); void - ThreadDidDie (lldb::tid_t tid, ErrorFunction &error_function); + ThreadWasCreated (lldb::tid_t tid, bool is_stopped, const ErrorFunction &error_function); void - ResetNow (); + ThreadDidDie (lldb::tid_t tid, const ErrorFunction &error_function); bool IsKnownThread(lldb::tid_t tid) const; @@ -187,13 +237,9 @@ void Log (const char *format, ...); - EventCallAfterThreadsStop * - GetPendingThreadStopNotification (); - // Member variables. LogFunction m_log_function; - - EventBaseSP m_pending_notification_sp; + std::unique_ptr m_pending_notification_up; // Maps known TIDs to ThreadContext. TIDContextMap m_tid_map; Index: source/Plugins/Process/Linux/ThreadStateCoordinator.cpp =================================================================== --- source/Plugins/Process/Linux/ThreadStateCoordinator.cpp +++ source/Plugins/Process/Linux/ThreadStateCoordinator.cpp @@ -24,618 +24,281 @@ //===----------------------------------------------------------------------===// -class ThreadStateCoordinator::EventBase : public std::enable_shared_from_this -{ -public: - EventBase () - { - } - - virtual - ~EventBase () - { - } - - virtual std::string - GetDescription () = 0; - - // Return false if the coordinator should terminate running. - virtual EventLoopResult - ProcessEvent (ThreadStateCoordinator &coordinator) = 0; -}; - -//===----------------------------------------------------------------------===// - -class ThreadStateCoordinator::EventCallAfterThreadsStop : public ThreadStateCoordinator::EventBase +void +ThreadStateCoordinator::DoResume( + lldb::tid_t tid, + ResumeThreadFunction request_thread_resume_function, + ErrorFunction error_function, + bool error_when_already_running) { -public: - EventCallAfterThreadsStop (lldb::tid_t triggering_tid, - const ThreadIDSet &wait_for_stop_tids, - const StopThreadFunction &request_thread_stop_function, - const ThreadIDFunction &call_after_function, - const ErrorFunction &error_function): - EventBase (), - m_triggering_tid (triggering_tid), - m_wait_for_stop_tids (wait_for_stop_tids), - m_original_wait_for_stop_tids (wait_for_stop_tids), - m_request_thread_stop_function (request_thread_stop_function), - m_call_after_function (call_after_function), - m_error_function (error_function), - m_request_stop_on_all_unstopped_threads (false), - m_skip_stop_request_tids () - { - } - - EventCallAfterThreadsStop (lldb::tid_t triggering_tid, - const StopThreadFunction &request_thread_stop_function, - const ThreadIDFunction &call_after_function, - const ErrorFunction &error_function) : - EventBase (), - m_triggering_tid (triggering_tid), - m_wait_for_stop_tids (), - m_original_wait_for_stop_tids (), - m_request_thread_stop_function (request_thread_stop_function), - m_call_after_function (call_after_function), - m_error_function (error_function), - m_request_stop_on_all_unstopped_threads (true), - m_skip_stop_request_tids () - { - } - - EventCallAfterThreadsStop (lldb::tid_t triggering_tid, - const StopThreadFunction &request_thread_stop_function, - const ThreadIDFunction &call_after_function, - const ThreadIDSet &skip_stop_request_tids, - const ErrorFunction &error_function) : - EventBase (), - m_triggering_tid (triggering_tid), - m_wait_for_stop_tids (), - m_original_wait_for_stop_tids (), - m_request_thread_stop_function (request_thread_stop_function), - m_call_after_function (call_after_function), - m_error_function (error_function), - m_request_stop_on_all_unstopped_threads (true), - m_skip_stop_request_tids (skip_stop_request_tids) - { - } - - lldb::tid_t GetTriggeringTID () const - { - return m_triggering_tid; - } - - ThreadIDSet & - GetRemainingWaitTIDs () - { - return m_wait_for_stop_tids; - } - - const ThreadIDSet & - GetRemainingWaitTIDs () const - { - return m_wait_for_stop_tids; - } - - - const ThreadIDSet & - GetInitialWaitTIDs () const + // Ensure we know about the thread. + auto find_it = m_tid_map.find (tid); + if (find_it == m_tid_map.end ()) { - return m_original_wait_for_stop_tids; + // We don't know about this thread. This is an error condition. + std::ostringstream error_message; + error_message << "error: tid " << tid << " asked to resume but tid is unknown"; + error_function (error_message.str ()); + return; } - - EventLoopResult - ProcessEvent(ThreadStateCoordinator &coordinator) override - { - // Validate we know about the deferred trigger thread. - if (!coordinator.IsKnownThread (m_triggering_tid)) - { - // We don't know about this thread. This is an error condition. - std::ostringstream error_message; - error_message << "error: deferred notification tid " << m_triggering_tid << " is unknown"; - m_error_function (error_message.str ()); - - // We bail out here. - return eventLoopResultContinue; - } - - if (m_request_stop_on_all_unstopped_threads) - { - RequestStopOnAllRunningThreads (coordinator); - } - else - { - if (!RequestStopOnAllSpecifiedThreads (coordinator)) - return eventLoopResultContinue; - } - - if (m_wait_for_stop_tids.empty ()) + auto& context = find_it->second; + // Tell the thread to resume if we don't already think it is running. + const bool is_stopped = context.m_state == ThreadState::Stopped; + if (!is_stopped) + { + // It's not an error, just a log, if the error_when_already_running flag is not set. + // This covers cases where, for instance, we're just trying to resume all threads + // from the user side. + if (!error_when_already_running) { - // We're not waiting for any threads. Fire off the deferred signal delivery event. - NotifyNow (); + Log ("ThreadStateCoordinator::%s tid %" PRIu64 " optional resume skipped since it is already running", + __FUNCTION__, + tid); } else { - // The real meat of this class: wait until all - // the thread stops (or thread deaths) come in - // before firing off that the triggering signal - // arrived. - coordinator.SetPendingNotification (shared_from_this ()); + // Skip the resume call - we have tracked it to be running. And we unconditionally + // expected to resume this thread. Flag this as an error. + std::ostringstream error_message; + error_message << "error: tid " << tid << " asked to resume but we think it is already running"; + error_function (error_message.str ()); } - return eventLoopResultContinue; + // Error or not, we're done. + return; } - // Return true if still pending thread stops waiting; false if no more stops. - // If no more pending stops, signal. - bool - RemoveThreadStopRequirementAndMaybeSignal (lldb::tid_t tid) + // Before we do the resume below, first check if we have a pending + // stop notification this is currently or was previously waiting for + // this thread to stop. This is potentially a buggy situation since + // we're ostensibly waiting for threads to stop before we send out the + // pending notification, and here we are resuming one before we send + // out the pending stop notification. + if (m_pending_notification_up) { - // Remove this tid if it was in it. - m_wait_for_stop_tids.erase (tid); - - // Fire pending notification if no pending thread stops remain. - if (m_wait_for_stop_tids.empty ()) + if (m_pending_notification_up->wait_for_stop_tids.count (tid) > 0) { - // Fire the pending notification now. - NotifyNow (); - return false; + Log ("ThreadStateCoordinator::%s about to resume tid %" PRIu64 " per explicit request but we have a pending stop notification (tid %" PRIu64 ") that is actively waiting for this thread to stop. Valid sequence of events?", __FUNCTION__, tid, m_pending_notification_up->triggering_tid); } - - // Still have pending thread stops. - return true; - } - - void - AddThreadStopRequirement (lldb::tid_t tid) - { - // Add this tid. - auto insert_result = m_wait_for_stop_tids.insert (tid); - - // If it was really added, send the stop request to it. - if (insert_result.second) - m_request_thread_stop_function (tid); - } - - std::string - GetDescription () override - { - std::ostringstream description; - description << "EventCallAfterThreadsStop (triggering_tid=" << m_triggering_tid << ", request_stop_on_all_unstopped_threads=" << m_request_stop_on_all_unstopped_threads << ", skip_stop_request_tids.size()=" << m_skip_stop_request_tids.size () << ")"; - return description.str (); - } - -private: - - void - NotifyNow () - { - m_call_after_function (m_triggering_tid); - } - - bool - RequestStopOnAllSpecifiedThreads (ThreadStateCoordinator &coordinator) - { - // Request a stop for all the thread stops that need to be stopped - // and are not already known to be stopped. Keep a list of all the - // threads from which we still need to hear a stop reply. - - ThreadIDSet sent_tids; - for (auto tid : m_wait_for_stop_tids) + else if (m_pending_notification_up->original_wait_for_stop_tids.count (tid) > 0) { - // Validate we know about all tids for which we must first receive a stop before - // triggering the deferred stop notification. - auto find_it = coordinator.m_tid_map.find (tid); - if (find_it == coordinator.m_tid_map.end ()) + Log ("ThreadStateCoordinator::%s about to resume tid %" PRIu64 " per explicit request but we have a pending stop notification (tid %" PRIu64 ") that hasn't fired yet and this is one of the threads we had been waiting on (and already marked satisfied for this tid). Valid sequence of events?", __FUNCTION__, tid, m_pending_notification_up->triggering_tid); + for (auto tid : m_pending_notification_up->wait_for_stop_tids) { - // This is an error. We shouldn't be asking for waiting pids that aren't known. - // NOTE: we may be stripping out the specification of wait tids and handle this - // automatically, in which case this state can never occur. - std::ostringstream error_message; - error_message << "error: deferred notification for tid " << m_triggering_tid << " specified an unknown/untracked pending stop tid " << m_triggering_tid; - m_error_function (error_message.str ()); - - // Bail out here. - return false; - } - - // If the pending stop thread is currently running, we need to send it a stop request. - auto& context = find_it->second; - if (context.m_state == ThreadState::Running) - { - RequestThreadStop (tid, coordinator, context); - sent_tids.insert (tid); + Log ("ThreadStateCoordinator::%s tid %" PRIu64 " deferred stop notification still waiting on tid %" PRIu64, + __FUNCTION__, + m_pending_notification_up->triggering_tid, + tid); } } - - // We only need to wait for the sent_tids - so swap our wait set - // to the sent tids. The rest are already stopped and we won't - // be receiving stop notifications for them. - m_wait_for_stop_tids.swap (sent_tids); - - // Succeeded, keep running. - return true; } - void - RequestStopOnAllRunningThreads (ThreadStateCoordinator &coordinator) + // Request a resume. We expect this to be synchronous and the system + // to reflect it is running after this completes. + const auto error = request_thread_resume_function (tid, false); + if (error.Success ()) { - // Request a stop for all the thread stops that need to be stopped - // and are not already known to be stopped. Keep a list of all the - // threads from which we still need to hear a stop reply. - - ThreadIDSet sent_tids; - for (auto it = coordinator.m_tid_map.begin(); it != coordinator.m_tid_map.end(); ++it) - { - // We only care about threads not stopped. - const bool running = it->second.m_state == ThreadState::Running; - if (running) - { - const lldb::tid_t tid = it->first; - - // Request this thread stop if the tid stop request is not explicitly ignored. - const bool skip_stop_request = m_skip_stop_request_tids.count (tid) > 0; - if (!skip_stop_request) - RequestThreadStop (tid, coordinator, it->second); - - // Even if we skipped sending the stop request for other reasons (like stepping), - // we still need to wait for that stepping thread to notify completion/stop. - sent_tids.insert (tid); - } - } - - // Set the wait list to the set of tids for which we requested stops. - m_wait_for_stop_tids.swap (sent_tids); + // Now mark it is running. + context.m_state = ThreadState::Running; + context.m_request_resume_function = request_thread_resume_function; } - - void - RequestThreadStop (lldb::tid_t tid, ThreadStateCoordinator &coordinator, ThreadContext& context) + else { - const auto error = m_request_thread_stop_function (tid); - if (error.Success ()) - { - context.m_stop_requested = true; - } - else - { - coordinator.Log ("EventCallAfterThreadsStop::%s failed to request thread stop tid %" PRIu64 ": %s", - __FUNCTION__, tid, error.AsCString ()); - } + Log ("ThreadStateCoordinator::%s failed to resume thread tid %" PRIu64 ": %s", + __FUNCTION__, tid, error.AsCString ()); } - const lldb::tid_t m_triggering_tid; - ThreadIDSet m_wait_for_stop_tids; - const ThreadIDSet m_original_wait_for_stop_tids; - StopThreadFunction m_request_thread_stop_function; - ThreadIDFunction m_call_after_function; - ErrorFunction m_error_function; - const bool m_request_stop_on_all_unstopped_threads; - ThreadIDSet m_skip_stop_request_tids; -}; + return; +} //===----------------------------------------------------------------------===// -class ThreadStateCoordinator::EventReset : public ThreadStateCoordinator::EventBase +ThreadStateCoordinator::ThreadStateCoordinator (const LogFunction &log_function) : + m_log_function (log_function), + m_tid_map (), + m_log_event_processing (false) { -public: - EventReset (): - EventBase () - { - } +} - EventLoopResult - ProcessEvent(ThreadStateCoordinator &coordinator) override - { - coordinator.ResetNow (); - return eventLoopResultContinue; - } +void +ThreadStateCoordinator::CallAfterThreadsStop (const lldb::tid_t triggering_tid, + const ThreadIDSet &wait_for_stop_tids, + const StopThreadFunction &request_thread_stop_function, + const ThreadIDFunction &call_after_function, + const ErrorFunction &error_function) +{ + std::lock_guard lock(m_event_mutex); - std::string - GetDescription () override + if (m_log_event_processing) { - return "EventReset"; + Log ("ThreadStateCoordinator::%s about to process event: (triggering_tid: %" PRIu64 ", wait_for_stop_tids.size(): %zd)", + __FUNCTION__, triggering_tid, wait_for_stop_tids.size()); } -}; -//===----------------------------------------------------------------------===// + DoCallAfterThreadsStop(std::unique_ptr(new PendingNotification( + triggering_tid, wait_for_stop_tids, request_thread_stop_function, + call_after_function, error_function))); -class ThreadStateCoordinator::EventThreadStopped : public ThreadStateCoordinator::EventBase -{ -public: - EventThreadStopped (lldb::tid_t tid, - bool initiated_by_llgs, - const ErrorFunction &error_function): - EventBase (), - m_tid (tid), - m_initiated_by_llgs (initiated_by_llgs), - m_error_function (error_function) + if (m_log_event_processing) { + Log ("ThreadStateCoordinator::%s event processing done", __FUNCTION__); } +} - EventLoopResult - ProcessEvent(ThreadStateCoordinator &coordinator) override - { - coordinator.ThreadDidStop (m_tid, m_initiated_by_llgs, m_error_function); - return eventLoopResultContinue; - } +void +ThreadStateCoordinator::CallAfterRunningThreadsStop (const lldb::tid_t triggering_tid, + const StopThreadFunction &request_thread_stop_function, + const ThreadIDFunction &call_after_function, + const ErrorFunction &error_function) +{ + std::lock_guard lock(m_event_mutex); - std::string - GetDescription () override + if (m_log_event_processing) { - std::ostringstream description; - description << "EventThreadStopped (tid=" << m_tid << ")"; - return description.str (); + Log ("ThreadStateCoordinator::%s about to process event: (triggering_tid: %" PRIu64 ")", + __FUNCTION__, triggering_tid); } -private: - - const lldb::tid_t m_tid; - const bool m_initiated_by_llgs; - ErrorFunction m_error_function; -}; - -//===----------------------------------------------------------------------===// + DoCallAfterThreadsStop(std::unique_ptr(new PendingNotification( + triggering_tid, + request_thread_stop_function, + call_after_function, + error_function))); -class ThreadStateCoordinator::EventThreadCreate : public ThreadStateCoordinator::EventBase -{ -public: - EventThreadCreate (lldb::tid_t tid, - bool is_stopped, - const ErrorFunction &error_function): - EventBase (), - m_tid (tid), - m_is_stopped (is_stopped), - m_error_function (error_function) + if (m_log_event_processing) { + Log ("ThreadStateCoordinator::%s event processing done", __FUNCTION__); } +} - EventLoopResult - ProcessEvent(ThreadStateCoordinator &coordinator) override - { - coordinator.ThreadWasCreated (m_tid, m_is_stopped, m_error_function); - return eventLoopResultContinue; - } +void +ThreadStateCoordinator::CallAfterRunningThreadsStopWithSkipTIDs (lldb::tid_t triggering_tid, + const ThreadIDSet &skip_stop_request_tids, + const StopThreadFunction &request_thread_stop_function, + const ThreadIDFunction &call_after_function, + const ErrorFunction &error_function) +{ + std::lock_guard lock(m_event_mutex); - std::string - GetDescription () override + if (m_log_event_processing) { - std::ostringstream description; - description << "EventThreadCreate (tid=" << m_tid << ", " << (m_is_stopped ? "stopped" : "running") << ")"; - return description.str (); + Log ("ThreadStateCoordinator::%s about to process event: (triggering_tid: %" PRIu64 ", skip_stop_request_tids.size(): %zd)", + __FUNCTION__, triggering_tid, skip_stop_request_tids.size()); } -private: - - const lldb::tid_t m_tid; - const bool m_is_stopped; - ErrorFunction m_error_function; -}; - -//===----------------------------------------------------------------------===// - -class ThreadStateCoordinator::EventThreadDeath : public ThreadStateCoordinator::EventBase -{ -public: - EventThreadDeath (lldb::tid_t tid, - const ErrorFunction &error_function): - EventBase (), - m_tid (tid), - m_error_function (error_function) - { - } + DoCallAfterThreadsStop(std::unique_ptr(new PendingNotification( + triggering_tid, + request_thread_stop_function, + call_after_function, + skip_stop_request_tids, + error_function))); - EventLoopResult - ProcessEvent(ThreadStateCoordinator &coordinator) override + if (m_log_event_processing) { - coordinator.ThreadDidDie (m_tid, m_error_function); - return eventLoopResultContinue; + Log ("ThreadStateCoordinator::%s event processing done", __FUNCTION__); } +} - std::string - GetDescription () override +void +ThreadStateCoordinator::SignalIfRequirementsSatisfied() +{ + if (m_pending_notification_up && m_pending_notification_up->wait_for_stop_tids.empty ()) { - std::ostringstream description; - description << "EventThreadDeath (tid=" << m_tid << ")"; - return description.str (); + m_pending_notification_up->call_after_function(m_pending_notification_up->triggering_tid); + m_pending_notification_up.reset(); } +} -private: - - const lldb::tid_t m_tid; - ErrorFunction m_error_function; -}; - -//===----------------------------------------------------------------------===// - -class ThreadStateCoordinator::EventRequestResume : public ThreadStateCoordinator::EventBase +bool +ThreadStateCoordinator::RequestStopOnAllSpecifiedThreads() { -public: - EventRequestResume (lldb::tid_t tid, - const ResumeThreadFunction &request_thread_resume_function, - const ErrorFunction &error_function, - bool error_when_already_running): - EventBase (), - m_tid (tid), - m_request_thread_resume_function (request_thread_resume_function), - m_error_function (error_function), - m_error_when_already_running (error_when_already_running) - { - } + // Request a stop for all the thread stops that need to be stopped + // and are not already known to be stopped. Keep a list of all the + // threads from which we still need to hear a stop reply. - EventLoopResult - ProcessEvent(ThreadStateCoordinator &coordinator) override + ThreadIDSet sent_tids; + for (auto tid : m_pending_notification_up->wait_for_stop_tids) { - // Ensure we know about the thread. - auto find_it = coordinator.m_tid_map.find (m_tid); - if (find_it == coordinator.m_tid_map.end ()) + // Validate we know about all tids for which we must first receive a stop before + // triggering the deferred stop notification. + auto find_it = m_tid_map.find (tid); + if (find_it == m_tid_map.end ()) { - // We don't know about this thread. This is an error condition. + // This is an error. We shouldn't be asking for waiting pids that aren't known. + // NOTE: we may be stripping out the specification of wait tids and handle this + // automatically, in which case this state can never occur. std::ostringstream error_message; - error_message << "error: tid " << m_tid << " asked to resume but tid is unknown"; - m_error_function (error_message.str ()); - return eventLoopResultContinue; - } - auto& context = find_it->second; - // Tell the thread to resume if we don't already think it is running. - const bool is_stopped = context.m_state == ThreadState::Stopped; - if (!is_stopped) - { - // It's not an error, just a log, if the m_already_running_no_error flag is set. - // This covers cases where, for instance, we're just trying to resume all threads - // from the user side. - if (!m_error_when_already_running) - { - coordinator.Log ("EventRequestResume::%s tid %" PRIu64 " optional resume skipped since it is already running", - __FUNCTION__, - m_tid); - } - else - { - // Skip the resume call - we have tracked it to be running. And we unconditionally - // expected to resume this thread. Flag this as an error. - std::ostringstream error_message; - error_message << "error: tid " << m_tid << " asked to resume but we think it is already running"; - m_error_function (error_message.str ()); - } - - // Error or not, we're done. - return eventLoopResultContinue; - } + error_message << "error: deferred notification for tid " << m_pending_notification_up->triggering_tid << " specified an unknown/untracked pending stop tid " << m_pending_notification_up->triggering_tid; + m_pending_notification_up->error_function (error_message.str ()); - // Before we do the resume below, first check if we have a pending - // stop notification this is currently or was previously waiting for - // this thread to stop. This is potentially a buggy situation since - // we're ostensibly waiting for threads to stop before we send out the - // pending notification, and here we are resuming one before we send - // out the pending stop notification. - const EventCallAfterThreadsStop *const pending_stop_notification = coordinator.GetPendingThreadStopNotification (); - if (pending_stop_notification) - { - if (pending_stop_notification->GetRemainingWaitTIDs ().count (m_tid) > 0) - { - coordinator.Log ("EventRequestResume::%s about to resume tid %" PRIu64 " per explicit request but we have a pending stop notification (tid %" PRIu64 ") that is actively waiting for this thread to stop. Valid sequence of events?", __FUNCTION__, m_tid, pending_stop_notification->GetTriggeringTID ()); - } - else if (pending_stop_notification->GetInitialWaitTIDs ().count (m_tid) > 0) - { - coordinator.Log ("EventRequestResume::%s about to resume tid %" PRIu64 " per explicit request but we have a pending stop notification (tid %" PRIu64 ") that hasn't fired yet and this is one of the threads we had been waiting on (and already marked satisfied for this tid). Valid sequence of events?", __FUNCTION__, m_tid, pending_stop_notification->GetTriggeringTID ()); - for (auto tid : pending_stop_notification->GetRemainingWaitTIDs ()) - { - coordinator.Log ("EventRequestResume::%s tid %" PRIu64 " deferred stop notification still waiting on tid %" PRIu64, - __FUNCTION__, - pending_stop_notification->GetTriggeringTID (), - tid); - } - } + // Bail out here. + return false; } - // Request a resume. We expect this to be synchronous and the system - // to reflect it is running after this completes. - const auto error = m_request_thread_resume_function (m_tid, false); - if (error.Success ()) - { - // Now mark it is running. - context.m_state = ThreadState::Running; - context.m_request_resume_function = m_request_thread_resume_function; - } - else + // If the pending stop thread is currently running, we need to send it a stop request. + auto& context = find_it->second; + if (context.m_state == ThreadState::Running) { - coordinator.Log ("EventRequestResume::%s failed to resume thread tid %" PRIu64 ": %s", - __FUNCTION__, m_tid, error.AsCString ()); + RequestThreadStop (tid, context); + sent_tids.insert (tid); } - - return eventLoopResultContinue; } + // We only need to wait for the sent_tids - so swap our wait set + // to the sent tids. The rest are already stopped and we won't + // be receiving stop notifications for them. + m_pending_notification_up->wait_for_stop_tids.swap (sent_tids); - std::string - GetDescription () override - { - std::ostringstream description; - description << "EventRequestResume (tid=" << m_tid << ")"; - return description.str (); - } - -private: - - const lldb::tid_t m_tid; - ResumeThreadFunction m_request_thread_resume_function; - ErrorFunction m_error_function; - const bool m_error_when_already_running; -}; - -//===----------------------------------------------------------------------===// - -ThreadStateCoordinator::ThreadStateCoordinator (const LogFunction &log_function) : - m_log_function (log_function), - m_tid_map (), - m_log_event_processing (false) -{ + // Succeeded, keep running. + return true; } void -ThreadStateCoordinator::SetPendingNotification (const EventBaseSP &event_sp) +ThreadStateCoordinator::RequestStopOnAllRunningThreads() { - assert (event_sp && "null event_sp"); - if (!event_sp) - return; + // Request a stop for all the thread stops that need to be stopped + // and are not already known to be stopped. Keep a list of all the + // threads from which we still need to hear a stop reply. - const EventCallAfterThreadsStop *new_call_after_event = static_cast (event_sp.get ()); - - EventCallAfterThreadsStop *const prev_call_after_event = GetPendingThreadStopNotification (); - if (prev_call_after_event) + ThreadIDSet sent_tids; + for (auto it = m_tid_map.begin(); it != m_tid_map.end(); ++it) { - // Yikes - we've already got a pending signal notification in progress. - // Log this info. We lose the pending notification here. - Log ("ThreadStateCoordinator::%s dropping existing pending signal notification for tid %" PRIu64 ", to be replaced with signal for tid %" PRIu64, - __FUNCTION__, - prev_call_after_event->GetTriggeringTID (), - new_call_after_event->GetTriggeringTID ()); - } + // We only care about threads not stopped. + const bool running = it->second.m_state == ThreadState::Running; + if (running) + { + const lldb::tid_t tid = it->first; - m_pending_notification_sp = event_sp; -} + // Request this thread stop if the tid stop request is not explicitly ignored. + const bool skip_stop_request = m_pending_notification_up->skip_stop_request_tids.count (tid) > 0; + if (!skip_stop_request) + RequestThreadStop (tid, it->second); -void -ThreadStateCoordinator::CallAfterThreadsStop (const lldb::tid_t triggering_tid, - const ThreadIDSet &wait_for_stop_tids, - const StopThreadFunction &request_thread_stop_function, - const ThreadIDFunction &call_after_function, - const ErrorFunction &error_function) -{ - ProcessEvent(EventBaseSP(new EventCallAfterThreadsStop (triggering_tid, - wait_for_stop_tids, - request_thread_stop_function, - call_after_function, - error_function))); -} + // Even if we skipped sending the stop request for other reasons (like stepping), + // we still need to wait for that stepping thread to notify completion/stop. + sent_tids.insert (tid); + } + } -void -ThreadStateCoordinator::CallAfterRunningThreadsStop (const lldb::tid_t triggering_tid, - const StopThreadFunction &request_thread_stop_function, - const ThreadIDFunction &call_after_function, - const ErrorFunction &error_function) -{ - ProcessEvent(EventBaseSP(new EventCallAfterThreadsStop (triggering_tid, - request_thread_stop_function, - call_after_function, - error_function))); + // Set the wait list to the set of tids for which we requested stops. + m_pending_notification_up->wait_for_stop_tids.swap (sent_tids); } void -ThreadStateCoordinator::CallAfterRunningThreadsStopWithSkipTIDs (lldb::tid_t triggering_tid, - const ThreadIDSet &skip_stop_request_tids, - const StopThreadFunction &request_thread_stop_function, - const ThreadIDFunction &call_after_function, - const ErrorFunction &error_function) +ThreadStateCoordinator::RequestThreadStop (lldb::tid_t tid, ThreadContext& context) { - ProcessEvent(EventBaseSP(new EventCallAfterThreadsStop (triggering_tid, - request_thread_stop_function, - call_after_function, - skip_stop_request_tids, - error_function))); + const auto error = m_pending_notification_up->request_thread_stop_function (tid); + if (error.Success ()) + context.m_stop_requested = true; + else + { + Log ("ThreadStateCoordinator::%s failed to request thread stop tid %" PRIu64 ": %s", + __FUNCTION__, tid, error.AsCString ()); + } } void -ThreadStateCoordinator::ThreadDidStop (lldb::tid_t tid, bool initiated_by_llgs, ErrorFunction &error_function) +ThreadStateCoordinator::ThreadDidStop (lldb::tid_t tid, bool initiated_by_llgs, const ErrorFunction &error_function) { // Ensure we know about the thread. auto find_it = m_tid_map.find (tid); @@ -655,15 +318,10 @@ context.m_stop_requested = false; // If we have a pending notification, remove this from the set. - EventCallAfterThreadsStop *const call_after_event = GetPendingThreadStopNotification (); - if (call_after_event) + if (m_pending_notification_up) { - const bool pending_stops_remain = call_after_event->RemoveThreadStopRequirementAndMaybeSignal (tid); - if (!pending_stops_remain) - { - // Clear the pending notification now. - m_pending_notification_sp.reset (); - } + m_pending_notification_up->wait_for_stop_tids.erase(tid); + SignalIfRequirementsSatisfied(); } if (initiated_by_llgs && context.m_request_resume_function && !stop_was_requested) @@ -685,7 +343,49 @@ } void -ThreadStateCoordinator::ThreadWasCreated (lldb::tid_t tid, bool is_stopped, ErrorFunction &error_function) +ThreadStateCoordinator::DoCallAfterThreadsStop(std::unique_ptr &¬ification_up) +{ + // Validate we know about the deferred trigger thread. + if (!IsKnownThread (notification_up->triggering_tid)) + { + // We don't know about this thread. This is an error condition. + std::ostringstream error_message; + error_message << "error: deferred notification tid " << notification_up->triggering_tid << " is unknown"; + notification_up->error_function (error_message.str ()); + + // We bail out here. + return; + } + + if (m_pending_notification_up) + { + // Yikes - we've already got a pending signal notification in progress. + // Log this info. We lose the pending notification here. + Log ("ThreadStateCoordinator::%s dropping existing pending signal notification for tid %" PRIu64 ", to be replaced with signal for tid %" PRIu64, + __FUNCTION__, + m_pending_notification_up->triggering_tid, + notification_up->triggering_tid); + } + m_pending_notification_up = std::move(notification_up); + + if (m_pending_notification_up->request_stop_on_all_unstopped_threads) + RequestStopOnAllRunningThreads(); + else + { + if (!RequestStopOnAllSpecifiedThreads()) + return; + } + + if (m_pending_notification_up->wait_for_stop_tids.empty ()) + { + // We're not waiting for any threads. Fire off the deferred signal delivery event. + m_pending_notification_up->call_after_function(m_pending_notification_up->triggering_tid); + m_pending_notification_up.reset(); + } +} + +void +ThreadStateCoordinator::ThreadWasCreated (lldb::tid_t tid, bool is_stopped, const ErrorFunction &error_function) { // Ensure we don't already know about the thread. auto find_it = m_tid_map.find (tid); @@ -703,17 +403,17 @@ ctx.m_state = (is_stopped) ? ThreadState::Stopped : ThreadState::Running; m_tid_map[tid] = std::move(ctx); - EventCallAfterThreadsStop *const call_after_event = GetPendingThreadStopNotification (); - if (call_after_event && !is_stopped) + if (m_pending_notification_up && !is_stopped) { - // Tell the pending notification that we need to wait - // for this new thread to stop. - call_after_event->AddThreadStopRequirement (tid); + // We will need to wait for this new thread to stop as well before firing the + // notification. + m_pending_notification_up->wait_for_stop_tids.insert(tid); + m_pending_notification_up->request_thread_stop_function(tid); } } void -ThreadStateCoordinator::ThreadDidDie (lldb::tid_t tid, ErrorFunction &error_function) +ThreadStateCoordinator::ThreadDidDie (lldb::tid_t tid, const ErrorFunction &error_function) { // Ensure we know about the thread. auto find_it = m_tid_map.find (tid); @@ -732,32 +432,14 @@ m_tid_map.erase (find_it); // If we have a pending notification, remove this from the set. - EventCallAfterThreadsStop *const call_after_event = GetPendingThreadStopNotification (); - if (call_after_event) + if (m_pending_notification_up) { - const bool pending_stops_remain = call_after_event->RemoveThreadStopRequirementAndMaybeSignal (tid); - if (!pending_stops_remain) - { - // Clear the pending notification now. - m_pending_notification_sp.reset (); - } + m_pending_notification_up->wait_for_stop_tids.erase(tid); + SignalIfRequirementsSatisfied(); } } void -ThreadStateCoordinator::ResetNow () -{ - // Clear the pending notification if there was one. - m_pending_notification_sp.reset (); - - // Clear the stop map - we no longer know anything about any thread state. - // The caller is expected to reset thread states for all threads, and we - // will assume anything we haven't heard about is running and requires a - // stop. - m_tid_map.clear (); -} - -void ThreadStateCoordinator::Log (const char *format, ...) { va_list args; @@ -773,7 +455,20 @@ bool initiated_by_llgs, const ErrorFunction &error_function) { - ProcessEvent(EventBaseSP(new EventThreadStopped (tid, initiated_by_llgs, error_function))); + std::lock_guard lock(m_event_mutex); + + if (m_log_event_processing) + { + Log ("ThreadStateCoordinator::%s about to process event: (tid: %" PRIu64 ", %sinitiated by llgs)", + __FUNCTION__, tid, initiated_by_llgs?"":"not "); + } + + ThreadDidStop (tid, initiated_by_llgs, error_function); + + if (m_log_event_processing) + { + Log ("ThreadStateCoordinator::%s event processing done", __FUNCTION__); + } } void @@ -781,7 +476,20 @@ const ResumeThreadFunction &request_thread_resume_function, const ErrorFunction &error_function) { - ProcessEvent(EventBaseSP(new EventRequestResume (tid, request_thread_resume_function, error_function, true))); + std::lock_guard lock(m_event_mutex); + + if (m_log_event_processing) + { + Log ("ThreadStateCoordinator::%s about to process event: (tid: %" PRIu64 ")", + __FUNCTION__, tid); + } + + DoResume(tid, request_thread_resume_function, error_function, true); + + if (m_log_event_processing) + { + Log ("ThreadStateCoordinator::%s event processing done", __FUNCTION__); + } } void @@ -789,7 +497,20 @@ const ResumeThreadFunction &request_thread_resume_function, const ErrorFunction &error_function) { - ProcessEvent(EventBaseSP(new EventRequestResume (tid, request_thread_resume_function, error_function, false))); + std::lock_guard lock(m_event_mutex); + + if (m_log_event_processing) + { + Log ("ThreadStateCoordinator::%s about to process event: (tid: %" PRIu64 ")", + __FUNCTION__, tid); + } + + DoResume (tid, request_thread_resume_function, error_function, false); + + if (m_log_event_processing) + { + Log ("ThreadStateCoordinator::%s event processing done", __FUNCTION__); + } } void @@ -797,53 +518,71 @@ bool is_stopped, const ErrorFunction &error_function) { - ProcessEvent(EventBaseSP(new EventThreadCreate (tid, is_stopped, error_function))); + std::lock_guard lock(m_event_mutex); + + if (m_log_event_processing) + { + Log ("ThreadStateCoordinator::%s about to process event: (tid: %" PRIu64 ", is %sstopped)", + __FUNCTION__, tid, is_stopped?"":"not "); + } + + ThreadWasCreated (tid, is_stopped, error_function); + + if (m_log_event_processing) + { + Log ("ThreadStateCoordinator::%s event processing done", __FUNCTION__); + } } void ThreadStateCoordinator::NotifyThreadDeath (lldb::tid_t tid, const ErrorFunction &error_function) { - ProcessEvent(EventBaseSP(new EventThreadDeath (tid, error_function))); -} + std::lock_guard lock(m_event_mutex); -void -ThreadStateCoordinator::ResetForExec () -{ - ProcessEvent(EventBaseSP(new EventReset())); + if (m_log_event_processing) + { + Log ("ThreadStateCoordinator::%s about to process event: (tid: %" PRIu64 ")", __FUNCTION__, tid); + } + + ThreadDidDie(tid, error_function); + + if (m_log_event_processing) + { + Log ("ThreadStateCoordinator::%s event processing done", __FUNCTION__); + } } void -ThreadStateCoordinator::ProcessEvent (const EventBaseSP &event_sp) +ThreadStateCoordinator::ResetForExec () { std::lock_guard lock(m_event_mutex); if (m_log_event_processing) { - Log ("ThreadStateCoordinator::%s about to process event: %s", __FUNCTION__, event_sp->GetDescription ().c_str ()); + Log ("ThreadStateCoordinator::%s about to process event", __FUNCTION__); } - // Process the event. - event_sp->ProcessEvent (*this); + // Clear the pending notification if there was one. + m_pending_notification_up.reset (); + + // Clear the stop map - we no longer know anything about any thread state. + // The caller is expected to reset thread states for all threads, and we + // will assume anything we haven't heard about is running and requires a + // stop. + m_tid_map.clear (); if (m_log_event_processing) { Log ("ThreadStateCoordinator::%s event processing done", __FUNCTION__); } } - void ThreadStateCoordinator::LogEnableEventProcessing (bool enabled) { m_log_event_processing = enabled; } -ThreadStateCoordinator::EventCallAfterThreadsStop * -ThreadStateCoordinator::GetPendingThreadStopNotification () -{ - return static_cast (m_pending_notification_sp.get ()); -} - bool ThreadStateCoordinator::IsKnownThread (lldb::tid_t tid) const {