2011-05-30 Pedro Alves <pedro@codesourcery.com>
authorPedro Alves <palves@redhat.com>
Mon, 30 May 2011 18:04:32 +0000 (18:04 +0000)
committerPedro Alves <palves@redhat.com>
Mon, 30 May 2011 18:04:32 +0000 (18:04 +0000)
gdb/
* continuations.h (continuation_ftype): Add `err' parameter.
Document parameters.
(do_all_continuations, do_all_continuations_thread)
(do_all_intermediate_continuations)
(do_all_intermediate_continuations_thread)
(do_all_inferior_continuations): Add `err' parameter.
* continuations.c (do_my_continuations_1, do_my_continuations)
(do_all_inferior_continuations, do_all_continuations_ptid)
(do_all_continuations_thread_callback)
(do_all_continuations_thread, do_all_continuations)
(do_all_intermediate_continuations_thread_callback)
(do_all_intermediate_continuations_thread)
(do_all_intermediate_continuations): Add `err' parameter, and pass
it down all the way to the continuations proper.
* inf-loop.c (inferior_event_handler): If fetching an inferior
event throws an error, don't pop the target, and still call the
continuations, but with `err' set.  Adjust all other continuation
calls.
* breakpoint.c (until_break_command_continuation): Add `err'
parameter.
* infcmd.c (step_1_continuation): Add `err' parameter.  Don't
issue another step if `err' is set.
(struct until_next_continuation_args): New.
(until_next_continuation): Add `err' parameter.  Adjust.
(until_next_command): Adjust.
(struct finish_command_continuation_args): Add `thread' field.
(finish_command_continuation): Add `err' parameter.  Handle it.
(finish_forward): Adjust.
(attach_command_continuation): Add `err' parameter.  Handle it.
* infrun.c (infrun_thread_stop_requested_callback): Adjust to
cancel the continuations.
* interps.c (interp_set): Adjust to cancel the continuations.
* thread.c (clear_thread_inferior_resources): Adjust to cancel the
continuations rather than discarding.
(free_thread): Don't clear thread inferior resources here.
(delete_thread_1): Do it here instead.  And do it before removing
the thread from the threads list.  Tag the thread as exited before
clearing thread inferior resources.

gdb/ChangeLog
gdb/breakpoint.c
gdb/continuations.c
gdb/continuations.h
gdb/inf-loop.c
gdb/infcmd.c
gdb/infrun.c
gdb/interps.c
gdb/thread.c

index 7a60b8e3cf50a78023950126bde082739a04385b..17db1752743aa477a6f8895fba52cc64c5abb126 100644 (file)
@@ -1,3 +1,44 @@
+2011-05-30  Pedro Alves  <pedro@codesourcery.com>
+
+       * continuations.h (continuation_ftype): Add `err' parameter.
+       Document parameters.
+       (do_all_continuations, do_all_continuations_thread)
+       (do_all_intermediate_continuations)
+       (do_all_intermediate_continuations_thread)
+       (do_all_inferior_continuations): Add `err' parameter.
+       * continuations.c (do_my_continuations_1, do_my_continuations)
+       (do_all_inferior_continuations, do_all_continuations_ptid)
+       (do_all_continuations_thread_callback)
+       (do_all_continuations_thread, do_all_continuations)
+       (do_all_intermediate_continuations_thread_callback)
+       (do_all_intermediate_continuations_thread)
+       (do_all_intermediate_continuations): Add `err' parameter, and pass
+       it down all the way to the continuations proper.
+       * inf-loop.c (inferior_event_handler): If fetching an inferior
+       event throws an error, don't pop the target, and still call the
+       continuations, but with `err' set.  Adjust all other continuation
+       calls.
+       * breakpoint.c (until_break_command_continuation): Add `err'
+       parameter.
+       * infcmd.c (step_1_continuation): Add `err' parameter.  Don't
+       issue another step if `err' is set.
+       (struct until_next_continuation_args): New.
+       (until_next_continuation): Add `err' parameter.  Adjust.
+       (until_next_command): Adjust.
+       (struct finish_command_continuation_args): Add `thread' field.
+       (finish_command_continuation): Add `err' parameter.  Handle it.
+       (finish_forward): Adjust.
+       (attach_command_continuation): Add `err' parameter.  Handle it.
+       * infrun.c (infrun_thread_stop_requested_callback): Adjust to
+       cancel the continuations.
+       * interps.c (interp_set): Adjust to cancel the continuations.
+       * thread.c (clear_thread_inferior_resources): Adjust to cancel the
+       continuations rather than discarding.
+       (free_thread): Don't clear thread inferior resources here.
+       (delete_thread_1): Do it here instead.  And do it before removing
+       the thread from the threads list.  Tag the thread as exited before
+       clearing thread inferior resources.
+
 2011-05-30  Joel Brobecker  <brobecker@adacore.com>
 
        * infcall.c (call_function_by_hand): Rephrase error message.
index d26a347ba1aec2fe3d5894e8d8ed9d25192c2a25..fe797dd31403f983db135f5ef6e948461d8fa873 100644 (file)
@@ -9508,7 +9508,7 @@ struct until_break_command_continuation_args
    care of cleaning up the temporary breakpoints set up by the until
    command.  */
 static void
-until_break_command_continuation (void *arg)
+until_break_command_continuation (void *arg, int err)
 {
   struct until_break_command_continuation_args *a = arg;
 
index c6f45a23e8f74ef323c65a61c6ba11363cf5c719..0ad3184702ba9d6bf10d0e8dd1049e6cf9bd1143 100644 (file)
@@ -51,14 +51,14 @@ make_continuation (struct continuation **pmy_chain,
 }
 
 static void
-do_my_continuations_1 (struct continuation **pmy_chain)
+do_my_continuations_1 (struct continuation **pmy_chain, int err)
 {
   struct continuation *ptr;
 
   while ((ptr = *pmy_chain) != NULL)
     {
       *pmy_chain = ptr->next;  /* Do this first in case of recursion.  */
-      (*ptr->function) (ptr->arg);
+      (*ptr->function) (ptr->arg, err);
       if (ptr->free_arg)
        (*ptr->free_arg) (ptr->arg);
       xfree (ptr);
@@ -66,7 +66,7 @@ do_my_continuations_1 (struct continuation **pmy_chain)
 }
 
 static void
-do_my_continuations (struct continuation **list)
+do_my_continuations (struct continuation **list, int err)
 {
   struct continuation *continuations;
 
@@ -82,7 +82,7 @@ do_my_continuations (struct continuation **list)
   *list = NULL;
 
   /* Work now on the list we have set aside.  */
-  do_my_continuations_1 (&continuations);
+  do_my_continuations_1 (&continuations, err);
 }
 
 static void
@@ -123,10 +123,10 @@ add_inferior_continuation (continuation_ftype *hook, void *args,
 /* Do all continuations of the current inferior.  */
 
 void
-do_all_inferior_continuations (void)
+do_all_inferior_continuations (int err)
 {
   struct inferior *inf = current_inferior ();
-  do_my_continuations (&inf->continuations);
+  do_my_continuations (&inf->continuations, err);
 }
 
 /* Get rid of all the inferior-wide continuations of INF.  */
@@ -167,7 +167,8 @@ restore_thread_cleanup (void *arg)
 
 static void
 do_all_continuations_ptid (ptid_t ptid,
-                          struct continuation **continuations_p)
+                          struct continuation **continuations_p,
+                          int err)
 {
   struct cleanup *old_chain;
   ptid_t current_thread;
@@ -191,7 +192,7 @@ do_all_continuations_ptid (ptid_t ptid,
   /* Let the continuation see this thread as selected.  */
   switch_to_thread (ptid);
 
-  do_my_continuations (continuations_p);
+  do_my_continuations (continuations_p, err);
 
   do_cleanups (old_chain);
 }
@@ -201,24 +202,25 @@ do_all_continuations_ptid (ptid_t ptid,
 static int
 do_all_continuations_thread_callback (struct thread_info *thread, void *data)
 {
-  do_all_continuations_ptid (thread->ptid, &thread->continuations);
+  int err = * (int *) data;
+  do_all_continuations_ptid (thread->ptid, &thread->continuations, err);
   return 0;
 }
 
 /* Do all continuations of thread THREAD.  */
 
 void
-do_all_continuations_thread (struct thread_info *thread)
+do_all_continuations_thread (struct thread_info *thread, int err)
 {
-  do_all_continuations_thread_callback (thread, NULL);
+  do_all_continuations_thread_callback (thread, &err);
 }
 
 /* Do all continuations of all threads.  */
 
 void
-do_all_continuations (void)
+do_all_continuations (int err)
 {
-  iterate_over_threads (do_all_continuations_thread_callback, NULL);
+  iterate_over_threads (do_all_continuations_thread_callback, &err);
 }
 
 /* Callback for iterate over threads.  */
@@ -274,26 +276,28 @@ static int
 do_all_intermediate_continuations_thread_callback (struct thread_info *thread,
                                                   void *data)
 {
+  int err = * (int *) data;
+
   do_all_continuations_ptid (thread->ptid,
-                            &thread->intermediate_continuations);
+                            &thread->intermediate_continuations, err);
   return 0;
 }
 
 /* Do all intermediate continuations of thread THREAD.  */
 
 void
-do_all_intermediate_continuations_thread (struct thread_info *thread)
+do_all_intermediate_continuations_thread (struct thread_info *thread, int err)
 {
-  do_all_intermediate_continuations_thread_callback (thread, NULL);
+  do_all_intermediate_continuations_thread_callback (thread, &err);
 }
 
 /* Do all intermediate continuations of all threads.  */
 
 void
-do_all_intermediate_continuations (void)
+do_all_intermediate_continuations (int err)
 {
   iterate_over_threads (do_all_intermediate_continuations_thread_callback,
-                       NULL);
+                       &err);
 }
 
 /* Callback for iterate over threads.  */
index aaba362f0b9bee4865a2faf483941c2bb3c3faf3..bf3b516af7a6c1a2d43ee080f4699f916d64524d 100644 (file)
@@ -30,8 +30,16 @@ struct inferior;
    used by the finish and until commands, and in the remote protocol
    when opening an extended-remote connection.  */
 
-/* Prototype of the continuation callback functions.  */
-typedef void (continuation_ftype) (void *);
+/* Prototype of the continuation callback functions.  ARG is the
+   continuation argument registered in the corresponding
+   add_*_continuation call.  ERR is true when the command should be
+   cancelled instead of finished normally.  In that case, the
+   continuation should clean up whatever state had been set up for the
+   command in question (e.g., remove momentary breakpoints).  This
+   happens e.g., when an error was thrown while handling a target
+   event, or when the inferior thread the command was being executed
+   on exits.  */
+typedef void (continuation_ftype) (void *arg, int err);
 
 /* Prototype of the function responsible for releasing the argument
    passed to the continuation callback functions, either when the
@@ -43,16 +51,16 @@ typedef void (continuation_free_arg_ftype) (void *);
 extern void add_continuation (struct thread_info *,
                              continuation_ftype *, void *,
                              continuation_free_arg_ftype *);
-extern void do_all_continuations (void);
-extern void do_all_continuations_thread (struct thread_info *);
+extern void do_all_continuations (int err);
+extern void do_all_continuations_thread (struct thread_info *, int err);
 extern void discard_all_continuations (void);
 extern void discard_all_continuations_thread (struct thread_info *);
 
 extern void add_intermediate_continuation (struct thread_info *,
                                           continuation_ftype *, void *,
                                           continuation_free_arg_ftype *);
-extern void do_all_intermediate_continuations (void);
-extern void do_all_intermediate_continuations_thread (struct thread_info *);
+extern void do_all_intermediate_continuations (int err);
+extern void do_all_intermediate_continuations_thread (struct thread_info *, int err);
 extern void discard_all_intermediate_continuations (void);
 extern void discard_all_intermediate_continuations_thread (struct thread_info *);
 
@@ -61,7 +69,7 @@ extern void discard_all_intermediate_continuations_thread (struct thread_info *)
 extern void add_inferior_continuation (continuation_ftype *,
                                       void *,
                                       continuation_free_arg_ftype *);
-extern void do_all_inferior_continuations (void);
+extern void do_all_inferior_continuations (int err);
 extern void discard_all_inferior_continuations (struct inferior *inf);
 
 #endif
index 1645c38b6e1889eeffc1468b932ea406ffb2e2b1..0ab372c784d3cf86683fa537ff5d3afe12887a6a 100644 (file)
@@ -63,13 +63,12 @@ inferior_event_handler (enum inferior_event_type event_type,
       /* Use catch errors for now, until the inner layers of
         fetch_inferior_event (i.e. readchar) can return meaningful
         error status.  If an error occurs while getting an event from
-        the target, just get rid of the target.  */
+        the target, just cancel the current command.  */
       if (!catch_errors (fetch_inferior_event_wrapper, 
                         client_data, "", RETURN_MASK_ALL))
        {
-         pop_all_targets_above (file_stratum, 0);
-         discard_all_intermediate_continuations ();
-         discard_all_continuations ();
+         do_all_intermediate_continuations (1);
+         do_all_continuations (1);
          async_enable_stdin ();
          display_gdb_prompt (0);
        }
@@ -95,7 +94,7 @@ inferior_event_handler (enum inferior_event_type event_type,
       /* Do all continuations associated with the whole inferior (not
         a particular thread).  */
       if (!ptid_equal (inferior_ptid, null_ptid))
-       do_all_inferior_continuations ();
+       do_all_inferior_continuations (0);
 
       /* If we were doing a multi-step (eg: step n, next n), but it
         got interrupted by a breakpoint, still do the pending
@@ -107,9 +106,9 @@ inferior_event_handler (enum inferior_event_type event_type,
       if (non_stop
          && target_has_execution
          && !ptid_equal (inferior_ptid, null_ptid))
-       do_all_intermediate_continuations_thread (inferior_thread ());
+       do_all_intermediate_continuations_thread (inferior_thread (), 0);
       else
-       do_all_intermediate_continuations ();
+       do_all_intermediate_continuations (0);
 
       /* Always finish the previous command before running any
         breakpoint commands.  Any stop cancels the previous command.
@@ -118,9 +117,9 @@ inferior_event_handler (enum inferior_event_type event_type,
       if (non_stop
          && target_has_execution
          && !ptid_equal (inferior_ptid, null_ptid))
-       do_all_continuations_thread (inferior_thread ());
+       do_all_continuations_thread (inferior_thread (), 0);
       else
-       do_all_continuations ();
+       do_all_continuations (0);
 
       if (info_verbose
          && current_language != expected_language
@@ -147,9 +146,9 @@ inferior_event_handler (enum inferior_event_type event_type,
          complete?  */
 
       if (non_stop)
-       do_all_intermediate_continuations_thread (inferior_thread ());
+       do_all_intermediate_continuations_thread (inferior_thread (), 0);
       else
-       do_all_intermediate_continuations ();
+       do_all_intermediate_continuations (0);
       break;
 
     case INF_QUIT_REQ: 
index f499e3e64bf44b5b48275b31f9c6c40fa54ff4f5..aa3646b483c938b8d8bccb0722ef4b8141f04686 100644 (file)
@@ -940,7 +940,7 @@ struct step_1_continuation_args
    proceed(), via step_once().  Basically it is like step_once and
    step_1_continuation are co-recursive.  */
 static void
-step_1_continuation (void *args)
+step_1_continuation (void *args, int err)
 {
   struct step_1_continuation_args *a = args;
 
@@ -949,7 +949,8 @@ step_1_continuation (void *args)
       struct thread_info *tp;
 
       tp = inferior_thread ();
-      if (tp->step_multi && tp->control.stop_step)
+      if (!err
+         && tp->step_multi && tp->control.stop_step)
        {
          /* There are more steps to make, and we did stop due to
             ending a stepping range.  Do another step.  */
@@ -960,8 +961,9 @@ step_1_continuation (void *args)
       tp->step_multi = 0;
     }
 
-  /* We either stopped for some reason that is not stepping, or there
-     are no further steps to make.  Cleanup.  */
+  /* We either hit an error, or stopped for some reason that is
+     not stepping, or there are no further steps to make.
+     Cleanup.  */
   if (!a->single_inst || a->skip_subroutines)
     delete_longjmp_breakpoint (a->thread);
 }
@@ -1246,14 +1248,22 @@ signal_command (char *signum_exp, int from_tty)
   proceed ((CORE_ADDR) -1, oursig, 0);
 }
 
+/* Continuation args to be passed to the "until" command
+   continuation.  */
+struct until_next_continuation_args
+{
+  /* The thread that was current when the command was executed.  */
+  int thread;
+};
+
 /* A continuation callback for until_next_command.  */
 
 static void
-until_next_continuation (void *arg)
+until_next_continuation (void *arg, int err)
 {
-  struct thread_info *tp = arg;
+  struct until_next_continuation_args *a = arg;
 
-  delete_longjmp_breakpoint (tp->num);
+  delete_longjmp_breakpoint (a->thread);
 }
 
 /* Proceed until we reach a different source line with pc greater than
@@ -1316,8 +1326,13 @@ until_next_command (int from_tty)
 
   if (target_can_async_p () && is_running (inferior_ptid))
     {
+      struct until_next_continuation_args *cont_args;
+
       discard_cleanups (old_chain);
-      add_continuation (tp, until_next_continuation, tp, NULL);
+      cont_args = XNEW (struct until_next_continuation_args);
+      cont_args->thread = inferior_thread ()->num;
+
+      add_continuation (tp, until_next_continuation, cont_args, xfree);
     }
   else
     do_cleanups (old_chain);
@@ -1458,62 +1473,69 @@ print_return_value (struct type *func_type, struct type *value_type)
    impossible to do all the stuff as part of the finish_command
    function itself.  The only chance we have to complete this command
    is in fetch_inferior_event, which is called by the event loop as
-   soon as it detects that the target has stopped.  This function is
-   called via the cmd_continuation pointer.  */
+   soon as it detects that the target has stopped.  */
 
 struct finish_command_continuation_args
 {
+  /* The thread that as current when the command was executed.  */
+  int thread;
   struct breakpoint *breakpoint;
   struct symbol *function;
 };
 
 static void
-finish_command_continuation (void *arg)
+finish_command_continuation (void *arg, int err)
 {
   struct finish_command_continuation_args *a = arg;
-  struct thread_info *tp = NULL;
-  bpstat bs = NULL;
-
-  if (!ptid_equal (inferior_ptid, null_ptid)
-      && target_has_execution
-      && is_stopped (inferior_ptid))
-    {
-      tp = inferior_thread ();
-      bs = tp->control.stop_bpstat;
-    }
 
-  if (bpstat_find_breakpoint (bs, a->breakpoint) != NULL
-      && a->function != NULL)
+  if (!err)
     {
-      struct type *value_type;
+      struct thread_info *tp = NULL;
+      bpstat bs = NULL;
 
-      value_type = TYPE_TARGET_TYPE (SYMBOL_TYPE (a->function));
-      if (!value_type)
-       internal_error (__FILE__, __LINE__,
-                       _("finish_command: function has no target type"));
+      if (!ptid_equal (inferior_ptid, null_ptid)
+         && target_has_execution
+         && is_stopped (inferior_ptid))
+       {
+         tp = inferior_thread ();
+         bs = tp->control.stop_bpstat;
+       }
 
-      if (TYPE_CODE (value_type) != TYPE_CODE_VOID)
+      if (bpstat_find_breakpoint (bs, a->breakpoint) != NULL
+         && a->function != NULL)
        {
-         volatile struct gdb_exception ex;
+         struct type *value_type;
+
+         value_type = TYPE_TARGET_TYPE (SYMBOL_TYPE (a->function));
+         if (!value_type)
+           internal_error (__FILE__, __LINE__,
+                           _("finish_command: function has no target type"));
 
-         TRY_CATCH (ex, RETURN_MASK_ALL)
+         if (TYPE_CODE (value_type) != TYPE_CODE_VOID)
            {
-             /* print_return_value can throw an exception in some
-                circumstances.  We need to catch this so that we still
-                delete the breakpoint.  */
-             print_return_value (SYMBOL_TYPE (a->function), value_type);
+             volatile struct gdb_exception ex;
+
+             TRY_CATCH (ex, RETURN_MASK_ALL)
+               {
+                 /* print_return_value can throw an exception in some
+                    circumstances.  We need to catch this so that we still
+                    delete the breakpoint.  */
+                 print_return_value (SYMBOL_TYPE (a->function), value_type);
+               }
+             if (ex.reason < 0)
+               exception_print (gdb_stdout, ex);
            }
-         if (ex.reason < 0)
-           exception_print (gdb_stdout, ex);
        }
+
+      /* We suppress normal call of normal_stop observer and do it
+        here so that the *stopped notification includes the return
+        value.  */
+      if (bs != NULL && tp->control.proceed_to_finish)
+       observer_notify_normal_stop (bs, 1 /* print frame */);
     }
 
-  /* We suppress normal call of normal_stop observer and do it here so
-     that the *stopped notification includes the return value.  */
-  if (bs != NULL && tp->control.proceed_to_finish)
-    observer_notify_normal_stop (bs, 1 /* print frame */);
   delete_breakpoint (a->breakpoint);
-  delete_longjmp_breakpoint (inferior_thread ()->num);
+  delete_longjmp_breakpoint (a->thread);
 }
 
 static void
@@ -1604,6 +1626,7 @@ finish_forward (struct symbol *function, struct frame_info *frame)
   tp->control.proceed_to_finish = 1;
   cargs = xmalloc (sizeof (*cargs));
 
+  cargs->thread = thread;
   cargs->breakpoint = breakpoint;
   cargs->function = function;
   add_continuation (tp, finish_command_continuation, cargs,
@@ -1612,7 +1635,7 @@ finish_forward (struct symbol *function, struct frame_info *frame)
 
   discard_cleanups (old_chain);
   if (!target_can_async_p ())
-    do_all_continuations ();
+    do_all_continuations (0);
 }
 
 /* "finish": Set a temporary breakpoint at the place the selected
@@ -2405,10 +2428,13 @@ struct attach_command_continuation_args
 };
 
 static void
-attach_command_continuation (void *args)
+attach_command_continuation (void *args, int err)
 {
   struct attach_command_continuation_args *a = args;
 
+  if (err)
+    return;
+
   attach_command_post_wait (a->args, a->from_tty, a->async_exec);
 }
 
index efc35f96f8a7ec2620a117d53a8d935dcc0f0bff..e0d808ae255ec0bfd61e071270cc49480c1fc567 100644 (file)
@@ -2393,12 +2393,10 @@ infrun_thread_stop_requested_callback (struct thread_info *info, void *arg)
 
          normal_stop ();
 
-         /* Finish off the continuations.  The continations
-            themselves are responsible for realising the thread
-            didn't finish what it was supposed to do.  */
+         /* Finish off the continuations.  */
          tp = inferior_thread ();
-         do_all_intermediate_continuations_thread (tp);
-         do_all_continuations_thread (tp);
+         do_all_intermediate_continuations_thread (tp, 1);
+         do_all_continuations_thread (tp, 1);
        }
 
       do_cleanups (old_chain);
index bd9a352ce2c07eb4dbddea30365034f202f51a45..2fbd855c81df2164ae03eb23067acfafad88bba0 100644 (file)
@@ -150,7 +150,7 @@ interp_set (struct interp *interp, int top_level)
 
   if (current_interpreter != NULL)
     {
-      do_all_continuations ();
+      do_all_continuations (1);
       ui_out_flush (uiout);
       if (current_interpreter->procs->suspend_proc
          && !current_interpreter->procs->suspend_proc (current_interpreter->
index 3090b692556ffaa0c9ff9096be5bc59855274daf..5a78ad17025bced7ba304664f8ac2eff07b1058f 100644 (file)
@@ -125,8 +125,8 @@ clear_thread_inferior_resources (struct thread_info *tp)
 
   bpstat_clear (&tp->control.stop_bpstat);
 
-  discard_all_intermediate_continuations_thread (tp);
-  discard_all_continuations_thread (tp);
+  do_all_intermediate_continuations_thread (tp, 1);
+  do_all_continuations_thread (tp, 1);
 
   delete_longjmp_breakpoint (tp->num);
 }
@@ -134,8 +134,6 @@ clear_thread_inferior_resources (struct thread_info *tp)
 static void
 free_thread (struct thread_info *tp)
 {
-  clear_thread_inferior_resources (tp);
-
   if (tp->private)
     {
       if (tp->private_dtor)
@@ -297,15 +295,19 @@ delete_thread_1 (ptid_t ptid, int silent)
        return;
      }
 
+  /* Notify thread exit, but only if we haven't already.  */
+  if (tp->state_ != THREAD_EXITED)
+    observer_notify_thread_exit (tp, silent);
+
+  /* Tag it as exited.  */
+  tp->state_ = THREAD_EXITED;
+  clear_thread_inferior_resources (tp);
+
   if (tpprev)
     tpprev->next = tp->next;
   else
     thread_list = tp->next;
 
-  /* Notify thread exit, but only if we haven't already.  */
-  if (tp->state_ != THREAD_EXITED)
-    observer_notify_thread_exit (tp, silent);
-
   free_thread (tp);
 }