#include "cli/cli-decode.h"
#include "gdb_regex.h"
#include "cli/cli-utils.h"
-#include "continuations.h"
+#include "thread-fsm.h"
/* Definition of struct thread_info exported to gdbthread.h. */
&& breakpoint_has_location_inserted_here (ss_bps, aspace, addr));
}
+/* See gdbthread.h. */
+
+void
+thread_cancel_execution_command (struct thread_info *thr)
+{
+ if (thr->thread_fsm != NULL)
+ {
+ thread_fsm_clean_up (thr->thread_fsm);
+ thread_fsm_delete (thr->thread_fsm);
+ thr->thread_fsm = NULL;
+ }
+}
+
static void
clear_thread_inferior_resources (struct thread_info *tp)
{
btrace_teardown (tp);
- do_all_intermediate_continuations_thread (tp, 1);
- do_all_continuations_thread (tp, 1);
+ thread_cancel_execution_command (tp);
}
static void
free_thread (struct thread_info *tp)
{
- if (tp->private)
+ if (tp->priv)
{
if (tp->private_dtor)
- tp->private_dtor (tp->private);
+ tp->private_dtor (tp->priv);
else
- xfree (tp->private);
+ xfree (tp->priv);
}
xfree (tp->name);
static struct thread_info *
new_thread (ptid_t ptid)
{
- struct thread_info *tp;
-
- tp = xcalloc (1, sizeof (*tp));
+ struct thread_info *tp = XCNEW (struct thread_info);
tp->ptid = ptid;
tp->num = ++highest_thread_num;
/* Nothing to follow yet. */
tp->pending_follow.kind = TARGET_WAITKIND_SPURIOUS;
tp->state = THREAD_STOPPED;
+ tp->suspend.waitstatus.kind = TARGET_WAITKIND_IGNORE;
return tp;
}
}
struct thread_info *
-add_thread_with_info (ptid_t ptid, struct private_thread_info *private)
+add_thread_with_info (ptid_t ptid, struct private_thread_info *priv)
{
struct thread_info *result = add_thread_silent (ptid);
- result->private = private;
+ result->priv = priv;
if (print_thread_events)
printf_unfiltered (_("[New %s]\n"), target_pid_to_str (ptid));
return add_thread_with_info (ptid, NULL);
}
+/* Add TP to the end of the step-over chain LIST_P. */
+
+static void
+step_over_chain_enqueue (struct thread_info **list_p, struct thread_info *tp)
+{
+ gdb_assert (tp->step_over_next == NULL);
+ gdb_assert (tp->step_over_prev == NULL);
+
+ if (*list_p == NULL)
+ {
+ *list_p = tp;
+ tp->step_over_prev = tp->step_over_next = tp;
+ }
+ else
+ {
+ struct thread_info *head = *list_p;
+ struct thread_info *tail = head->step_over_prev;
+
+ tp->step_over_prev = tail;
+ tp->step_over_next = head;
+ head->step_over_prev = tp;
+ tail->step_over_next = tp;
+ }
+}
+
+/* Remove TP from step-over chain LIST_P. */
+
+static void
+step_over_chain_remove (struct thread_info **list_p, struct thread_info *tp)
+{
+ gdb_assert (tp->step_over_next != NULL);
+ gdb_assert (tp->step_over_prev != NULL);
+
+ if (*list_p == tp)
+ {
+ if (tp == tp->step_over_next)
+ *list_p = NULL;
+ else
+ *list_p = tp->step_over_next;
+ }
+
+ tp->step_over_prev->step_over_next = tp->step_over_next;
+ tp->step_over_next->step_over_prev = tp->step_over_prev;
+ tp->step_over_prev = tp->step_over_next = NULL;
+}
+
+/* See gdbthread.h. */
+
+struct thread_info *
+thread_step_over_chain_next (struct thread_info *tp)
+{
+ struct thread_info *next = tp->step_over_next;
+
+ return (next == step_over_queue_head ? NULL : next);
+}
+
+/* See gdbthread.h. */
+
+int
+thread_is_in_step_over_chain (struct thread_info *tp)
+{
+ return (tp->step_over_next != NULL);
+}
+
+/* See gdbthread.h. */
+
+void
+thread_step_over_chain_enqueue (struct thread_info *tp)
+{
+ step_over_chain_enqueue (&step_over_queue_head, tp);
+}
+
+/* See gdbthread.h. */
+
+void
+thread_step_over_chain_remove (struct thread_info *tp)
+{
+ step_over_chain_remove (&step_over_queue_head, tp);
+}
+
/* Delete thread PTID. If SILENT, don't notify the observer of this
exit. */
static void
if (!tp)
return;
+ /* Dead threads don't need to step-over. Remove from queue. */
+ if (tp->step_over_next != NULL)
+ thread_step_over_chain_remove (tp);
+
/* If this is the current thread, or there's code out there that
relies on it existing (refcount > 0) we can't delete yet. Mark
it as exited, and notify it. */
void
prune_threads (void)
{
- struct thread_info *tp, *next;
+ struct thread_info *tp, *tmp;
- for (tp = thread_list; tp; tp = next)
+ ALL_THREADS_SAFE (tp, tmp)
{
- next = tp->next;
if (!thread_alive (tp))
delete_thread (tp->ptid);
}
}
+/* See gdbthreads.h. */
+
+void
+delete_exited_threads (void)
+{
+ struct thread_info *tp, *tmp;
+
+ ALL_THREADS_SAFE (tp, tmp)
+ {
+ if (tp->state == THREAD_EXITED)
+ delete_thread (tp->ptid);
+ }
+}
+
/* Disable storing stack temporaries for the thread whose id is
stored in DATA. */
tp->stack_temporaries_enabled = 1;
tp->stack_temporaries = NULL;
- data = (ptid_t *) xmalloc (sizeof (ptid_t));
+ data = XNEW (ptid_t);
*data = ptid;
c = make_cleanup (disable_thread_stack_temporaries, data);
observer_notify_thread_ptid_changed (old_ptid, new_ptid);
}
+/* See gdbthread.h. */
+
+void
+set_resumed (ptid_t ptid, int resumed)
+{
+ struct thread_info *tp;
+ int all = ptid_equal (ptid, minus_one_ptid);
+
+ if (all || ptid_is_pid (ptid))
+ {
+ for (tp = thread_list; tp; tp = tp->next)
+ if (all || ptid_get_pid (tp->ptid) == ptid_get_pid (ptid))
+ tp->resumed = resumed;
+ }
+ else
+ {
+ tp = find_thread_ptid (ptid);
+ gdb_assert (tp != NULL);
+ tp->resumed = resumed;
+ }
+}
+
+/* Helper for set_running, that marks one thread either running or
+ stopped. */
+
+static int
+set_running_thread (struct thread_info *tp, int running)
+{
+ int started = 0;
+
+ if (running && tp->state == THREAD_STOPPED)
+ started = 1;
+ tp->state = running ? THREAD_RUNNING : THREAD_STOPPED;
+
+ if (!running)
+ {
+ /* If the thread is now marked stopped, remove it from
+ the step-over queue, so that we don't try to resume
+ it until the user wants it to. */
+ if (tp->step_over_next != NULL)
+ thread_step_over_chain_remove (tp);
+ }
+
+ return started;
+}
+
void
set_running (ptid_t ptid, int running)
{
struct thread_info *tp;
int all = ptid_equal (ptid, minus_one_ptid);
+ int any_started = 0;
/* We try not to notify the observer if no thread has actually changed
the running state -- merely to reduce the number of messages to
frontend. Frontend is supposed to handle multiple *running just fine. */
if (all || ptid_is_pid (ptid))
{
- int any_started = 0;
-
for (tp = thread_list; tp; tp = tp->next)
if (all || ptid_get_pid (tp->ptid) == ptid_get_pid (ptid))
{
if (tp->state == THREAD_EXITED)
continue;
- if (running && tp->state == THREAD_STOPPED)
+
+ if (set_running_thread (tp, running))
any_started = 1;
- tp->state = running ? THREAD_RUNNING : THREAD_STOPPED;
}
- if (any_started)
- observer_notify_target_resumed (ptid);
}
else
{
- int started = 0;
-
tp = find_thread_ptid (ptid);
- gdb_assert (tp);
+ gdb_assert (tp != NULL);
gdb_assert (tp->state != THREAD_EXITED);
- if (running && tp->state == THREAD_STOPPED)
- started = 1;
- tp->state = running ? THREAD_RUNNING : THREAD_STOPPED;
- if (started)
- observer_notify_target_resumed (ptid);
+ if (set_running_thread (tp, running))
+ any_started = 1;
}
+ if (any_started)
+ observer_notify_target_resumed (ptid);
}
static int
continue;
if (all || ptid_get_pid (ptid) == ptid_get_pid (tp->ptid))
{
- if (tp->executing && tp->state == THREAD_STOPPED)
+ if (set_running_thread (tp, tp->executing))
any_started = 1;
- tp->state = tp->executing ? THREAD_RUNNING : THREAD_STOPPED;
}
}
}
gdb_assert (tp);
if (tp->state != THREAD_EXITED)
{
- if (tp->executing && tp->state == THREAD_STOPPED)
+ if (set_running_thread (tp, tp->executing))
any_started = 1;
- tp->state = tp->executing ? THREAD_RUNNING : THREAD_STOPPED;
}
}
}
}
+/* Data used by the cleanup installed by
+ 'make_cleanup_restore_current_thread'. */
+
struct current_thread_cleanup
{
+ /* Next in list of currently installed 'struct
+ current_thread_cleanup' cleanups. See
+ 'current_thread_cleanup_chain' below. */
+ struct current_thread_cleanup *next;
+
ptid_t inferior_ptid;
struct frame_id selected_frame_id;
int selected_frame_level;
int was_removable;
};
+/* A chain of currently installed 'struct current_thread_cleanup'
+ cleanups. Restoring the previously selected thread looks up the
+ old thread in the thread list by ptid. If the thread changes ptid,
+ we need to update the cleanup's thread structure so the look up
+ succeeds. */
+static struct current_thread_cleanup *current_thread_cleanup_chain;
+
+/* A thread_ptid_changed observer. Update all currently installed
+ current_thread_cleanup cleanups that want to switch back to
+ OLD_PTID to switch back to NEW_PTID instead. */
+
+static void
+restore_current_thread_ptid_changed (ptid_t old_ptid, ptid_t new_ptid)
+{
+ struct current_thread_cleanup *it;
+
+ for (it = current_thread_cleanup_chain; it != NULL; it = it->next)
+ {
+ if (ptid_equal (it->inferior_ptid, old_ptid))
+ it->inferior_ptid = new_ptid;
+ }
+}
+
static void
do_restore_current_thread_cleanup (void *arg)
{
struct thread_info *tp;
struct inferior *inf;
+ current_thread_cleanup_chain = current_thread_cleanup_chain->next;
+
tp = find_thread_ptid (old->inferior_ptid);
if (tp)
tp->refcount--;
{
struct thread_info *tp;
struct frame_info *frame;
- struct current_thread_cleanup *old;
+ struct current_thread_cleanup *old = XNEW (struct current_thread_cleanup);
- old = xmalloc (sizeof (struct current_thread_cleanup));
old->inferior_ptid = inferior_ptid;
old->inf_id = current_inferior ()->num;
old->was_removable = current_inferior ()->removable;
+ old->next = current_thread_cleanup_chain;
+ current_thread_cleanup_chain = old;
+
if (!ptid_equal (inferior_ptid, null_ptid))
{
old->was_stopped = is_stopped (inferior_ptid);
restore_current_thread_cleanup_dtor);
}
+/* If non-zero tp_array_compar should sort in ascending order, otherwise in
+ descending order. */
+
+static int tp_array_compar_ascending;
+
+/* Sort an array for struct thread_info pointers by their NUM, order is
+ determined by TP_ARRAY_COMPAR_ASCENDING. */
+
+static int
+tp_array_compar (const void *ap_voidp, const void *bp_voidp)
+{
+ const struct thread_info *const *ap = ap_voidp;
+ const struct thread_info *const *bp = bp_voidp;
+
+ return ((((*ap)->num > (*bp)->num) - ((*ap)->num < (*bp)->num))
+ * (tp_array_compar_ascending ? +1 : -1));
+}
+
/* Apply a GDB command to a list of threads. List syntax is a whitespace
seperated list of numbers, or ranges, or the keyword `all'. Ranges consist
of two numbers seperated by a hyphen. Examples:
int tc;
struct thread_array_cleanup ta_cleanup;
+ tp_array_compar_ascending = 0;
+ if (cmd != NULL
+ && check_for_argument (&cmd, "-ascending", strlen ("-ascending")))
+ {
+ cmd = skip_spaces (cmd);
+ tp_array_compar_ascending = 1;
+ }
+
if (cmd == NULL || *cmd == '\000')
error (_("Please specify a command following the thread ID list"));
execute_command. */
saved_cmd = xstrdup (cmd);
make_cleanup (xfree, saved_cmd);
- tc = thread_count ();
- if (tc)
+ /* Note this includes exited threads. */
+ tc = thread_count ();
+ if (tc != 0)
{
struct thread_info **tp_array;
struct thread_info *tp;
/* Save a copy of the thread_list in case we execute detach
command. */
- tp_array = xmalloc (sizeof (struct thread_info *) * tc);
+ tp_array = XNEWVEC (struct thread_info *, tc);
make_cleanup (xfree, tp_array);
- ta_cleanup.tp_array = tp_array;
- ta_cleanup.count = tc;
ALL_NON_EXITED_THREADS (tp)
{
tp->refcount++;
i++;
}
+ /* Because we skipped exited threads, we may end up with fewer
+ threads in the array than the total count of threads. */
+ gdb_assert (i <= tc);
+
+ if (i != 0)
+ qsort (tp_array, i, sizeof (*tp_array), tp_array_compar);
+ ta_cleanup.tp_array = tp_array;
+ ta_cleanup.count = i;
make_cleanup (set_thread_refcount, &ta_cleanup);
for (k = 0; k != i; k++)
&thread_apply_list, "thread apply ", 1, &thread_cmd_list);
add_cmd ("all", class_run, thread_apply_all_command,
- _("Apply a command to all threads."), &thread_apply_list);
+ _("\
+Apply a command to all threads.\n\
+\n\
+Usage: thread apply all [-ascending] <command>\n\
+-ascending: Call <command> for all threads in ascending order.\n\
+ The default is descending order.\
+"),
+ &thread_apply_list);
add_cmd ("name", class_run, thread_name_command,
_("Set the current thread's name.\n\
Will display thread ids whose name, target ID, or extra info matches REGEXP."),
&thread_cmd_list);
- if (!xdb_commands)
- add_com_alias ("t", "thread", class_run, 1);
+ add_com_alias ("t", "thread", class_run, 1);
add_setshow_boolean_cmd ("thread-events", no_class,
&print_thread_events, _("\
&setprintlist, &showprintlist);
create_internalvar_type_lazy ("_thread", &thread_funcs, NULL);
+
+ observer_attach_thread_ptid_changed (restore_current_thread_ptid_changed);
}