#define GDBSERVER_GDBTHREAD_H
#include "gdbsupport/function-view.h"
-#include "gdbsupport/owning_intrusive_list.h"
+#include "gdbsupport/intrusive_list.h"
struct btrace_target_info;
struct regcache;
gdb_thread_options thread_options = 0;
};
-extern owning_intrusive_list<thread_info> all_threads;
-
void remove_thread (struct thread_info *thread);
struct thread_info *add_thread (ptid_t ptid, void *target_data);
void for_each_thread (int pid, gdb::function_view<void (thread_info *)> func);
-/* Find the a random thread for which FUNC (THREAD) returns true. If
- no entry is found then return NULL. */
+/* Like the above, but only consider threads matching PTID. */
+
+void for_each_thread
+ (ptid_t ptid, gdb::function_view<void (thread_info *)> func);
+
+/* Find a random thread that matches PTID and for which FUNC (THREAD)
+ returns true. If no entry is found then return nullptr. */
thread_info *find_thread_in_random
(gdb::function_view<bool (thread_info *)> func);
+/* Like the above, but only consider threads matching PTID. */
+
+thread_info *find_thread_in_random
+ (ptid_t ptid, gdb::function_view<bool (thread_info *)> func);
+
/* Get current thread ID (Linux task ID). */
#define current_ptid (current_thread->id)
#include "dll.h"
owning_intrusive_list<process_info> all_processes;
-owning_intrusive_list<thread_info> all_threads;
/* The current process. */
static process_info *current_process_;
struct thread_info *
add_thread (ptid_t thread_id, void *target_data)
{
- auto &new_thread = all_threads.emplace_back (thread_id, target_data);
+ process_info *process = find_process_pid (thread_id.pid ());
+ gdb_assert (process != nullptr);
+
+ auto &new_thread = process->thread_list ().emplace_back (thread_id,
+ target_data);
+ bool inserted
+ = process->thread_map ().insert ({thread_id, &new_thread}).second;
+
+ /* A thread with this ptid should not exist in the map yet. */
+ gdb_assert (inserted);
if (current_thread == NULL)
switch_to_thread (&new_thread);
struct thread_info *
get_first_thread (void)
{
- if (!all_threads.empty ())
- return &all_threads.front ();
- else
- return NULL;
+ return find_thread ([] (thread_info *thread)
+ {
+ return true;
+ });
}
struct thread_info *
find_thread_ptid (ptid_t ptid)
{
- return find_thread ([&] (thread_info *thread) {
- return thread->id == ptid;
- });
+ process_info *process = find_process_pid (ptid.pid ());
+ if (process == nullptr)
+ return nullptr;
+
+ auto &thread_map = process->thread_map ();
+
+ if (auto it = thread_map.find (ptid);
+ it != thread_map.end ())
+ return it->second;
+
+ return nullptr;
}
/* Find a thread associated with the given PROCESS, or NULL if no
target_disable_btrace (thread->btrace);
discard_queued_stop_replies (ptid_of (thread));
+ process_info *process = get_thread_process (thread);
+ gdb_assert (process != nullptr);
+
if (current_thread == thread)
switch_to_thread (nullptr);
- all_threads.erase (all_threads.iterator_to (*thread));
+ /* We should not try to remove a thread that was not added. */
+ int num_erased = process->thread_map ().erase (thread->id);
+ gdb_assert (num_erased > 0);
+
+ process->thread_list ().erase (process->thread_list ().iterator_to (*thread));
}
void *
void
clear_inferiors (void)
{
- all_threads.clear ();
+ for_each_process ([] (process_info *process)
+ {
+ process->thread_list ().clear ();
+ process->thread_map ().clear ();
+ });
clear_dlls ();
return NULL;
}
-/* See gdbthread.h. */
+/* See inferiors.h. */
thread_info *
-find_thread (gdb::function_view<bool (thread_info *)> func)
+process_info::find_thread (gdb::function_view<bool (thread_info *)> func)
{
- for (thread_info &thread : all_threads)
+ for (thread_info &thread : m_thread_list)
if (func (&thread))
return &thread;
+ return nullptr;
+}
+
+/* See gdbthread.h. */
+
+thread_info *
+find_thread (gdb::function_view<bool (thread_info *)> func)
+{
+ for (process_info &process : all_processes)
+ if (thread_info *thread = process.find_thread (func);
+ thread != nullptr)
+ return thread;
+
return NULL;
}
thread_info *
find_thread (int pid, gdb::function_view<bool (thread_info *)> func)
{
- return find_thread ([&] (thread_info *thread)
- {
- return thread->id.pid () == pid && func (thread);
- });
+ process_info *process = find_process_pid (pid);
+ if (process == nullptr)
+ return nullptr;
+
+ return process->find_thread (func);
}
/* See gdbthread.h. */
thread_info *
find_thread (ptid_t filter, gdb::function_view<bool (thread_info *)> func)
{
- return find_thread ([&] (thread_info *thread) {
- return thread->id.matches (filter) && func (thread);
- });
+ if (filter == minus_one_ptid)
+ return find_thread (func);
+
+ process_info *process = find_process_pid (filter.pid ());
+ if (process == nullptr)
+ return nullptr;
+
+ if (filter.is_pid ())
+ return process->find_thread (func);
+
+ auto &thread_map = process->thread_map ();
+
+ if (auto it = thread_map.find (filter);
+ it != thread_map.end () && func (it->second))
+ return it->second;
+
+ return nullptr;
}
/* See gdbthread.h. */
void
for_each_thread (gdb::function_view<void (thread_info *)> func)
{
- owning_intrusive_list<thread_info>::iterator next, cur = all_threads.begin ();
+ for_each_process ([&] (process_info *proc)
+ {
+ proc->for_each_thread (func);
+ });
+}
+
+/* See inferiors.h. */
+
+void
+process_info::for_each_thread (gdb::function_view<void (thread_info *)> func)
+{
+ owning_intrusive_list<thread_info>::iterator next, cur
+ = m_thread_list.begin ();
- while (cur != all_threads.end ())
+ while (cur != m_thread_list.end ())
{
+ /* FUNC may alter the current iterator. */
next = cur;
next++;
func (&*cur);
void
for_each_thread (int pid, gdb::function_view<void (thread_info *)> func)
{
- for_each_thread ([&] (thread_info *thread)
- {
- if (pid == thread->id.pid ())
+ process_info *process = find_process_pid (pid);
+ if (process == nullptr)
+ return;
+
+ process->for_each_thread (func);
+}
+
+/* See gdbthread.h. */
+
+void
+for_each_thread (ptid_t ptid, gdb::function_view<void (thread_info *)> func)
+{
+ if (ptid == minus_one_ptid)
+ for_each_thread (func);
+ else if (ptid.is_pid ())
+ for_each_thread (ptid.pid (), func);
+ else
+ find_thread (ptid, [func] (thread_info *thread)
+ {
func (thread);
- });
+ return false;
+ });
}
/* See gdbthread.h. */
thread_info *
-find_thread_in_random (gdb::function_view<bool (thread_info *)> func)
+find_thread_in_random (ptid_t ptid,
+ gdb::function_view<bool (thread_info *)> func)
{
int count = 0;
int random_selector;
/* First count how many interesting entries we have. */
- for_each_thread ([&] (thread_info *thread) {
- if (func (thread))
- count++;
- });
+ for_each_thread (ptid, [&] (thread_info *thread)
+ {
+ if (func (thread))
+ count++;
+ });
if (count == 0)
- return NULL;
+ return nullptr;
/* Now randomly pick an entry out of those. */
random_selector = (int)
((count * (double) rand ()) / (RAND_MAX + 1.0));
- thread_info *thread = find_thread ([&] (thread_info *thr_arg) {
- return func (thr_arg) && (random_selector-- == 0);
- });
+ thread_info *thread = find_thread (ptid, [&] (thread_info *thr_arg)
+ {
+ return func (thr_arg) && (random_selector-- == 0);
+ });
gdb_assert (thread != NULL);
return thread;
}
+/* See gdbthread.h. */
+
+thread_info *
+find_thread_in_random (gdb::function_view<bool (thread_info *)> func)
+{
+ return find_thread_in_random (minus_one_ptid, func);
+}
/* See gdbsupport/common-gdbthread.h. */
#include "dll.h"
+#include <unordered_map>
+
struct thread_info;
struct regcache;
struct target_desc;
struct raw_breakpoint;
struct fast_tracepoint_jump;
struct process_info_private;
+struct process_info;
+
+extern owning_intrusive_list<process_info> all_processes;
struct process_info : public intrusive_list_node<process_info>
{
not access inferior memory or registers, as we haven't determined
the target architecture/description. */
bool starting_up = false;
+
+ /* Return a reference to the private thread list. */
+ owning_intrusive_list<thread_info> &thread_list ()
+ { return m_thread_list; }
+
+ /* Return a reference to the private thread map. */
+ std::unordered_map<ptid_t, thread_info *> &thread_map ()
+ { return m_ptid_thread_map; }
+
+ /* Find the first thread for which FUNC returns true. Return nullptr if no
+ such thread is found. */
+ thread_info *find_thread (gdb::function_view<bool (thread_info *)> func);
+
+ /* Invoke FUNC for each thread. */
+ void for_each_thread (gdb::function_view<void (thread_info *)> func);
+
+private:
+ /* This processes' thread list, sorted by creation order. */
+ owning_intrusive_list<thread_info> m_thread_list;
+
+ /* A map of ptid_t to thread_info*, for average O(1) ptid_t lookup.
+ Exited threads do not appear in the map. */
+ std::unordered_map<ptid_t, thread_info *> m_ptid_thread_map;
};
/* Get the pid of PROC. */
return proc->pid;
}
-/* Return a pointer to the process that corresponds to the current
- thread (current_thread). It is an error to call this if there is
- no current thread selected. */
+/* Return a pointer to the current process. Note that the current
+ process may be non-null while the current thread (current_thread)
+ is null. */
struct process_info *current_process (void);
struct process_info *get_thread_process (const struct thread_info *);
another process might delete the next thread in the iteration, which is
the one saved by the safe iterator. We will never delete the currently
iterated on thread, so standard iteration should be safe. */
- for (thread_info &thread : all_threads)
+ for (thread_info &thread : process->thread_list ())
{
- /* Only threads that are of the process we are detaching. */
- if (thread.id.pid () != pid)
- continue;
-
/* Only threads that have a pending fork event. */
target_waitkind kind;
thread_info *child = target_thread_pending_child (&thread, &kind);
handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
{
client_state &cs = get_client_state ();
+ static owning_intrusive_list<process_info>::iterator process_iter;
static owning_intrusive_list<thread_info>::iterator thread_iter;
+ auto init_thread_iter = [&] ()
+ {
+ process_iter = all_processes.begin ();
+ owning_intrusive_list<thread_info> *thread_list;
+
+ for (; process_iter != all_processes.end (); ++process_iter)
+ {
+ thread_list = &process_iter->thread_list ();
+ thread_iter = thread_list->begin ();
+ if (thread_iter != thread_list->end ())
+ break;
+ }
+ /* Make sure that there is at least one thread to iterate. */
+ gdb_assert (process_iter != all_processes.end ());
+ gdb_assert (thread_iter != thread_list->end ());
+ };
+
+ auto advance_thread_iter = [&] ()
+ {
+ /* The loop below is written in the natural way as-if we'd always
+ start at the beginning of the inferior list. This fast forwards
+ the algorithm to the actual current position. */
+ owning_intrusive_list<thread_info> *thread_list
+ = &process_iter->thread_list ();
+ goto start;
+
+ for (; process_iter != all_processes.end (); ++process_iter)
+ {
+ thread_list = &process_iter->thread_list ();
+ thread_iter = thread_list->begin ();
+ while (thread_iter != thread_list->end ())
+ {
+ return;
+ start:
+ ++thread_iter;
+ }
+ }
+ };
+
/* Reply the current thread id. */
if (strcmp ("qC", own_buf) == 0 && !disable_packet_qC)
{
ptid = cs.general_thread;
else
{
- thread_iter = all_threads.begin ();
+ init_thread_iter ();
ptid = thread_iter->id;
}
if (strcmp ("qfThreadInfo", own_buf) == 0)
{
require_running_or_return (own_buf);
- thread_iter = all_threads.begin ();
+ init_thread_iter ();
*own_buf++ = 'm';
ptid_t ptid = thread_iter->id;
write_ptid (own_buf, ptid);
- thread_iter++;
+ advance_thread_iter ();
return;
}
if (strcmp ("qsThreadInfo", own_buf) == 0)
{
require_running_or_return (own_buf);
- if (thread_iter != all_threads.end ())
+ /* We're done if the process iterator hits the end of the
+ process list. */
+ if (process_iter != all_processes.end ())
{
*own_buf++ = 'm';
ptid_t ptid = thread_iter->id;
write_ptid (own_buf, ptid);
- thread_iter++;
+ advance_thread_iter ();
return;
}
else