2017-04-10 Yao Qi <yao.qi@linaro.org>
+ PR gdb/19942
+ * gdbthread.h (thread_info::deletable): New method.
+ (thread_info::incref): New method.
+ (thread_info::decref): New method.
+ (thread_info::refcount): Move it to private.
+ * infrun.c (save_stop_context): Call inc_refcount.
+ (release_stop_context_cleanup): Likewise.
+ * thread.c (set_thread_exited): New function.
+ (init_thread_list): Delete "tp" only it is deletable, otherwise
+ call set_thread_exited.
+ (delete_thread_1): Call set_thread_exited.
+ (current_thread_cleanup) <inferior_pid>: Remove.
+ <thread>: New field.
+ (restore_current_thread_ptid_changed): Removed.
+ (do_restore_current_thread_cleanup): Adjust.
+ (restore_current_thread_cleanup_dtor): Don't call
+ find_thread_ptid.
+ (set_thread_refcount): Use dec_refcount.
+ (make_cleanup_restore_current_thread): Adjust.
+ (thread_apply_all_command): Call inc_refcount.
+ (_initialize_thread): Don't call
+ observer_attach_thread_ptid_changed.
+
+2017-04-10 Yao Qi <yao.qi@linaro.org>
+
* thread.c (delete_thread_1): Hoist code on marking thread as
exited.
explicit thread_info (inferior *inf, ptid_t ptid);
~thread_info ();
+ bool deletable () const
+ {
+ /* If this is the current thread, or there's code out there that
+ relies on it existing (m_refcount > 0) we can't delete yet. */
+ return (m_refcount == 0 && !ptid_equal (ptid, inferior_ptid));
+ }
+
+ /* Increase the refcount. */
+ void incref ()
+ {
+ gdb_assert (m_refcount >= 0);
+ m_refcount++;
+ }
+
+ /* Decrease the refcount. */
+ void decref ()
+ {
+ m_refcount--;
+ gdb_assert (m_refcount >= 0);
+ }
+
struct thread_info *next = NULL;
ptid_t ptid; /* "Actual process id";
In fact, this may be overloaded with
but STATE will still be THREAD_RUNNING. */
enum thread_state state = THREAD_STOPPED;
- /* If this is > 0, then it means there's code out there that relies
- on this thread being listed. Don't delete it from the lists even
- if we detect it exiting. */
- int refcount = 0;
-
/* State of GDB control of inferior thread execution.
See `struct thread_control_state'. */
thread_control_state control {};
fields point to self. */
struct thread_info *step_over_prev = NULL;
struct thread_info *step_over_next = NULL;
+
+private:
+
+ /* If this is > 0, then it means there's code out there that relies
+ on this thread being listed. Don't delete it from the lists even
+ if we detect it exiting. */
+ int m_refcount = 0;
};
/* Create an empty thread list, or empty the existing one. */
thread_cancel_execution_command (tp);
}
+/* Set the TP's state as exited. */
+
+static void
+set_thread_exited (thread_info *tp, int silent)
+{
+ /* Dead threads don't need to step-over. Remove from queue. */
+ if (tp->step_over_next != NULL)
+ thread_step_over_chain_remove (tp);
+
+ if (tp->state != THREAD_EXITED)
+ {
+ observer_notify_thread_exit (tp, silent);
+
+ /* Tag it as exited. */
+ tp->state = THREAD_EXITED;
+
+ /* Clear breakpoints, etc. associated with this thread. */
+ clear_thread_inferior_resources (tp);
+ }
+}
+
void
init_thread_list (void)
{
for (tp = thread_list; tp; tp = tpnext)
{
tpnext = tp->next;
- delete tp;
+ if (tp->deletable ())
+ delete tp;
+ else
+ set_thread_exited (tp, 1);
}
thread_list = NULL;
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);
+ set_thread_exited (tp, silent);
- if (tp->state != THREAD_EXITED)
- {
- observer_notify_thread_exit (tp, silent);
-
- /* Tag it as exited. */
- tp->state = THREAD_EXITED;
-
- /* Clear breakpoints, etc. associated with this thread. */
- clear_thread_inferior_resources (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. */
- if (tp->refcount > 0
- || ptid_equal (tp->ptid, inferior_ptid))
+ if (!tp->deletable ())
{
/* Will be really deleted some other time. */
return;
'current_thread_cleanup_chain' below. */
struct current_thread_cleanup *next;
- ptid_t inferior_ptid;
+ thread_info *thread;
struct frame_id selected_frame_id;
int selected_frame_level;
int was_stopped;
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 current_thread_cleanup *old = (struct current_thread_cleanup *) arg;
- tp = find_thread_ptid (old->inferior_ptid);
-
- /* If the previously selected thread belonged to a process that has
- in the mean time been deleted (due to normal exit, detach, etc.),
- then don't revert back to it, but instead simply drop back to no
- thread selected. */
- if (tp
- && find_inferior_ptid (tp->ptid) != NULL)
- restore_current_thread (old->inferior_ptid);
+ /* If an entry of thread_info was previously selected, it won't be
+ deleted because we've increased its refcount. The thread represented
+ by this thread_info entry may have already exited (due to normal exit,
+ detach, etc), so the thread_info.state is THREAD_EXITED. */
+ if (old->thread != NULL
+ /* If the previously selected thread belonged to a process that has
+ in the mean time exited (or killed, detached, etc.), then don't revert
+ back to it, but instead simply drop back to no thread selected. */
+ && find_inferior_ptid (old->thread->ptid) != NULL)
+ restore_current_thread (old->thread->ptid);
else
{
restore_current_thread (null_ptid);
current_thread_cleanup_chain = current_thread_cleanup_chain->next;
- tp = find_thread_ptid (old->inferior_ptid);
- if (tp)
- tp->refcount--;
+ if (old->thread != NULL)
+ old->thread->decref ();
+
inf = find_inferior_id (old->inf_id);
if (inf != NULL)
inf->removable = old->was_removable;
= (struct thread_array_cleanup *) data;
for (k = 0; k != ta_cleanup->count; k++)
- ta_cleanup->tp_array[k]->refcount--;
+ ta_cleanup->tp_array[k]->decref ();
}
struct cleanup *
{
struct current_thread_cleanup *old = XNEW (struct current_thread_cleanup);
- old->inferior_ptid = inferior_ptid;
+ old->thread = NULL;
old->inf_id = current_inferior ()->num;
old->was_removable = current_inferior ()->removable;
struct thread_info *tp = find_thread_ptid (inferior_ptid);
if (tp)
- tp->refcount++;
+ tp->incref ();
+ old->thread = tp;
}
current_inferior ()->removable = 0;
ALL_NON_EXITED_THREADS (tp)
{
tp_array[i] = tp;
- tp->refcount++;
+ tp->incref ();
i++;
}
/* Because we skipped exited threads, we may end up with fewer
create_internalvar_type_lazy ("_thread", &thread_funcs, NULL);
create_internalvar_type_lazy ("_gthread", >hread_funcs, NULL);
-
- observer_attach_thread_ptid_changed (restore_current_thread_ptid_changed);
}