Index: include/lldb/Target/OperatingSystem.h =================================================================== --- include/lldb/Target/OperatingSystem.h +++ include/lldb/Target/OperatingSystem.h @@ -65,9 +65,9 @@ // Plug-in Methods //------------------------------------------------------------------ virtual bool - UpdateThreadList (ThreadList &old_thread_list, - ThreadList &real_thread_list, - ThreadList &new_thread_list) = 0; + UpdateThreadList (ProcessThreadList &old_thread_list, + ProcessThreadList &real_thread_list, + ProcessThreadList &new_thread_list) = 0; virtual void ThreadWasSelected (Thread *thread) = 0; Index: include/lldb/Target/Process.h =================================================================== --- include/lldb/Target/Process.h +++ include/lldb/Target/Process.h @@ -48,7 +48,7 @@ #include "lldb/Target/ProcessInfo.h" #include "lldb/Target/ProcessLaunchInfo.h" #include "lldb/Target/QueueList.h" -#include "lldb/Target/ThreadList.h" +#include "lldb/Target/ProcessThreadList.h" #include "lldb/Target/UnixSignals.h" #include "lldb/Utility/PseudoTerminal.h" @@ -701,7 +701,7 @@ friend class ProcessEventData; friend class StopInfo; friend class Target; - friend class ThreadList; + friend class ProcessThreadList; public: @@ -2601,12 +2601,12 @@ // Thread Queries //------------------------------------------------------------------ virtual bool - UpdateThreadList (ThreadList &old_thread_list, ThreadList &new_thread_list) = 0; + UpdateThreadList (ProcessThreadList &old_thread_list, ProcessThreadList &new_thread_list) = 0; void UpdateThreadListIfNeeded (); - ThreadList & + ProcessThreadList & GetThreadList () { return m_thread_list; @@ -2617,13 +2617,13 @@ // threads in this list are not iterated over - driver programs need to // request the extended backtrace calls starting from a root concrete // thread one by one. - ThreadList & + ProcessThreadList & GetExtendedThreadList () { return m_extended_thread_list; } - ThreadList::ThreadIterable + ProcessThreadList::ThreadIterable Threads () { return m_thread_list.Threads(); @@ -2914,6 +2914,9 @@ else return m_public_run_lock; } + + lldb::ThreadListSP + GetHistoryThreads(lldb::addr_t addr); public: virtual Error @@ -3047,10 +3050,10 @@ int m_exit_status; ///< The exit status of the process, or -1 if not set. std::string m_exit_string; ///< A textual description of why a process exited. Mutex m_thread_mutex; - ThreadList m_thread_list_real; ///< The threads for this process as are known to the protocol we are debugging with - ThreadList m_thread_list; ///< The threads for this process as the user will see them. This is usually the same as + ProcessThreadList m_thread_list_real; ///< The threads for this process as are known to the protocol we are debugging with + ProcessThreadList m_thread_list; ///< The threads for this process as the user will see them. This is usually the same as ///< m_thread_list_real, but might be different if there is an OS plug-in creating memory threads - ThreadList m_extended_thread_list; ///< Owner for extended threads that may be generated, cleared on natural stops + ProcessThreadList m_extended_thread_list; ///< Owner for extended threads that may be generated, cleared on natural stops uint32_t m_extended_thread_stop_id; ///< The natural stop id when extended_thread_list was last updated QueueList m_queue_list; ///< The list of libdispatch queues at a given stop point uint32_t m_queue_list_stop_id; ///< The natural stop id when queue list was last fetched Index: include/lldb/Target/ProcessThreadList.h =================================================================== --- include/lldb/Target/ProcessThreadList.h +++ include/lldb/Target/ProcessThreadList.h @@ -0,0 +1,155 @@ +//===-- ProcessThreadList.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ProcessThreadList_h_ +#define liblldb_ProcessThreadList_h_ + +#include + +#include "lldb/lldb-private.h" +#include "lldb/Core/UserID.h" +#include "lldb/Utility/Iterable.h" +#include "lldb/Target/ThreadList.h" + +namespace lldb_private { + +class ProcessThreadList : public ThreadList +{ + friend class Process; + +public: + + ProcessThreadList (Process *process); + + ProcessThreadList (const ProcessThreadList &rhs); + + ~ProcessThreadList (); + + const ProcessThreadList& + operator = (const ProcessThreadList& rhs); + + uint32_t + GetSize(bool can_update = true); + + // Return the selected thread if there is one. Otherwise, return the thread + // selected at index 0. + lldb::ThreadSP + GetSelectedThread (); + + bool + SetSelectedThreadByID (lldb::tid_t tid, bool notify = false); + + bool + SetSelectedThreadByIndexID (uint32_t index_id, bool notify = false); + + void + Clear(); + + void + Flush(); + + void + Destroy(); + + // Note that "idx" is not the same as the "thread_index". It is a zero + // based index to accessing the current threads, whereas "thread_index" + // is a unique index assigned + lldb::ThreadSP + GetThreadAtIndex (uint32_t idx, bool can_update = true); + + lldb::ThreadSP + FindThreadByID (lldb::tid_t tid, bool can_update = true); + + lldb::ThreadSP + FindThreadByProtocolID (lldb::tid_t tid, bool can_update = true); + + lldb::ThreadSP + RemoveThreadByID (lldb::tid_t tid, bool can_update = true); + + lldb::ThreadSP + RemoveThreadByProtocolID (lldb::tid_t tid, bool can_update = true); + + lldb::ThreadSP + FindThreadByIndexID (uint32_t index_id, bool can_update = true); + + lldb::ThreadSP + GetThreadSPForThreadPtr (Thread *thread_ptr); + + bool + ShouldStop (Event *event_ptr); + + Vote + ShouldReportStop (Event *event_ptr); + + Vote + ShouldReportRun (Event *event_ptr); + + void + RefreshStateAfterStop (); + + //------------------------------------------------------------------ + /// The thread list asks tells all the threads it is about to resume. + /// If a thread can "resume" without having to resume the target, it + /// will return false for WillResume, and then the process will not be + /// restarted. + /// + /// @return + /// \b true instructs the process to resume normally, + /// \b false means start & stopped events will be generated, but + /// the process will not actually run. The thread must then return + /// the correct StopInfo when asked. + /// + //------------------------------------------------------------------ + bool + WillResume (); + + void + DidResume (); + + void + DidStop (); + + void + DiscardThreadPlans(); + + uint32_t + GetStopID () const; + + void + SetStopID (uint32_t stop_id); + + Mutex & + GetMutex (); + + void + Update (ProcessThreadList &rhs); + + +protected: + + void + SetShouldReportStop (Vote vote); + + void + NotifySelectedThreadChanged (lldb::tid_t tid); + + //------------------------------------------------------------------ + // Classes that inherit from Process can see and modify these + //------------------------------------------------------------------ + Process *m_process; ///< The process that manages this thread list. + uint32_t m_stop_id; ///< The process stop ID that this thread list is valid for. + lldb::tid_t m_selected_tid; ///< For targets that need the notion of a current thread. + +private: + ProcessThreadList (); +}; + +} // namespace lldb_private + +#endif // liblldb_ProcessThreadList_h_ Index: include/lldb/Target/Thread.h =================================================================== --- include/lldb/Target/Thread.h +++ include/lldb/Target/Thread.h @@ -1235,7 +1235,7 @@ protected: friend class ThreadPlan; - friend class ThreadList; + friend class ProcessThreadList; friend class ThreadEventData; friend class StackFrameList; friend class StackFrame; Index: include/lldb/Target/ThreadList.h =================================================================== --- include/lldb/Target/ThreadList.h +++ include/lldb/Target/ThreadList.h @@ -17,156 +17,48 @@ #include "lldb/Utility/Iterable.h" -// FIXME: Currently this is a thread list with lots of functionality for use only by -// the process for which this is the thread list. If we ever want a container class -// to hand out that is just a random subset of threads, with iterator functionality, -// then we should make that part a base class, and make a ProcessThreadList for the -// process. namespace lldb_private { class ThreadList { -friend class Process; - public: - - ThreadList (Process *process); - - ThreadList (const ThreadList &rhs); - - ~ThreadList (); - - const ThreadList& - operator = (const ThreadList& rhs); - + typedef std::vector collection; + typedef LockingAdaptedIterable ThreadIterable; + + ThreadList(); + + ThreadList(collection threads); + uint32_t - GetSize(bool can_update = true); + GetSize(); void AddThread (const lldb::ThreadSP &thread_sp); void InsertThread (const lldb::ThreadSP &thread_sp, uint32_t idx); - // Return the selected thread if there is one. Otherwise, return the thread - // selected at index 0. - lldb::ThreadSP - GetSelectedThread (); - bool - SetSelectedThreadByID (lldb::tid_t tid, bool notify = false); - - bool - SetSelectedThreadByIndexID (uint32_t index_id, bool notify = false); - - void - Clear(); - - void - Flush(); - - void - Destroy(); - // Note that "idx" is not the same as the "thread_index". It is a zero // based index to accessing the current threads, whereas "thread_index" // is a unique index assigned lldb::ThreadSP - GetThreadAtIndex (uint32_t idx, bool can_update = true); + GetThreadAtIndex (uint32_t idx); - typedef std::vector collection; - typedef LockingAdaptedIterable ThreadIterable; - ThreadIterable Threads () { return ThreadIterable(m_threads, GetMutex()); } - - lldb::ThreadSP - FindThreadByID (lldb::tid_t tid, bool can_update = true); - lldb::ThreadSP - FindThreadByProtocolID (lldb::tid_t tid, bool can_update = true); + Mutex & + GetMutex() + { + return m_mutex; + } - lldb::ThreadSP - RemoveThreadByID (lldb::tid_t tid, bool can_update = true); - - lldb::ThreadSP - RemoveThreadByProtocolID (lldb::tid_t tid, bool can_update = true); - - lldb::ThreadSP - FindThreadByIndexID (uint32_t index_id, bool can_update = true); - - lldb::ThreadSP - GetThreadSPForThreadPtr (Thread *thread_ptr); - - bool - ShouldStop (Event *event_ptr); - - Vote - ShouldReportStop (Event *event_ptr); - - Vote - ShouldReportRun (Event *event_ptr); - - void - RefreshStateAfterStop (); - - //------------------------------------------------------------------ - /// The thread list asks tells all the threads it is about to resume. - /// If a thread can "resume" without having to resume the target, it - /// will return false for WillResume, and then the process will not be - /// restarted. - /// - /// @return - /// \b true instructs the process to resume normally, - /// \b false means start & stopped events will be generated, but - /// the process will not actually run. The thread must then return - /// the correct StopInfo when asked. - /// - //------------------------------------------------------------------ - bool - WillResume (); - - void - DidResume (); - - void - DidStop (); - - void - DiscardThreadPlans(); - - uint32_t - GetStopID () const; - - void - SetStopID (uint32_t stop_id); - - Mutex & - GetMutex (); - - void - Update (ThreadList &rhs); - protected: - - void - SetShouldReportStop (Vote vote); - - void - NotifySelectedThreadChanged (lldb::tid_t tid); - - //------------------------------------------------------------------ - // Classes that inherit from Process can see and modify these - //------------------------------------------------------------------ - Process *m_process; ///< The process that manages this thread list. - uint32_t m_stop_id; ///< The process stop ID that this thread list is valid for. collection m_threads; ///< The threads for this process. - lldb::tid_t m_selected_tid; ///< For targets that need the notion of a current thread. - -private: - ThreadList (); + Mutex m_mutex; }; } // namespace lldb_private Index: include/lldb/lldb-forward.h =================================================================== --- include/lldb/lldb-forward.h +++ include/lldb/lldb-forward.h @@ -160,6 +160,7 @@ class ProcessInstanceInfoList; class ProcessInstanceInfoMatch; class ProcessLaunchInfo; +class ProcessThreadList; class Property; struct PropertyDefinition; class PythonArray; @@ -381,6 +382,7 @@ typedef std::weak_ptr TargetWP; typedef std::shared_ptr ThreadSP; typedef std::weak_ptr ThreadWP; + typedef std::shared_ptr ThreadListSP; typedef std::shared_ptr ThreadPlanSP; typedef std::shared_ptr ThreadPlanTracerSP; typedef std::shared_ptr TypeSP; Index: lldb.xcodeproj/project.pbxproj =================================================================== --- lldb.xcodeproj/project.pbxproj +++ lldb.xcodeproj/project.pbxproj @@ -620,6 +620,7 @@ 4CF52AF8142829390051E832 /* SBFileSpecList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CF52AF7142829390051E832 /* SBFileSpecList.cpp */; }; 8C2D6A53197A1EAF006989C9 /* MemoryHistory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C2D6A52197A1EAF006989C9 /* MemoryHistory.cpp */; }; 8C2D6A5E197A250F006989C9 /* MemoryHistoryASan.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C2D6A5A197A1FDC006989C9 /* MemoryHistoryASan.cpp */; }; + 8C395A2D19B91D37001C9B07 /* ProcessThreadList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C395A2B19B91C8F001C9B07 /* ProcessThreadList.cpp */; }; 94094C6B163B6F840083A547 /* ValueObjectCast.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94094C69163B6CD90083A547 /* ValueObjectCast.cpp */; }; 94145431175E63B500284436 /* lldb-versioning.h in Headers */ = {isa = PBXBuildFile; fileRef = 94145430175D7FDE00284436 /* lldb-versioning.h */; settings = {ATTRIBUTES = (Public, ); }; }; 941BCC7F14E48C4000BB969C /* SBTypeFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 9461568614E355F2003A195C /* SBTypeFilter.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -1900,6 +1903,8 @@ 8C2D6A54197A1EBE006989C9 /* MemoryHistory.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = MemoryHistory.h; path = include/lldb/Target/MemoryHistory.h; sourceTree = ""; }; 8C2D6A5A197A1FDC006989C9 /* MemoryHistoryASan.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MemoryHistoryASan.cpp; sourceTree = ""; }; 8C2D6A5B197A1FDC006989C9 /* MemoryHistoryASan.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MemoryHistoryASan.h; sourceTree = ""; }; + 8C395A2B19B91C8F001C9B07 /* ProcessThreadList.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = ProcessThreadList.cpp; path = source/Target/ProcessThreadList.cpp; sourceTree = ""; }; + 8C395A2C19B91C8F001C9B07 /* ProcessThreadList.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ProcessThreadList.h; path = include/lldb/Target/ProcessThreadList.h; sourceTree = ""; }; 94005E0313F438DF001EF42D /* python-wrapper.swig */ = {isa = PBXFileReference; lastKnownFileType = text; path = "python-wrapper.swig"; sourceTree = ""; }; 94005E0513F45A1B001EF42D /* embedded_interpreter.py */ = {isa = PBXFileReference; lastKnownFileType = text.script.python; name = embedded_interpreter.py; path = source/Interpreter/embedded_interpreter.py; sourceTree = ""; }; 94031A9F13CF5B3D00DCFF3C /* PriorityPointerPair.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = PriorityPointerPair.h; path = include/lldb/Utility/PriorityPointerPair.h; sourceTree = ""; }; @@ -3681,6 +3692,8 @@ 233B007B1960C9E60090E598 /* ProcessInfo.cpp */, 233B007E1960CB280090E598 /* ProcessLaunchInfo.cpp */, 233B007919609DB40090E598 /* ProcessLaunchInfo.h */, + 8C395A2C19B91C8F001C9B07 /* ProcessThreadList.h */, + 8C395A2B19B91C8F001C9B07 /* ProcessThreadList.cpp */, 26BC7DF310F1B81A00F91463 /* Process.h */, 26BC7F3610F1B90C00F91463 /* Process.cpp */, 260A63111860FDB600FECF8E /* Queue.h */, @@ -4867,6 +4882,7 @@ 2689002713353DDE00698AC0 /* CommandObjectTarget.cpp in Sources */, 2689002813353DDE00698AC0 /* CommandObjectThread.cpp in Sources */, 2689002913353DDE00698AC0 /* CommandObjectVersion.cpp in Sources */, + 8C395A2D19B91D37001C9B07 /* ProcessThreadList.cpp in Sources */, 2689002A13353E0400698AC0 /* Address.cpp in Sources */, 2689002B13353E0400698AC0 /* AddressRange.cpp in Sources */, 236124A41986B4E2004EFC37 /* IOObject.cpp in Sources */, Index: source/Commands/CommandObjectTarget.cpp =================================================================== --- source/Commands/CommandObjectTarget.cpp +++ source/Commands/CommandObjectTarget.cpp @@ -3648,7 +3648,7 @@ return false; } - ThreadList threads(process->GetThreadList()); + ProcessThreadList threads(process->GetThreadList()); if (threads.GetSize() == 0) { result.AppendError ("The process must be paused to use this command."); Index: source/Core/Debugger.cpp =================================================================== --- source/Core/Debugger.cpp +++ source/Core/Debugger.cpp @@ -3053,7 +3053,7 @@ { // Lock the thread list so it doesn't change on us, this is the scope for the locker: { - ThreadList &thread_list = process_sp->GetThreadList(); + ProcessThreadList &thread_list = process_sp->GetThreadList(); Mutex::Locker locker (thread_list.GetMutex()); ThreadSP curr_thread (thread_list.GetSelectedThread()); Index: source/Core/IOHandler.cpp =================================================================== --- source/Core/IOHandler.cpp +++ source/Core/IOHandler.cpp @@ -3232,7 +3232,7 @@ ThreadSP thread_sp = GetThread (item); if (thread_sp) { - ThreadList &thread_list = thread_sp->GetProcess()->GetThreadList(); + ProcessThreadList &thread_list = thread_sp->GetProcess()->GetThreadList(); Mutex::Locker locker (thread_list.GetMutex()); ThreadSP selected_thread_sp = thread_list.GetSelectedThread(); if (selected_thread_sp->GetID() != thread_sp->GetID()) @@ -3315,7 +3315,7 @@ } TreeItem t (&item, *m_thread_delegate_sp, false); - ThreadList &threads = process_sp->GetThreadList(); + ProcessThreadList &threads = process_sp->GetThreadList(); Mutex::Locker locker (threads.GetMutex()); size_t num_threads = threads.GetSize(); item.Resize (num_threads, t); @@ -4333,7 +4333,7 @@ else if (submenus.size() > 8) submenus.erase (submenus.begin() + 8, submenus.end()); - ThreadList &threads = process->GetThreadList(); + ProcessThreadList &threads = process->GetThreadList(); Mutex::Locker locker (threads.GetMutex()); size_t num_threads = threads.GetSize(); for (size_t i=0; iGetThreadList(); + ProcessThreadList &thread_list = process_sp->GetThreadList(); const uint32_t num_threads = thread_list.GetSize(); // Make an array of LC_THREAD data items. Each one contains Index: source/Plugins/OperatingSystem/Python/OperatingSystemPython.h =================================================================== --- source/Plugins/OperatingSystem/Python/OperatingSystemPython.h +++ source/Plugins/OperatingSystem/Python/OperatingSystemPython.h @@ -62,9 +62,9 @@ // lldb_private::OperatingSystem Methods //------------------------------------------------------------------ virtual bool - UpdateThreadList (lldb_private::ThreadList &old_thread_list, - lldb_private::ThreadList &real_thread_list, - lldb_private::ThreadList &new_thread_list); + UpdateThreadList (lldb_private::ProcessThreadList &old_thread_list, + lldb_private::ProcessThreadList &real_thread_list, + lldb_private::ProcessThreadList &new_thread_list); virtual void ThreadWasSelected (lldb_private::Thread *thread); @@ -91,8 +91,8 @@ lldb::ThreadSP CreateThreadFromThreadInfo (lldb_private::PythonDictionary &thread_dict, - lldb_private::ThreadList &core_thread_list, - lldb_private::ThreadList &old_thread_list, + lldb_private::ProcessThreadList &core_thread_list, + lldb_private::ProcessThreadList &old_thread_list, std::vector &core_used_map, bool *did_create_ptr); Index: source/Plugins/OperatingSystem/Python/OperatingSystemPython.cpp =================================================================== --- source/Plugins/OperatingSystem/Python/OperatingSystemPython.cpp +++ source/Plugins/OperatingSystem/Python/OperatingSystemPython.cpp @@ -167,9 +167,9 @@ } bool -OperatingSystemPython::UpdateThreadList (ThreadList &old_thread_list, - ThreadList &core_thread_list, - ThreadList &new_thread_list) +OperatingSystemPython::UpdateThreadList (ProcessThreadList &old_thread_list, + ProcessThreadList &core_thread_list, + ProcessThreadList &new_thread_list) { if (!m_interpreter || !m_python_object_sp) return false; @@ -240,8 +240,8 @@ ThreadSP OperatingSystemPython::CreateThreadFromThreadInfo (PythonDictionary &thread_dict, - ThreadList &core_thread_list, - ThreadList &old_thread_list, + ProcessThreadList &core_thread_list, + ProcessThreadList &old_thread_list, std::vector &core_used_map, bool *did_create_ptr) { @@ -421,8 +421,8 @@ std::vector core_used_map; if (thread_info_dict) { - ThreadList core_threads(m_process); - ThreadList &thread_list = m_process->GetThreadList(); + ProcessThreadList core_threads(m_process); + ProcessThreadList &thread_list = m_process->GetThreadList(); bool did_create = false; ThreadSP thread_sp (CreateThreadFromThreadInfo (thread_info_dict, core_threads, thread_list, core_used_map, &did_create)); if (did_create) Index: source/Plugins/Process/MacOSX-Kernel/ProcessKDP.h =================================================================== --- source/Plugins/Process/MacOSX-Kernel/ProcessKDP.h +++ source/Plugins/Process/MacOSX-Kernel/ProcessKDP.h @@ -229,8 +229,8 @@ Clear ( ); virtual bool - UpdateThreadList (lldb_private::ThreadList &old_thread_list, - lldb_private::ThreadList &new_thread_list); + UpdateThreadList (lldb_private::ProcessThreadList &old_thread_list, + lldb_private::ProcessThreadList &new_thread_list); enum { Index: source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp =================================================================== --- source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp +++ source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp @@ -575,7 +575,7 @@ bool -ProcessKDP::UpdateThreadList (ThreadList &old_thread_list, ThreadList &new_thread_list) +ProcessKDP::UpdateThreadList (ProcessThreadList &old_thread_list, ProcessThreadList &new_thread_list) { // locker will keep a mutex locked until it goes out of scope Log *log (ProcessKDPLog::GetLogIfAllCategoriesSet (KDP_LOG_THREAD)); Index: source/Plugins/Process/elf-core/ProcessElfCore.h =================================================================== --- source/Plugins/Process/elf-core/ProcessElfCore.h +++ source/Plugins/Process/elf-core/ProcessElfCore.h @@ -127,8 +127,8 @@ Clear ( ); virtual bool - UpdateThreadList (lldb_private::ThreadList &old_thread_list, - lldb_private::ThreadList &new_thread_list); + UpdateThreadList (lldb_private::ProcessThreadList &old_thread_list, + lldb_private::ProcessThreadList &new_thread_list); private: //------------------------------------------------------------------ Index: source/Plugins/Process/elf-core/ProcessElfCore.cpp =================================================================== --- source/Plugins/Process/elf-core/ProcessElfCore.cpp +++ source/Plugins/Process/elf-core/ProcessElfCore.cpp @@ -264,7 +264,7 @@ } bool -ProcessElfCore::UpdateThreadList (ThreadList &old_thread_list, ThreadList &new_thread_list) +ProcessElfCore::UpdateThreadList (ProcessThreadList &old_thread_list, ProcessThreadList &new_thread_list) { const uint32_t num_threads = GetNumThreadContexts (); if (!m_thread_data_valid) Index: source/Plugins/Process/gdb-remote/ProcessGDBRemote.h =================================================================== --- source/Plugins/Process/gdb-remote/ProcessGDBRemote.h +++ source/Plugins/Process/gdb-remote/ProcessGDBRemote.h @@ -285,8 +285,8 @@ } virtual bool - UpdateThreadList (lldb_private::ThreadList &old_thread_list, - lldb_private::ThreadList &new_thread_list); + UpdateThreadList (lldb_private::ProcessThreadList &old_thread_list, + lldb_private::ProcessThreadList &new_thread_list); lldb_private::Error LaunchAndConnectToDebugserver (const lldb_private::ProcessInfo &process_info); Index: source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp =================================================================== --- source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -1482,7 +1482,7 @@ } bool -ProcessGDBRemote::UpdateThreadList (ThreadList &old_thread_list, ThreadList &new_thread_list) +ProcessGDBRemote::UpdateThreadList (ProcessThreadList &old_thread_list, ProcessThreadList &new_thread_list) { // locker will keep a mutex locked until it goes out of scope Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_THREAD)); @@ -1499,7 +1499,7 @@ num_thread_ids = m_thread_ids.size(); } - ThreadList old_thread_list_copy(old_thread_list); + ProcessThreadList old_thread_list_copy(old_thread_list); if (num_thread_ids > 0) { for (size_t i=0; iUpdateThreadList(...) so it doesn't change on us - ThreadList &old_thread_list = m_thread_list; - ThreadList real_thread_list(this); - ThreadList new_thread_list(this); + ProcessThreadList &old_thread_list = m_thread_list; + ProcessThreadList real_thread_list(this); + ProcessThreadList new_thread_list(this); // Always update the thread list with the protocol specific // thread list, but only update if "true" is returned if (UpdateThreadList (m_thread_list_real, real_thread_list)) @@ -4244,7 +4245,7 @@ // If we're stopped and haven't restarted, then do the StopInfo actions here: if (m_state == eStateStopped && ! m_restarted) { - ThreadList &curr_thread_list = m_process_sp->GetThreadList(); + ProcessThreadList &curr_thread_list = m_process_sp->GetThreadList(); uint32_t num_threads = curr_thread_list.GetSize(); uint32_t idx; @@ -5637,7 +5638,7 @@ break; } - ThreadList &thread_list = process->GetThreadList(); + ProcessThreadList &thread_list = process->GetThreadList(); uint32_t num_threads = thread_list.GetSize(); uint32_t thread_index; @@ -5861,7 +5862,7 @@ //Scope for thread list locker; { Mutex::Locker locker (GetThreadList().GetMutex()); - ThreadList &curr_thread_list = GetThreadList(); + ProcessThreadList &curr_thread_list = GetThreadList(); num_threads = curr_thread_list.GetSize(); uint32_t idx; thread_index_array.resize(num_threads); Index: source/Target/ProcessThreadList.cpp =================================================================== --- source/Target/ProcessThreadList.cpp +++ source/Target/ProcessThreadList.cpp @@ -0,0 +1,800 @@ +//===-- ProcessThreadList.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include + +#include + +#include "lldb/Core/Log.h" +#include "lldb/Core/State.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/ProcessThreadList.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlan.h" +#include "lldb/Target/Process.h" + +using namespace lldb; +using namespace lldb_private; + +ProcessThreadList::ProcessThreadList (Process *process) : +ThreadList (), +m_process (process), +m_stop_id (0), +m_selected_tid (LLDB_INVALID_THREAD_ID) +{ +} + +ProcessThreadList::ProcessThreadList (const ProcessThreadList &rhs) : +ThreadList (), +m_process (rhs.m_process), +m_stop_id (rhs.m_stop_id), +m_selected_tid () +{ + // Use the assignment operator since it uses the mutex + *this = rhs; +} + +const ProcessThreadList& +ProcessThreadList::operator = (const ProcessThreadList& rhs) +{ + if (this != &rhs) + { + // Lock both mutexes to make sure neither side changes anyone on us + // while the assignement occurs + Mutex::Locker locker(GetMutex()); + m_process = rhs.m_process; + m_stop_id = rhs.m_stop_id; + m_threads = rhs.m_threads; + m_selected_tid = rhs.m_selected_tid; + } + return *this; +} + + +ProcessThreadList::~ProcessThreadList() +{ + // Clear the thread list. Clear will take the mutex lock + // which will ensure that if anyone is using the list + // they won't get it removed while using it. + Clear(); +} + + +uint32_t +ProcessThreadList::GetStopID () const +{ + return m_stop_id; +} + +void +ProcessThreadList::SetStopID (uint32_t stop_id) +{ + m_stop_id = stop_id; +} + +uint32_t +ProcessThreadList::GetSize (bool can_update) +{ + Mutex::Locker locker(GetMutex()); + if (can_update) + m_process->UpdateThreadListIfNeeded(); + return m_threads.size(); +} + +ThreadSP +ProcessThreadList::GetThreadAtIndex (uint32_t idx, bool can_update) +{ + Mutex::Locker locker(GetMutex()); + if (can_update) + m_process->UpdateThreadListIfNeeded(); + + ThreadSP thread_sp; + if (idx < m_threads.size()) + thread_sp = m_threads[idx]; + return thread_sp; +} + +ThreadSP +ProcessThreadList::FindThreadByID (lldb::tid_t tid, bool can_update) +{ + Mutex::Locker locker(GetMutex()); + + if (can_update) + m_process->UpdateThreadListIfNeeded(); + + ThreadSP thread_sp; + uint32_t idx = 0; + const uint32_t num_threads = m_threads.size(); + for (idx = 0; idx < num_threads; ++idx) + { + if (m_threads[idx]->GetID() == tid) + { + thread_sp = m_threads[idx]; + break; + } + } + return thread_sp; +} + +ThreadSP +ProcessThreadList::FindThreadByProtocolID (lldb::tid_t tid, bool can_update) +{ + Mutex::Locker locker(GetMutex()); + + if (can_update) + m_process->UpdateThreadListIfNeeded(); + + ThreadSP thread_sp; + uint32_t idx = 0; + const uint32_t num_threads = m_threads.size(); + for (idx = 0; idx < num_threads; ++idx) + { + if (m_threads[idx]->GetProtocolID() == tid) + { + thread_sp = m_threads[idx]; + break; + } + } + return thread_sp; +} + + +ThreadSP +ProcessThreadList::RemoveThreadByID (lldb::tid_t tid, bool can_update) +{ + Mutex::Locker locker(GetMutex()); + + if (can_update) + m_process->UpdateThreadListIfNeeded(); + + ThreadSP thread_sp; + uint32_t idx = 0; + const uint32_t num_threads = m_threads.size(); + for (idx = 0; idx < num_threads; ++idx) + { + if (m_threads[idx]->GetID() == tid) + { + thread_sp = m_threads[idx]; + m_threads.erase(m_threads.begin()+idx); + break; + } + } + return thread_sp; +} + +ThreadSP +ProcessThreadList::RemoveThreadByProtocolID (lldb::tid_t tid, bool can_update) +{ + Mutex::Locker locker(GetMutex()); + + if (can_update) + m_process->UpdateThreadListIfNeeded(); + + ThreadSP thread_sp; + uint32_t idx = 0; + const uint32_t num_threads = m_threads.size(); + for (idx = 0; idx < num_threads; ++idx) + { + if (m_threads[idx]->GetProtocolID() == tid) + { + thread_sp = m_threads[idx]; + m_threads.erase(m_threads.begin()+idx); + break; + } + } + return thread_sp; +} + +ThreadSP +ProcessThreadList::GetThreadSPForThreadPtr (Thread *thread_ptr) +{ + ThreadSP thread_sp; + if (thread_ptr) + { + Mutex::Locker locker(GetMutex()); + + uint32_t idx = 0; + const uint32_t num_threads = m_threads.size(); + for (idx = 0; idx < num_threads; ++idx) + { + if (m_threads[idx].get() == thread_ptr) + { + thread_sp = m_threads[idx]; + break; + } + } + } + return thread_sp; +} + + + +ThreadSP +ProcessThreadList::FindThreadByIndexID (uint32_t index_id, bool can_update) +{ + Mutex::Locker locker(GetMutex()); + + if (can_update) + m_process->UpdateThreadListIfNeeded(); + + ThreadSP thread_sp; + const uint32_t num_threads = m_threads.size(); + for (uint32_t idx = 0; idx < num_threads; ++idx) + { + if (m_threads[idx]->GetIndexID() == index_id) + { + thread_sp = m_threads[idx]; + break; + } + } + return thread_sp; +} + +bool +ProcessThreadList::ShouldStop (Event *event_ptr) +{ + // Running events should never stop, obviously... + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); + + // The ShouldStop method of the threads can do a whole lot of work, + // figuring out whether the thread plan conditions are met. So we don't want + // to keep the ThreadList locked the whole time we are doing this. + // FIXME: It is possible that running code could cause new threads + // to be created. If that happens we will miss asking them whether + // then should stop. This is not a big deal, since we haven't had + // a chance to hang any interesting operations on those threads yet. + + collection threads_copy; + { + // Scope for locker + Mutex::Locker locker(GetMutex()); + + m_process->UpdateThreadListIfNeeded(); + threads_copy = m_threads; + } + + collection::iterator pos, end = threads_copy.end(); + + if (log) + { + log->PutCString(""); + log->Printf ("ThreadList::%s: %" PRIu64 " threads", __FUNCTION__, (uint64_t)m_threads.size()); + } + + bool did_anybody_stop_for_a_reason = false; + + // If the event is an Interrupt event, then we're going to stop no matter what. Otherwise, presume we won't stop. + bool should_stop = false; + if (Process::ProcessEventData::GetInterruptedFromEvent(event_ptr)) + { + if (log) + log->Printf("ThreadList::%s handling interrupt event, should stop set to true", __FUNCTION__); + + should_stop = true; + } + + // Now we run through all the threads and get their stop info's. We want to make sure to do this first before + // we start running the ShouldStop, because one thread's ShouldStop could destroy information (like deleting a + // thread specific breakpoint another thread had stopped at) which could lead us to compute the StopInfo incorrectly. + // We don't need to use it here, we just want to make sure it gets computed. + + for (pos = threads_copy.begin(); pos != end; ++pos) + { + ThreadSP thread_sp(*pos); + thread_sp->GetStopInfo(); + } + + for (pos = threads_copy.begin(); pos != end; ++pos) + { + ThreadSP thread_sp(*pos); + + // We should never get a stop for which no thread had a stop reason, but sometimes we do see this - + // for instance when we first connect to a remote stub. In that case we should stop, since we can't figure out + // the right thing to do and stopping gives the user control over what to do in this instance. + // + // Note, this causes a problem when you have a thread specific breakpoint, and a bunch of threads hit the breakpoint, + // but not the thread which we are waiting for. All the threads that are not "supposed" to hit the breakpoint + // are marked as having no stop reason, which is right, they should not show a stop reason. But that triggers this + // code and causes us to stop seemingly for no reason. + // + // Since the only way we ever saw this error was on first attach, I'm only going to trigger set did_anybody_stop_for_a_reason + // to true unless this is the first stop. + // + // If this becomes a problem, we'll have to have another StopReason like "StopInfoHidden" which will look invalid + // everywhere but at this check. + + if (thread_sp->GetProcess()->GetStopID() > 1) + did_anybody_stop_for_a_reason = true; + else + did_anybody_stop_for_a_reason |= thread_sp->ThreadStoppedForAReason(); + + const bool thread_should_stop = thread_sp->ShouldStop(event_ptr); + if (thread_should_stop) + should_stop |= true; + } + + if (!should_stop && !did_anybody_stop_for_a_reason) + { + should_stop = true; + if (log) + log->Printf ("ThreadList::%s we stopped but no threads had a stop reason, overriding should_stop and stopping.", __FUNCTION__); + } + + if (log) + log->Printf ("ThreadList::%s overall should_stop = %i", __FUNCTION__, should_stop); + + if (should_stop) + { + for (pos = threads_copy.begin(); pos != end; ++pos) + { + ThreadSP thread_sp(*pos); + thread_sp->WillStop (); + } + } + + return should_stop; +} + +Vote +ProcessThreadList::ShouldReportStop (Event *event_ptr) +{ + Mutex::Locker locker(GetMutex()); + + Vote result = eVoteNoOpinion; + m_process->UpdateThreadListIfNeeded(); + collection::iterator pos, end = m_threads.end(); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); + + if (log) + log->Printf ("ThreadList::%s %" PRIu64 " threads", __FUNCTION__, (uint64_t)m_threads.size()); + + // Run through the threads and ask whether we should report this event. + // For stopping, a YES vote wins over everything. A NO vote wins over NO opinion. + for (pos = m_threads.begin(); pos != end; ++pos) + { + ThreadSP thread_sp(*pos); + const Vote vote = thread_sp->ShouldReportStop (event_ptr); + switch (vote) + { + case eVoteNoOpinion: + continue; + + case eVoteYes: + result = eVoteYes; + break; + + case eVoteNo: + if (result == eVoteNoOpinion) + { + result = eVoteNo; + } + else + { + if (log) + log->Printf ("ThreadList::%s thread 0x%4.4" PRIx64 ": voted %s, but lost out because result was %s", + __FUNCTION__, + thread_sp->GetID (), + GetVoteAsCString (vote), + GetVoteAsCString (result)); + } + break; + } + } + if (log) + log->Printf ("ThreadList::%s returning %s", __FUNCTION__, GetVoteAsCString (result)); + return result; +} + +void +ProcessThreadList::SetShouldReportStop (Vote vote) +{ + Mutex::Locker locker(GetMutex()); + m_process->UpdateThreadListIfNeeded(); + collection::iterator pos, end = m_threads.end(); + for (pos = m_threads.begin(); pos != end; ++pos) + { + ThreadSP thread_sp(*pos); + thread_sp->SetShouldReportStop (vote); + } +} + +Vote +ProcessThreadList::ShouldReportRun (Event *event_ptr) +{ + + Mutex::Locker locker(GetMutex()); + + Vote result = eVoteNoOpinion; + m_process->UpdateThreadListIfNeeded(); + collection::iterator pos, end = m_threads.end(); + + // Run through the threads and ask whether we should report this event. + // The rule is NO vote wins over everything, a YES vote wins over no opinion. + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); + + for (pos = m_threads.begin(); pos != end; ++pos) + { + if ((*pos)->GetResumeState () != eStateSuspended) + { + switch ((*pos)->ShouldReportRun (event_ptr)) + { + case eVoteNoOpinion: + continue; + case eVoteYes: + if (result == eVoteNoOpinion) + result = eVoteYes; + break; + case eVoteNo: + if (log) + log->Printf ("ThreadList::ShouldReportRun() thread %d (0x%4.4" PRIx64 ") says don't report.", + (*pos)->GetIndexID(), + (*pos)->GetID()); + result = eVoteNo; + break; + } + } + } + return result; +} + +void +ProcessThreadList::Clear() +{ + Mutex::Locker locker(GetMutex()); + m_stop_id = 0; + m_threads.clear(); + m_selected_tid = LLDB_INVALID_THREAD_ID; +} + +void +ProcessThreadList::Destroy() +{ + Mutex::Locker locker(GetMutex()); + const uint32_t num_threads = m_threads.size(); + for (uint32_t idx = 0; idx < num_threads; ++idx) + { + m_threads[idx]->DestroyThread(); + } +} + +void +ProcessThreadList::RefreshStateAfterStop () +{ + Mutex::Locker locker(GetMutex()); + + m_process->UpdateThreadListIfNeeded(); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); + if (log && log->GetVerbose()) + log->Printf ("Turning off notification of new threads while single stepping a thread."); + + collection::iterator pos, end = m_threads.end(); + for (pos = m_threads.begin(); pos != end; ++pos) + (*pos)->RefreshStateAfterStop (); +} + +void +ProcessThreadList::DiscardThreadPlans () +{ + // You don't need to update the thread list here, because only threads + // that you currently know about have any thread plans. + Mutex::Locker locker(GetMutex()); + + collection::iterator pos, end = m_threads.end(); + for (pos = m_threads.begin(); pos != end; ++pos) + (*pos)->DiscardThreadPlans (true); + +} + +bool +ProcessThreadList::WillResume () +{ + // Run through the threads and perform their momentary actions. + // But we only do this for threads that are running, user suspended + // threads stay where they are. + + Mutex::Locker locker(GetMutex()); + m_process->UpdateThreadListIfNeeded(); + + collection::iterator pos, end = m_threads.end(); + + // See if any thread wants to run stopping others. If it does, then we won't + // setup the other threads for resume, since they aren't going to get a chance + // to run. This is necessary because the SetupForResume might add "StopOthers" + // plans which would then get to be part of the who-gets-to-run negotiation, but + // they're coming in after the fact, and the threads that are already set up should + // take priority. + + bool wants_solo_run = false; + + for (pos = m_threads.begin(); pos != end; ++pos) + { + if ((*pos)->GetResumeState() != eStateSuspended && + (*pos)->GetCurrentPlan()->StopOthers()) + { + if ((*pos)->IsOperatingSystemPluginThread() && !(*pos)->GetBackingThread()) + continue; + wants_solo_run = true; + break; + } + } + + if (wants_solo_run) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); + if (log && log->GetVerbose()) + log->Printf ("Turning on notification of new threads while single stepping a thread."); + m_process->StartNoticingNewThreads(); + } + else + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); + if (log && log->GetVerbose()) + log->Printf ("Turning off notification of new threads while single stepping a thread."); + m_process->StopNoticingNewThreads(); + } + + // Give all the threads that are likely to run a last chance to set up their state before we + // negotiate who is actually going to get a chance to run... + // Don't set to resume suspended threads, and if any thread wanted to stop others, only + // call setup on the threads that request StopOthers... + + for (pos = m_threads.begin(); pos != end; ++pos) + { + if ((*pos)->GetResumeState() != eStateSuspended + && (!wants_solo_run || (*pos)->GetCurrentPlan()->StopOthers())) + { + if ((*pos)->IsOperatingSystemPluginThread() && !(*pos)->GetBackingThread()) + continue; + (*pos)->SetupForResume (); + } + } + + // Now go through the threads and see if any thread wants to run just itself. + // if so then pick one and run it. + + ProcessThreadList run_me_only_list (m_process); + + run_me_only_list.SetStopID(m_process->GetStopID()); + + bool run_only_current_thread = false; + + for (pos = m_threads.begin(); pos != end; ++pos) + { + ThreadSP thread_sp(*pos); + if (thread_sp->GetResumeState() != eStateSuspended && + thread_sp->GetCurrentPlan()->StopOthers()) + { + if ((*pos)->IsOperatingSystemPluginThread() && !(*pos)->GetBackingThread()) + continue; + + // You can't say "stop others" and also want yourself to be suspended. + assert (thread_sp->GetCurrentPlan()->RunState() != eStateSuspended); + + if (thread_sp == GetSelectedThread()) + { + run_only_current_thread = true; + run_me_only_list.Clear(); + run_me_only_list.AddThread (thread_sp); + break; + } + + run_me_only_list.AddThread (thread_sp); + } + + } + + bool need_to_resume = true; + + if (run_me_only_list.GetSize (false) == 0) + { + // Everybody runs as they wish: + for (pos = m_threads.begin(); pos != end; ++pos) + { + ThreadSP thread_sp(*pos); + StateType run_state; + if (thread_sp->GetResumeState() != eStateSuspended) + run_state = thread_sp->GetCurrentPlan()->RunState(); + else + run_state = eStateSuspended; + if (!thread_sp->ShouldResume(run_state)) + need_to_resume = false; + } + } + else + { + ThreadSP thread_to_run; + + if (run_only_current_thread) + { + thread_to_run = GetSelectedThread(); + } + else if (run_me_only_list.GetSize (false) == 1) + { + thread_to_run = run_me_only_list.GetThreadAtIndex (0); + } + else + { + int random_thread = (int) + ((run_me_only_list.GetSize (false) * (double) rand ()) / (RAND_MAX + 1.0)); + thread_to_run = run_me_only_list.GetThreadAtIndex (random_thread); + } + + for (pos = m_threads.begin(); pos != end; ++pos) + { + ThreadSP thread_sp(*pos); + if (thread_sp == thread_to_run) + { + if (!thread_sp->ShouldResume(thread_sp->GetCurrentPlan()->RunState())) + need_to_resume = false; + } + else + thread_sp->ShouldResume (eStateSuspended); + } + } + + return need_to_resume; +} + +void +ProcessThreadList::DidResume () +{ + Mutex::Locker locker(GetMutex()); + collection::iterator pos, end = m_threads.end(); + for (pos = m_threads.begin(); pos != end; ++pos) + { + // Don't clear out threads that aren't going to get a chance to run, rather + // leave their state for the next time around. + ThreadSP thread_sp(*pos); + if (thread_sp->GetResumeState() != eStateSuspended) + thread_sp->DidResume (); + } +} + +void +ProcessThreadList::DidStop () +{ + Mutex::Locker locker(GetMutex()); + collection::iterator pos, end = m_threads.end(); + for (pos = m_threads.begin(); pos != end; ++pos) + { + // Notify threads that the process just stopped. + // Note, this currently assumes that all threads in the list + // stop when the process stops. In the future we will want to support + // a debugging model where some threads continue to run while others + // are stopped. We either need to handle that somehow here or + // create a special thread list containing only threads which will + // stop in the code that calls this method (currently + // Process::SetPrivateState). + ThreadSP thread_sp(*pos); + if (StateIsRunningState(thread_sp->GetState())) + thread_sp->DidStop (); + } +} + +ThreadSP +ProcessThreadList::GetSelectedThread () +{ + Mutex::Locker locker(GetMutex()); + ThreadSP thread_sp = FindThreadByID(m_selected_tid); + if (!thread_sp.get()) + { + if (m_threads.size() == 0) + return thread_sp; + m_selected_tid = m_threads[0]->GetID(); + thread_sp = m_threads[0]; + } + return thread_sp; +} + +bool +ProcessThreadList::SetSelectedThreadByID (lldb::tid_t tid, bool notify) +{ + Mutex::Locker locker(GetMutex()); + ThreadSP selected_thread_sp(FindThreadByID(tid)); + if (selected_thread_sp) + { + m_selected_tid = tid; + selected_thread_sp->SetDefaultFileAndLineToSelectedFrame(); + } + else + m_selected_tid = LLDB_INVALID_THREAD_ID; + + if (notify) + NotifySelectedThreadChanged(m_selected_tid); + + return m_selected_tid != LLDB_INVALID_THREAD_ID; +} + +bool +ProcessThreadList::SetSelectedThreadByIndexID (uint32_t index_id, bool notify) +{ + Mutex::Locker locker(GetMutex()); + ThreadSP selected_thread_sp (FindThreadByIndexID(index_id)); + if (selected_thread_sp.get()) + { + m_selected_tid = selected_thread_sp->GetID(); + selected_thread_sp->SetDefaultFileAndLineToSelectedFrame(); + } + else + m_selected_tid = LLDB_INVALID_THREAD_ID; + + if (notify) + NotifySelectedThreadChanged(m_selected_tid); + + return m_selected_tid != LLDB_INVALID_THREAD_ID; +} + +void +ProcessThreadList::NotifySelectedThreadChanged (lldb::tid_t tid) +{ + ThreadSP selected_thread_sp (FindThreadByID(tid)); + if (selected_thread_sp->EventTypeHasListeners(Thread::eBroadcastBitThreadSelected)) + selected_thread_sp->BroadcastEvent(Thread::eBroadcastBitThreadSelected, + new Thread::ThreadEventData(selected_thread_sp)); +} + +void +ProcessThreadList::Update (ProcessThreadList &rhs) +{ + if (this != &rhs) + { + // Lock both mutexes to make sure neither side changes anyone on us + // while the assignement occurs + Mutex::Locker locker(GetMutex()); + m_process = rhs.m_process; + m_stop_id = rhs.m_stop_id; + m_threads.swap(rhs.m_threads); + m_selected_tid = rhs.m_selected_tid; + + + // Now we look for threads that we are done with and + // make sure to clear them up as much as possible so + // anyone with a shared pointer will still have a reference, + // but the thread won't be of much use. Using std::weak_ptr + // for all backward references (such as a thread to a process) + // will eventually solve this issue for us, but for now, we + // need to work around the issue + collection::iterator rhs_pos, rhs_end = rhs.m_threads.end(); + for (rhs_pos = rhs.m_threads.begin(); rhs_pos != rhs_end; ++rhs_pos) + { + const lldb::tid_t tid = (*rhs_pos)->GetID(); + bool thread_is_alive = false; + const uint32_t num_threads = m_threads.size(); + for (uint32_t idx = 0; idx < num_threads; ++idx) + { + if (m_threads[idx]->GetID() == tid) + { + thread_is_alive = true; + break; + } + } + if (!thread_is_alive) + (*rhs_pos)->DestroyThread(); + } + } +} + +void +ProcessThreadList::Flush () +{ + Mutex::Locker locker(GetMutex()); + collection::iterator pos, end = m_threads.end(); + for (pos = m_threads.begin(); pos != end; ++pos) + (*pos)->Flush (); +} + +Mutex & +ProcessThreadList::GetMutex () +{ + return m_process->m_thread_mutex; +} + Index: source/Target/Target.cpp =================================================================== --- source/Target/Target.cpp +++ source/Target/Target.cpp @@ -2109,7 +2109,7 @@ std::vector exc_ctx_with_reasons; std::vector sym_ctx_with_reasons; - ThreadList &cur_threadlist = m_process_sp->GetThreadList(); + ProcessThreadList &cur_threadlist = m_process_sp->GetThreadList(); size_t num_threads = cur_threadlist.GetSize(); for (size_t i = 0; i < num_threads; i++) { Index: source/Target/ThreadList.cpp =================================================================== --- source/Target/ThreadList.cpp +++ source/Target/ThreadList.cpp @@ -6,79 +6,27 @@ // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// -#include -#include - -#include "lldb/Core/Log.h" -#include "lldb/Core/State.h" -#include "lldb/Target/RegisterContext.h" #include "lldb/Target/ThreadList.h" -#include "lldb/Target/Thread.h" -#include "lldb/Target/ThreadPlan.h" -#include "lldb/Target/Process.h" using namespace lldb; using namespace lldb_private; -ThreadList::ThreadList (Process *process) : - m_process (process), - m_stop_id (0), - m_threads(), - m_selected_tid (LLDB_INVALID_THREAD_ID) +ThreadList::ThreadList() : +m_threads(), +m_mutex() { + } -ThreadList::ThreadList (const ThreadList &rhs) : - m_process (rhs.m_process), - m_stop_id (rhs.m_stop_id), - m_threads (), - m_selected_tid () +ThreadList::ThreadList(collection threads) : +m_threads(threads), +m_mutex() { - // Use the assignment operator since it uses the mutex - *this = rhs; + } -const ThreadList& -ThreadList::operator = (const ThreadList& rhs) -{ - if (this != &rhs) - { - // Lock both mutexes to make sure neither side changes anyone on us - // while the assignement occurs - Mutex::Locker locker(GetMutex()); - m_process = rhs.m_process; - m_stop_id = rhs.m_stop_id; - m_threads = rhs.m_threads; - m_selected_tid = rhs.m_selected_tid; - } - return *this; -} - - -ThreadList::~ThreadList() -{ - // Clear the thread list. Clear will take the mutex lock - // which will ensure that if anyone is using the list - // they won't get it removed while using it. - Clear(); -} - - -uint32_t -ThreadList::GetStopID () const -{ - return m_stop_id; -} - void -ThreadList::SetStopID (uint32_t stop_id) -{ - m_stop_id = stop_id; -} - - -void ThreadList::AddThread (const ThreadSP &thread_sp) { Mutex::Locker locker(GetMutex()); @@ -95,725 +43,20 @@ m_threads.push_back (thread_sp); } - uint32_t -ThreadList::GetSize (bool can_update) +ThreadList::GetSize () { Mutex::Locker locker(GetMutex()); - if (can_update) - m_process->UpdateThreadListIfNeeded(); return m_threads.size(); } ThreadSP -ThreadList::GetThreadAtIndex (uint32_t idx, bool can_update) +ThreadList::GetThreadAtIndex (uint32_t idx) { Mutex::Locker locker(GetMutex()); - if (can_update) - m_process->UpdateThreadListIfNeeded(); - + ThreadSP thread_sp; if (idx < m_threads.size()) thread_sp = m_threads[idx]; return thread_sp; } - -ThreadSP -ThreadList::FindThreadByID (lldb::tid_t tid, bool can_update) -{ - Mutex::Locker locker(GetMutex()); - - if (can_update) - m_process->UpdateThreadListIfNeeded(); - - ThreadSP thread_sp; - uint32_t idx = 0; - const uint32_t num_threads = m_threads.size(); - for (idx = 0; idx < num_threads; ++idx) - { - if (m_threads[idx]->GetID() == tid) - { - thread_sp = m_threads[idx]; - break; - } - } - return thread_sp; -} - -ThreadSP -ThreadList::FindThreadByProtocolID (lldb::tid_t tid, bool can_update) -{ - Mutex::Locker locker(GetMutex()); - - if (can_update) - m_process->UpdateThreadListIfNeeded(); - - ThreadSP thread_sp; - uint32_t idx = 0; - const uint32_t num_threads = m_threads.size(); - for (idx = 0; idx < num_threads; ++idx) - { - if (m_threads[idx]->GetProtocolID() == tid) - { - thread_sp = m_threads[idx]; - break; - } - } - return thread_sp; -} - - -ThreadSP -ThreadList::RemoveThreadByID (lldb::tid_t tid, bool can_update) -{ - Mutex::Locker locker(GetMutex()); - - if (can_update) - m_process->UpdateThreadListIfNeeded(); - - ThreadSP thread_sp; - uint32_t idx = 0; - const uint32_t num_threads = m_threads.size(); - for (idx = 0; idx < num_threads; ++idx) - { - if (m_threads[idx]->GetID() == tid) - { - thread_sp = m_threads[idx]; - m_threads.erase(m_threads.begin()+idx); - break; - } - } - return thread_sp; -} - -ThreadSP -ThreadList::RemoveThreadByProtocolID (lldb::tid_t tid, bool can_update) -{ - Mutex::Locker locker(GetMutex()); - - if (can_update) - m_process->UpdateThreadListIfNeeded(); - - ThreadSP thread_sp; - uint32_t idx = 0; - const uint32_t num_threads = m_threads.size(); - for (idx = 0; idx < num_threads; ++idx) - { - if (m_threads[idx]->GetProtocolID() == tid) - { - thread_sp = m_threads[idx]; - m_threads.erase(m_threads.begin()+idx); - break; - } - } - return thread_sp; -} - -ThreadSP -ThreadList::GetThreadSPForThreadPtr (Thread *thread_ptr) -{ - ThreadSP thread_sp; - if (thread_ptr) - { - Mutex::Locker locker(GetMutex()); - - uint32_t idx = 0; - const uint32_t num_threads = m_threads.size(); - for (idx = 0; idx < num_threads; ++idx) - { - if (m_threads[idx].get() == thread_ptr) - { - thread_sp = m_threads[idx]; - break; - } - } - } - return thread_sp; -} - - - -ThreadSP -ThreadList::FindThreadByIndexID (uint32_t index_id, bool can_update) -{ - Mutex::Locker locker(GetMutex()); - - if (can_update) - m_process->UpdateThreadListIfNeeded(); - - ThreadSP thread_sp; - const uint32_t num_threads = m_threads.size(); - for (uint32_t idx = 0; idx < num_threads; ++idx) - { - if (m_threads[idx]->GetIndexID() == index_id) - { - thread_sp = m_threads[idx]; - break; - } - } - return thread_sp; -} - -bool -ThreadList::ShouldStop (Event *event_ptr) -{ - // Running events should never stop, obviously... - - Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); - - // The ShouldStop method of the threads can do a whole lot of work, - // figuring out whether the thread plan conditions are met. So we don't want - // to keep the ThreadList locked the whole time we are doing this. - // FIXME: It is possible that running code could cause new threads - // to be created. If that happens we will miss asking them whether - // then should stop. This is not a big deal, since we haven't had - // a chance to hang any interesting operations on those threads yet. - - collection threads_copy; - { - // Scope for locker - Mutex::Locker locker(GetMutex()); - - m_process->UpdateThreadListIfNeeded(); - threads_copy = m_threads; - } - - collection::iterator pos, end = threads_copy.end(); - - if (log) - { - log->PutCString(""); - log->Printf ("ThreadList::%s: %" PRIu64 " threads", __FUNCTION__, (uint64_t)m_threads.size()); - } - - bool did_anybody_stop_for_a_reason = false; - - // If the event is an Interrupt event, then we're going to stop no matter what. Otherwise, presume we won't stop. - bool should_stop = false; - if (Process::ProcessEventData::GetInterruptedFromEvent(event_ptr)) - { - if (log) - log->Printf("ThreadList::%s handling interrupt event, should stop set to true", __FUNCTION__); - - should_stop = true; - } - - // Now we run through all the threads and get their stop info's. We want to make sure to do this first before - // we start running the ShouldStop, because one thread's ShouldStop could destroy information (like deleting a - // thread specific breakpoint another thread had stopped at) which could lead us to compute the StopInfo incorrectly. - // We don't need to use it here, we just want to make sure it gets computed. - - for (pos = threads_copy.begin(); pos != end; ++pos) - { - ThreadSP thread_sp(*pos); - thread_sp->GetStopInfo(); - } - - for (pos = threads_copy.begin(); pos != end; ++pos) - { - ThreadSP thread_sp(*pos); - - // We should never get a stop for which no thread had a stop reason, but sometimes we do see this - - // for instance when we first connect to a remote stub. In that case we should stop, since we can't figure out - // the right thing to do and stopping gives the user control over what to do in this instance. - // - // Note, this causes a problem when you have a thread specific breakpoint, and a bunch of threads hit the breakpoint, - // but not the thread which we are waiting for. All the threads that are not "supposed" to hit the breakpoint - // are marked as having no stop reason, which is right, they should not show a stop reason. But that triggers this - // code and causes us to stop seemingly for no reason. - // - // Since the only way we ever saw this error was on first attach, I'm only going to trigger set did_anybody_stop_for_a_reason - // to true unless this is the first stop. - // - // If this becomes a problem, we'll have to have another StopReason like "StopInfoHidden" which will look invalid - // everywhere but at this check. - - if (thread_sp->GetProcess()->GetStopID() > 1) - did_anybody_stop_for_a_reason = true; - else - did_anybody_stop_for_a_reason |= thread_sp->ThreadStoppedForAReason(); - - const bool thread_should_stop = thread_sp->ShouldStop(event_ptr); - if (thread_should_stop) - should_stop |= true; - } - - if (!should_stop && !did_anybody_stop_for_a_reason) - { - should_stop = true; - if (log) - log->Printf ("ThreadList::%s we stopped but no threads had a stop reason, overriding should_stop and stopping.", __FUNCTION__); - } - - if (log) - log->Printf ("ThreadList::%s overall should_stop = %i", __FUNCTION__, should_stop); - - if (should_stop) - { - for (pos = threads_copy.begin(); pos != end; ++pos) - { - ThreadSP thread_sp(*pos); - thread_sp->WillStop (); - } - } - - return should_stop; -} - -Vote -ThreadList::ShouldReportStop (Event *event_ptr) -{ - Mutex::Locker locker(GetMutex()); - - Vote result = eVoteNoOpinion; - m_process->UpdateThreadListIfNeeded(); - collection::iterator pos, end = m_threads.end(); - - Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); - - if (log) - log->Printf ("ThreadList::%s %" PRIu64 " threads", __FUNCTION__, (uint64_t)m_threads.size()); - - // Run through the threads and ask whether we should report this event. - // For stopping, a YES vote wins over everything. A NO vote wins over NO opinion. - for (pos = m_threads.begin(); pos != end; ++pos) - { - ThreadSP thread_sp(*pos); - const Vote vote = thread_sp->ShouldReportStop (event_ptr); - switch (vote) - { - case eVoteNoOpinion: - continue; - - case eVoteYes: - result = eVoteYes; - break; - - case eVoteNo: - if (result == eVoteNoOpinion) - { - result = eVoteNo; - } - else - { - if (log) - log->Printf ("ThreadList::%s thread 0x%4.4" PRIx64 ": voted %s, but lost out because result was %s", - __FUNCTION__, - thread_sp->GetID (), - GetVoteAsCString (vote), - GetVoteAsCString (result)); - } - break; - } - } - if (log) - log->Printf ("ThreadList::%s returning %s", __FUNCTION__, GetVoteAsCString (result)); - return result; -} - -void -ThreadList::SetShouldReportStop (Vote vote) -{ - Mutex::Locker locker(GetMutex()); - m_process->UpdateThreadListIfNeeded(); - collection::iterator pos, end = m_threads.end(); - for (pos = m_threads.begin(); pos != end; ++pos) - { - ThreadSP thread_sp(*pos); - thread_sp->SetShouldReportStop (vote); - } -} - -Vote -ThreadList::ShouldReportRun (Event *event_ptr) -{ - - Mutex::Locker locker(GetMutex()); - - Vote result = eVoteNoOpinion; - m_process->UpdateThreadListIfNeeded(); - collection::iterator pos, end = m_threads.end(); - - // Run through the threads and ask whether we should report this event. - // The rule is NO vote wins over everything, a YES vote wins over no opinion. - - Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); - - for (pos = m_threads.begin(); pos != end; ++pos) - { - if ((*pos)->GetResumeState () != eStateSuspended) - { - switch ((*pos)->ShouldReportRun (event_ptr)) - { - case eVoteNoOpinion: - continue; - case eVoteYes: - if (result == eVoteNoOpinion) - result = eVoteYes; - break; - case eVoteNo: - if (log) - log->Printf ("ThreadList::ShouldReportRun() thread %d (0x%4.4" PRIx64 ") says don't report.", - (*pos)->GetIndexID(), - (*pos)->GetID()); - result = eVoteNo; - break; - } - } - } - return result; -} - -void -ThreadList::Clear() -{ - Mutex::Locker locker(GetMutex()); - m_stop_id = 0; - m_threads.clear(); - m_selected_tid = LLDB_INVALID_THREAD_ID; -} - -void -ThreadList::Destroy() -{ - Mutex::Locker locker(GetMutex()); - const uint32_t num_threads = m_threads.size(); - for (uint32_t idx = 0; idx < num_threads; ++idx) - { - m_threads[idx]->DestroyThread(); - } -} - -void -ThreadList::RefreshStateAfterStop () -{ - Mutex::Locker locker(GetMutex()); - - m_process->UpdateThreadListIfNeeded(); - - Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); - if (log && log->GetVerbose()) - log->Printf ("Turning off notification of new threads while single stepping a thread."); - - collection::iterator pos, end = m_threads.end(); - for (pos = m_threads.begin(); pos != end; ++pos) - (*pos)->RefreshStateAfterStop (); -} - -void -ThreadList::DiscardThreadPlans () -{ - // You don't need to update the thread list here, because only threads - // that you currently know about have any thread plans. - Mutex::Locker locker(GetMutex()); - - collection::iterator pos, end = m_threads.end(); - for (pos = m_threads.begin(); pos != end; ++pos) - (*pos)->DiscardThreadPlans (true); - -} - -bool -ThreadList::WillResume () -{ - // Run through the threads and perform their momentary actions. - // But we only do this for threads that are running, user suspended - // threads stay where they are. - - Mutex::Locker locker(GetMutex()); - m_process->UpdateThreadListIfNeeded(); - - collection::iterator pos, end = m_threads.end(); - - // See if any thread wants to run stopping others. If it does, then we won't - // setup the other threads for resume, since they aren't going to get a chance - // to run. This is necessary because the SetupForResume might add "StopOthers" - // plans which would then get to be part of the who-gets-to-run negotiation, but - // they're coming in after the fact, and the threads that are already set up should - // take priority. - - bool wants_solo_run = false; - - for (pos = m_threads.begin(); pos != end; ++pos) - { - if ((*pos)->GetResumeState() != eStateSuspended && - (*pos)->GetCurrentPlan()->StopOthers()) - { - if ((*pos)->IsOperatingSystemPluginThread() && !(*pos)->GetBackingThread()) - continue; - wants_solo_run = true; - break; - } - } - - if (wants_solo_run) - { - Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); - if (log && log->GetVerbose()) - log->Printf ("Turning on notification of new threads while single stepping a thread."); - m_process->StartNoticingNewThreads(); - } - else - { - Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); - if (log && log->GetVerbose()) - log->Printf ("Turning off notification of new threads while single stepping a thread."); - m_process->StopNoticingNewThreads(); - } - - // Give all the threads that are likely to run a last chance to set up their state before we - // negotiate who is actually going to get a chance to run... - // Don't set to resume suspended threads, and if any thread wanted to stop others, only - // call setup on the threads that request StopOthers... - - for (pos = m_threads.begin(); pos != end; ++pos) - { - if ((*pos)->GetResumeState() != eStateSuspended - && (!wants_solo_run || (*pos)->GetCurrentPlan()->StopOthers())) - { - if ((*pos)->IsOperatingSystemPluginThread() && !(*pos)->GetBackingThread()) - continue; - (*pos)->SetupForResume (); - } - } - - // Now go through the threads and see if any thread wants to run just itself. - // if so then pick one and run it. - - ThreadList run_me_only_list (m_process); - - run_me_only_list.SetStopID(m_process->GetStopID()); - - bool run_only_current_thread = false; - - for (pos = m_threads.begin(); pos != end; ++pos) - { - ThreadSP thread_sp(*pos); - if (thread_sp->GetResumeState() != eStateSuspended && - thread_sp->GetCurrentPlan()->StopOthers()) - { - if ((*pos)->IsOperatingSystemPluginThread() && !(*pos)->GetBackingThread()) - continue; - - // You can't say "stop others" and also want yourself to be suspended. - assert (thread_sp->GetCurrentPlan()->RunState() != eStateSuspended); - - if (thread_sp == GetSelectedThread()) - { - run_only_current_thread = true; - run_me_only_list.Clear(); - run_me_only_list.AddThread (thread_sp); - break; - } - - run_me_only_list.AddThread (thread_sp); - } - - } - - bool need_to_resume = true; - - if (run_me_only_list.GetSize (false) == 0) - { - // Everybody runs as they wish: - for (pos = m_threads.begin(); pos != end; ++pos) - { - ThreadSP thread_sp(*pos); - StateType run_state; - if (thread_sp->GetResumeState() != eStateSuspended) - run_state = thread_sp->GetCurrentPlan()->RunState(); - else - run_state = eStateSuspended; - if (!thread_sp->ShouldResume(run_state)) - need_to_resume = false; - } - } - else - { - ThreadSP thread_to_run; - - if (run_only_current_thread) - { - thread_to_run = GetSelectedThread(); - } - else if (run_me_only_list.GetSize (false) == 1) - { - thread_to_run = run_me_only_list.GetThreadAtIndex (0); - } - else - { - int random_thread = (int) - ((run_me_only_list.GetSize (false) * (double) rand ()) / (RAND_MAX + 1.0)); - thread_to_run = run_me_only_list.GetThreadAtIndex (random_thread); - } - - for (pos = m_threads.begin(); pos != end; ++pos) - { - ThreadSP thread_sp(*pos); - if (thread_sp == thread_to_run) - { - if (!thread_sp->ShouldResume(thread_sp->GetCurrentPlan()->RunState())) - need_to_resume = false; - } - else - thread_sp->ShouldResume (eStateSuspended); - } - } - - return need_to_resume; -} - -void -ThreadList::DidResume () -{ - Mutex::Locker locker(GetMutex()); - collection::iterator pos, end = m_threads.end(); - for (pos = m_threads.begin(); pos != end; ++pos) - { - // Don't clear out threads that aren't going to get a chance to run, rather - // leave their state for the next time around. - ThreadSP thread_sp(*pos); - if (thread_sp->GetResumeState() != eStateSuspended) - thread_sp->DidResume (); - } -} - -void -ThreadList::DidStop () -{ - Mutex::Locker locker(GetMutex()); - collection::iterator pos, end = m_threads.end(); - for (pos = m_threads.begin(); pos != end; ++pos) - { - // Notify threads that the process just stopped. - // Note, this currently assumes that all threads in the list - // stop when the process stops. In the future we will want to support - // a debugging model where some threads continue to run while others - // are stopped. We either need to handle that somehow here or - // create a special thread list containing only threads which will - // stop in the code that calls this method (currently - // Process::SetPrivateState). - ThreadSP thread_sp(*pos); - if (StateIsRunningState(thread_sp->GetState())) - thread_sp->DidStop (); - } -} - -ThreadSP -ThreadList::GetSelectedThread () -{ - Mutex::Locker locker(GetMutex()); - ThreadSP thread_sp = FindThreadByID(m_selected_tid); - if (!thread_sp.get()) - { - if (m_threads.size() == 0) - return thread_sp; - m_selected_tid = m_threads[0]->GetID(); - thread_sp = m_threads[0]; - } - return thread_sp; -} - -bool -ThreadList::SetSelectedThreadByID (lldb::tid_t tid, bool notify) -{ - Mutex::Locker locker(GetMutex()); - ThreadSP selected_thread_sp(FindThreadByID(tid)); - if (selected_thread_sp) - { - m_selected_tid = tid; - selected_thread_sp->SetDefaultFileAndLineToSelectedFrame(); - } - else - m_selected_tid = LLDB_INVALID_THREAD_ID; - - if (notify) - NotifySelectedThreadChanged(m_selected_tid); - - return m_selected_tid != LLDB_INVALID_THREAD_ID; -} - -bool -ThreadList::SetSelectedThreadByIndexID (uint32_t index_id, bool notify) -{ - Mutex::Locker locker(GetMutex()); - ThreadSP selected_thread_sp (FindThreadByIndexID(index_id)); - if (selected_thread_sp.get()) - { - m_selected_tid = selected_thread_sp->GetID(); - selected_thread_sp->SetDefaultFileAndLineToSelectedFrame(); - } - else - m_selected_tid = LLDB_INVALID_THREAD_ID; - - if (notify) - NotifySelectedThreadChanged(m_selected_tid); - - return m_selected_tid != LLDB_INVALID_THREAD_ID; -} - -void -ThreadList::NotifySelectedThreadChanged (lldb::tid_t tid) -{ - ThreadSP selected_thread_sp (FindThreadByID(tid)); - if (selected_thread_sp->EventTypeHasListeners(Thread::eBroadcastBitThreadSelected)) - selected_thread_sp->BroadcastEvent(Thread::eBroadcastBitThreadSelected, - new Thread::ThreadEventData(selected_thread_sp)); -} - -void -ThreadList::Update (ThreadList &rhs) -{ - if (this != &rhs) - { - // Lock both mutexes to make sure neither side changes anyone on us - // while the assignement occurs - Mutex::Locker locker(GetMutex()); - m_process = rhs.m_process; - m_stop_id = rhs.m_stop_id; - m_threads.swap(rhs.m_threads); - m_selected_tid = rhs.m_selected_tid; - - - // Now we look for threads that we are done with and - // make sure to clear them up as much as possible so - // anyone with a shared pointer will still have a reference, - // but the thread won't be of much use. Using std::weak_ptr - // for all backward references (such as a thread to a process) - // will eventually solve this issue for us, but for now, we - // need to work around the issue - collection::iterator rhs_pos, rhs_end = rhs.m_threads.end(); - for (rhs_pos = rhs.m_threads.begin(); rhs_pos != rhs_end; ++rhs_pos) - { - const lldb::tid_t tid = (*rhs_pos)->GetID(); - bool thread_is_alive = false; - const uint32_t num_threads = m_threads.size(); - for (uint32_t idx = 0; idx < num_threads; ++idx) - { - if (m_threads[idx]->GetID() == tid) - { - thread_is_alive = true; - break; - } - } - if (!thread_is_alive) - (*rhs_pos)->DestroyThread(); - } - } -} - -void -ThreadList::Flush () -{ - Mutex::Locker locker(GetMutex()); - collection::iterator pos, end = m_threads.end(); - for (pos = m_threads.begin(); pos != end; ++pos) - (*pos)->Flush (); -} - -Mutex & -ThreadList::GetMutex () -{ - return m_process->m_thread_mutex; -} -