This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [WIP/FYI] fix remaining problems with target async


On Mon, 13 Jun 2011 16:16:51 +0200, Pedro Alves wrote:
> Rather than leaving it rotting on my drive, I figure I'd post it, and
> perhaps get some early comments.

Posting a purely mechanical rebase to HEAD.

By a brief reading I do not see there any problem, I also do not see any
regressions.  It for example fixes:
./gdb -nx ./gdb -ex 'set target-async on' -ex start -ex step
Cannot execute this command while the selected thread is running.

I had difficulties developing the async part of the bpstat_clear_actions patch
as one has to avoid various async bugs in the current code.

Unless you have very specific plans with it I would find it as a nice
incremental improvement (+few comments for the new objects would be
appropriate).


Thanks,
Jan


--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -9531,15 +9531,20 @@ struct until_break_command_continuation_args
    cmd_continuation pointer, to complete the until command.  It takes
    care of cleaning up the temporary breakpoints set up by the until
    command.  */
-static void
+static int
 until_break_command_continuation (void *arg, int err)
 {
   struct until_break_command_continuation_args *a = arg;
 
+  if (!err)
+    normal_stop ();
+
   delete_breakpoint (a->breakpoint);
   if (a->breakpoint2)
     delete_breakpoint (a->breakpoint2);
   delete_longjmp_breakpoint (a->thread_num);
+
+  return 1;
 }
 
 void
@@ -9612,14 +9617,14 @@ until_break_command (char *arg, int from_tty, int anywhere)
 
   proceed (-1, TARGET_SIGNAL_DEFAULT, 0);
 
-  /* If we are running asynchronously, and proceed call above has
-     actually managed to start the target, arrange for breakpoints to
-     be deleted when the target stops.  Otherwise, we're already
-     stopped and delete breakpoints via cleanup chain.  */
-
-  if (target_can_async_p () && is_running (inferior_ptid))
+  /* If the proceed call above has actually managed to start the
+     target, arrange for breakpoints to be deleted when the target
+     stops.  Otherwise, we're already stopped and delete breakpoints
+     via cleanup chain.  */
+  if (is_running (inferior_ptid))
     {
       struct until_break_command_continuation_args *args;
+
       args = xmalloc (sizeof (*args));
 
       args->breakpoint = breakpoint;
@@ -9627,9 +9632,14 @@ until_break_command (char *arg, int from_tty, int anywhere)
       args->thread_num = thread;
 
       discard_cleanups (old_chain);
-      add_continuation (inferior_thread (),
-			until_break_command_continuation, args,
+      add_continuation (tp, until_break_command_continuation, args,
 			xfree);
+
+      if (!target_can_async_p ())
+	{
+	  extern void sync_run_command (void);
+	  sync_run_command ();
+	}
     }
   else
     do_cleanups (old_chain);
--- a/gdb/cli/cli-script.c
+++ b/gdb/cli/cli-script.c
@@ -36,6 +36,11 @@
 
 #include "python/python.h"
 
+#include "gdbthread.h"
+#include "observer.h"
+
+#include "continuations.h"
+
 /* Prototypes for local functions.  */
 
 static enum command_control_type
@@ -59,6 +64,32 @@ static int command_nest_depth = 1;
 /* This is to prevent certain commands being printed twice.  */
 static int suppress_next_print_command_trace = 0;
 
+/* Flag indicating that a command has proceeded the inferior.  */
+
+static int command_proceeded;
+
+/* Called when a command is about to proceed the inferior.  */
+
+static void
+cli_script_about_to_proceed (void)
+{
+  if (!ptid_equal (inferior_ptid, null_ptid))
+    {
+      struct thread_info *tp = inferior_thread ();
+
+      /* Allow inferior function calls in breakpoint commands to not
+	 interrupt the command list.  When the call finishes
+	 successfully, the inferior will be standing at the same
+	 breakpoint as if nothing happened.  */
+      if (tp->control.in_infcall)
+	return;
+    }
+
+  command_proceeded = 1;
+}
+
+
+
 /* Structure for arguments to user defined functions.  */
 #define MAXUSERARGS 10
 struct user_args
@@ -296,60 +327,329 @@ execute_cmd_post_hook (struct cmd_list_element *c)
     }
 }
 
-/* Execute the command in CMD.  */
+struct command_stack_entry
+{
+  struct command_line *cmd;
+  struct expression *expr;
+
+  struct command_stack_entry *prev;
+};
+
+struct continuation_args
+{
+  char *args;
+  int command_nest_depth;
+  int user_call_depth;
+  FILE *instream;
+  int in_user_command;
+
+  struct command_stack_entry *top;
+};
+
+static int user_call_depth = 0;
+extern int max_user_call_depth;
+
+static struct command_stack_entry *
+new_command_list (struct command_stack_entry *previous_top,
+		  struct command_line *cmdlist)
+{
+  /* New list to execute.  */
+  struct command_stack_entry *top;
+
+  top = XNEW (struct command_stack_entry);
+  top->prev = previous_top;
+  top->cmd = cmdlist;
+  return top;
+}
+
+/* A `while' or `if' have an associated expression.  This evaluates
+   it.  */
+
+static int
+eval_cmd_condition (struct command_stack_entry *top)
+{
+  int cond_result;
+  struct value *val;
+  struct value *val_mark;
+
+  gdb_assert (top->expr);
+
+  /* Evaluate the expression.  */
+  val_mark = value_mark ();
+  val = evaluate_expression (top->expr);
+  cond_result = value_true (val);
+  value_free_to_mark (val_mark);
+
+  return cond_result;
+}
+
 static void
-do_restore_user_call_depth (void * call_depth)
-{	
-  int *depth = call_depth;
+pop_stack (struct continuation_args *args)
+{
+  struct command_stack_entry *old_top;
+
+  old_top = args->top;
+  args->top = args->top->prev;
+  xfree (old_top);
+}
+
+static enum command_control_type execute_control_command_1 (struct command_line *cmd);
+
+/* A continuation callback for execute_user_command.  */
+
+static int
+do_command_list_1 (struct continuation_args *args)
+{
+  /* Execute the command list.  */
+ again:
+  while (args->top->cmd)
+    {
+      enum command_control_type ret;
+
+      command_proceeded = 0;
+
+      ret = execute_control_command_1 (args->top->cmd);
+
+      if (ret == while_control || ret == if_control)
+	{
+	  struct cleanup *old_chain;
+	  char *new_line;
+
+	  /* Parse the loop control expression for the while
+	     statement, or the conditional for the if statement.  */
+	  new_line = insert_args (args->top->cmd->line);
+	  if (!new_line)
+	    {
+	      ret = invalid_control;
+	      while (args->top)
+		{
+		  pop_stack (args);
+		  command_nest_depth--;
+		}
+	      return 0;
+	    }
+
+	  old_chain = make_cleanup (free_current_contents, &new_line);
+	  args->top->expr = parse_expression (new_line);
+	  do_cleanups (old_chain);
+
+	  if (ret == while_control)
+	    {
+	      if (eval_cmd_condition (args->top))
+		{
+		  /* Execute while body.  */
+		  command_nest_depth++;
+		  args->top = new_command_list (args->top,
+						*args->top->cmd->body_list);
+		  goto again;
+		}
+	    }
+	  else if (ret == if_control)
+	    {
+	      struct command_line *current = NULL;
+
+	      if (eval_cmd_condition (args->top))
+		current = args->top->cmd->body_list[0];
+	      else if (args->top->cmd->body_count == 2)
+		current = args->top->cmd->body_list[1];
+
+	      if (current != NULL)
+		{
+		  /* Execute "then" body.  */
+		  command_nest_depth++;
+		  args->top = new_command_list (args->top, current);
+		  goto again;
+		}
+	      else
+		{
+		  ret = simple_control;
+		}
+	    }
+	}
+
+      if (args->top->prev
+	  && args->top->prev->cmd->control_type == while_control)
+	{
+	  /* If we got a "break" command, then stop looping.  */
+	  if (ret == break_control)
+	    {
+	      pop_stack (args);
+	      command_nest_depth--;
+	      goto again;
+	    }
+
+	  /* If we got a "continue" command, then restart the loop at
+	     this point.  */
+	  if (ret == continue_control)
+	    {
+	      pop_stack (args);
+	      command_nest_depth--;
+
+	      if (eval_cmd_condition (args->top))
+		{
+		  /* Re-execute while body.  */
+		  args->top = new_command_list (args->top,
+						*args->top->cmd->body_list);
+		  command_nest_depth++;
+		}
+	      goto again;
+	    }
+	}
+
+      /* If we got an error, get out.  */
+      if (ret != simple_control)
+	{
+	  while (args->top)
+	    {
+	      pop_stack (args);
+	      command_nest_depth--;
+	    }
+	  return 0;
+	}
 
-  (*depth)--;
-  if ((*depth) == 0)
-    in_user_command = 0;
+      /* Get the next statement.  */
+      args->top->cmd = args->top->cmd->next;
+
+      /* If the command started the target synchronously, go back to
+	 the even loop waiting for the command to finish, before
+	 moving on to the following command.  */
+      if (command_proceeded && sync_execution && target_is_async_p ())
+	return 1;
+    }
+
+  /* If we were executing a while, try one more iteration.  */
+  if (args->top->prev
+      && args->top->prev->cmd->control_type == while_control
+      && eval_cmd_condition (args->top->prev))
+    {
+      /* Re-execute while body.  */
+      args->top->cmd = *args->top->prev->cmd->body_list;
+      goto again;
+    }
+  else
+    {
+      /* Reached the end of the command list.  */
+      pop_stack (args);
+      command_nest_depth--;
+
+      /* Move on to the following command.  */
+      if (args->top != NULL)
+	{
+	  args->top->cmd = args->top->cmd->next;
+	  goto again;
+	}
+    }
+
+  return 0;
 }
 
+static int do_command_list_continuation (void *arg, int err);
+
+static void
+do_command_list (struct continuation_args *args)
+{
+  if (do_command_list_1 (args))
+    {
+      struct thread_info *tp = inferior_thread ();
+      struct continuation_args *new_args;
+
+      new_args = XNEW (struct continuation_args);
+      new_args->args = args->args;
+      args->args = NULL;
+      new_args->top = args->top;
+      args->top = NULL;
+
+      new_args->user_call_depth = user_call_depth;
+      new_args->instream = instream;
+      new_args->in_user_command = in_user_command;
+      new_args->command_nest_depth = command_nest_depth;
+
+      add_interpreter_continuation (do_command_list_continuation,
+				    new_args, xfree);
+    }
+}
+
+static int
+do_command_list_continuation (void *arg, int err)
+{
+  struct continuation_args *args = arg;
+  struct cleanup *old_chain;
+  int resumed;
+
+  if (err)
+    {
+      /* cleanup */
+      return 1;
+    }
+
+  if (args->args)
+    old_chain = setup_user_args (args->args);
+  else
+    old_chain = make_cleanup (null_cleanup, NULL);
+
+  make_cleanup_restore_integer (&user_call_depth);
+  user_call_depth = args->user_call_depth;
+
+  /* Set the instream to 0, indicating execution of a user-defined
+     function.  */
+  make_cleanup (do_restore_instream_cleanup, instream);
+  instream = args->instream;
+
+  /* Also set the global in_user_command, so that NULL instream is not
+     confused with Insight.  */
+  make_cleanup_restore_integer (&in_user_command);
+  in_user_command = args->in_user_command;
+
+  make_cleanup_restore_integer (&command_nest_depth);
+  command_nest_depth = args->command_nest_depth;
+
+  resumed = do_command_list_1 (arg);
+
+  do_cleanups (old_chain);
+
+  return resumed ? 0 : -1;
+}
 
 void
 execute_user_command (struct cmd_list_element *c, char *args)
 {
-  struct command_line *cmdlines;
+  struct continuation_args *cont_args;
   struct cleanup *old_chain;
-  enum command_control_type ret;
-  static int user_call_depth = 0;
-  extern int max_user_call_depth;
 
-  cmdlines = c->user_commands;
-  if (cmdlines == 0)
+  if (c->user_commands == 0)
     /* Null command */
     return;
 
   old_chain = setup_user_args (args);
 
+  make_cleanup_restore_integer (&user_call_depth);
   if (++user_call_depth > max_user_call_depth)
     error (_("Max user call depth exceeded -- command aborted."));
 
-  make_cleanup (do_restore_user_call_depth, &user_call_depth);
-
-  /* Set the instream to 0, indicating execution of a
-     user-defined function.  */
+  /* Set the instream to 0, indicating execution of a user-defined
+     function.  */
   make_cleanup (do_restore_instream_cleanup, instream);
   instream = (FILE *) 0;
 
-  /* Also set the global in_user_command, so that NULL instream is
-     not confused with Insight.  */
+  /* Also set the global in_user_command, so that NULL instream is not
+     confused with Insight.  */
+  make_cleanup_restore_integer (&in_user_command);
   in_user_command = 1;
 
+  make_cleanup_restore_integer (&command_nest_depth);
   command_nest_depth++;
-  while (cmdlines)
-    {
-      ret = execute_control_command (cmdlines);
-      if (ret != simple_control && ret != break_control)
-	{
-	  warning (_("Error executing canned sequence of commands."));
-	  break;
-	}
-      cmdlines = cmdlines->next;
-    }
-  command_nest_depth--;
+
+  cont_args = XNEW (struct continuation_args);
+  cont_args->args = args;
+  cont_args->user_call_depth = user_call_depth;
+  cont_args->instream = instream;
+  cont_args->in_user_command = in_user_command;
+  cont_args->command_nest_depth = command_nest_depth;
+
+  cont_args->top = new_command_list (NULL, c->user_commands);
+
+  do_command_list (cont_args);
+
   do_cleanups (old_chain);
 }
 
@@ -393,15 +693,10 @@ print_command_trace (const char *cmd)
   printf_filtered ("%s\n", cmd);
 }
 
-enum command_control_type
-execute_control_command (struct command_line *cmd)
+static enum command_control_type
+execute_control_command_1 (struct command_line *cmd)
 {
-  struct expression *expr;
-  struct command_line *current;
   struct cleanup *old_chain = make_cleanup (null_cleanup, 0);
-  struct value *val;
-  struct value *val_mark;
-  int loop;
   enum command_control_type ret;
   char *new_line;
 
@@ -444,64 +739,7 @@ execute_control_command (struct command_line *cmd)
 	sprintf (buffer, "while %s", cmd->line);
 	print_command_trace (buffer);
 
-	/* Parse the loop control expression for the while statement.  */
-	new_line = insert_args (cmd->line);
-	if (!new_line)
-	  break;
-	make_cleanup (free_current_contents, &new_line);
-	expr = parse_expression (new_line);
-	make_cleanup (free_current_contents, &expr);
-
-	ret = simple_control;
-	loop = 1;
-
-	/* Keep iterating so long as the expression is true.  */
-	while (loop == 1)
-	  {
-	    int cond_result;
-
-	    QUIT;
-
-	    /* Evaluate the expression.  */
-	    val_mark = value_mark ();
-	    val = evaluate_expression (expr);
-	    cond_result = value_true (val);
-	    value_free_to_mark (val_mark);
-
-	    /* If the value is false, then break out of the loop.  */
-	    if (!cond_result)
-	      break;
-
-	    /* Execute the body of the while statement.  */
-	    current = *cmd->body_list;
-	    while (current)
-	      {
-		command_nest_depth++;
-		ret = execute_control_command (current);
-		command_nest_depth--;
-
-		/* If we got an error, or a "break" command, then stop
-		   looping.  */
-		if (ret == invalid_control || ret == break_control)
-		  {
-		    loop = 0;
-		    break;
-		  }
-
-		/* If we got a "continue" command, then restart the loop
-		   at this point.  */
-		if (ret == continue_control)
-		  break;
-
-		/* Get the next statement.  */
-		current = current->next;
-	      }
-	  }
-
-	/* Reset RET so that we don't recurse the break all the way down.  */
-	if (ret == break_control)
-	  ret = simple_control;
-
+	ret = cmd->control_type;
 	break;
       }
 
@@ -512,44 +750,7 @@ execute_control_command (struct command_line *cmd)
 	sprintf (buffer, "if %s", cmd->line);
 	print_command_trace (buffer);
 
-	new_line = insert_args (cmd->line);
-	if (!new_line)
-	  break;
-	make_cleanup (free_current_contents, &new_line);
-	/* Parse the conditional for the if statement.  */
-	expr = parse_expression (new_line);
-	make_cleanup (free_current_contents, &expr);
-
-	current = NULL;
-	ret = simple_control;
-
-	/* Evaluate the conditional.  */
-	val_mark = value_mark ();
-	val = evaluate_expression (expr);
-
-	/* Choose which arm to take commands from based on the value
-	   of the conditional expression.  */
-	if (value_true (val))
-	  current = *cmd->body_list;
-	else if (cmd->body_count == 2)
-	  current = *(cmd->body_list + 1);
-	value_free_to_mark (val_mark);
-
-	/* Execute commands in the given arm.  */
-	while (current)
-	  {
-	    command_nest_depth++;
-	    ret = execute_control_command (current);
-	    command_nest_depth--;
-
-	    /* If we got an error, get out.  */
-	    if (ret != simple_control)
-	      break;
-
-	    /* Get the next statement in the body.  */
-	    current = current->next;
-	  }
-
+	ret = cmd->control_type;
 	break;
       }
     case commands_control:
@@ -580,10 +781,31 @@ execute_control_command (struct command_line *cmd)
   return ret;
 }
 
+void
+execute_control_command (struct command_line *cmd)
+{
+  struct continuation_args *cont_args;
+  struct command_line *cmdline;
+
+  cmdline = XNEW (struct command_line);
+  cmdline->next = NULL;
+  cmdline->control_type = cmd->control_type;
+  cmdline->body_count = cmd->body_count;
+  cmdline->body_list = cmd->body_list;
+  cmdline->line = cmd->line;
+
+  cont_args = XNEW (struct continuation_args);
+  cont_args->command_nest_depth = command_nest_depth;
+  cont_args->args = NULL;
+  cont_args->top = new_command_list (NULL, cmdline);
+
+  do_command_list (cont_args);
+}
+
 /* Like execute_control_command, but first set
    suppress_next_print_command_trace.  */
 
-enum command_control_type
+void
 execute_control_command_untraced (struct command_line *cmd)
 {
   suppress_next_print_command_trace = 1;
@@ -1665,3 +1887,9 @@ show_user_1 (struct cmd_list_element *c, char *prefix, char *name,
   fputs_filtered ("\n", stream);
 }
 
+
+void
+_initialize_cli_script (void)
+{
+  observer_attach_about_to_proceed (cli_script_about_to_proceed);
+}
--- a/gdb/cli/cli-script.h
+++ b/gdb/cli/cli-script.h
@@ -39,11 +39,9 @@ extern void show_user_1 (struct cmd_list_element *c, char *prefix,
 
 /* Exported to gdb/breakpoint.c */
 
-extern enum command_control_type
-	execute_control_command (struct command_line *cmd);
+extern void execute_control_command (struct command_line *cmd);
 
-extern enum command_control_type
-	execute_control_command_untraced (struct command_line *cmd);
+extern void execute_control_command_untraced (struct command_line *cmd);
 
 extern struct command_line *get_command_line (enum command_control_type,
 					      char *);
--- a/gdb/continuations.c
+++ b/gdb/continuations.c
@@ -23,6 +23,7 @@
 #include "gdbthread.h"
 #include "inferior.h"
 #include "continuations.h"
+#include "exceptions.h"
 
 struct continuation
 {
@@ -36,53 +37,53 @@ struct continuation
    FUNCTION to run the continuation up with, and ARG to pass to
    it.  */
 
-static void
+static struct continuation *
 make_continuation (struct continuation **pmy_chain,
 		   continuation_ftype *function,
 		   void *arg,  void (*free_arg) (void *))
 {
   struct continuation *new = XNEW (struct continuation);
+  struct continuation *old_chain = *pmy_chain;
 
   new->next = *pmy_chain;
   new->function = function;
   new->free_arg = free_arg;
   new->arg = arg;
   *pmy_chain = new;
+
+  return old_chain;
 }
 
-static void
-do_my_continuations_1 (struct continuation **pmy_chain, int err)
+static int
+do_my_continuations (struct continuation **pmy_chain, int err,
+		     struct continuation *old_chain)
 {
-  struct continuation *ptr;
+  volatile struct gdb_exception e;
+  struct continuation *ptr = *pmy_chain;
+  int ret = -1;
 
-  while ((ptr = *pmy_chain) != NULL)
+  TRY_CATCH (e, RETURN_MASK_ALL)
     {
-      *pmy_chain = ptr->next;	/* Do this first in case of recursion.  */
-      (*ptr->function) (ptr->arg, err);
-      if (ptr->free_arg)
-	(*ptr->free_arg) (ptr->arg);
-      xfree (ptr);
+      while ((ptr = *pmy_chain) != old_chain)
+	{
+	  ret = (*ptr->function) (ptr->arg, err);
+	  if (!ret)
+	    break;
+
+	  *pmy_chain = ptr->next;
+	  if (ptr->free_arg)
+	    (*ptr->free_arg) (ptr->arg);
+	  xfree (ptr);
+	}
     }
-}
-
-static void
-do_my_continuations (struct continuation **list, int err)
-{
-  struct continuation *continuations;
-
-  if (*list == NULL)
-    return;
 
-  /* Copy the list header into another pointer, and set the global
-     list header to null, so that the global list can change as a side
-     effect of invoking the continuations and the processing of the
-     preexisting continuations will not be affected.  */
+  if (e.reason < 0 && !err)
+    do_my_continuations (pmy_chain, 1, old_chain);
 
-  continuations = *list;
-  *list = NULL;
+  if (e.reason < 0)
+    throw_exception (e);
 
-  /* Work now on the list we have set aside.  */
-  do_my_continuations_1 (&continuations, err);
+  return ret;
 }
 
 static void
@@ -122,11 +123,11 @@ add_inferior_continuation (continuation_ftype *hook, void *args,
 
 /* Do all continuations of the current inferior.  */
 
-void
+int
 do_all_inferior_continuations (int err)
 {
   struct inferior *inf = current_inferior ();
-  do_my_continuations (&inf->continuations, err);
+  return do_my_continuations (&inf->continuations, err, NULL);
 }
 
 /* Get rid of all the inferior-wide continuations of INF.  */
@@ -140,12 +141,12 @@ discard_all_inferior_continuations (struct inferior *inf)
 /* Add a continuation to the continuation list of THREAD.  The new
    continuation will be added at the front.  */
 
-void
+struct continuation *
 add_continuation (struct thread_info *thread,
 		  continuation_ftype *hook, void *args,
 		  continuation_free_arg_ftype *free_arg)
 {
-  make_continuation (&thread->continuations, hook, args, free_arg);
+  return make_continuation (&thread->continuations, hook, args, free_arg);
 }
 
 static void
@@ -165,16 +166,17 @@ restore_thread_cleanup (void *arg)
    continuations from there on, instead of using the global beginning
    of list as our iteration pointer.  */
 
-static void
+static int
 do_all_continuations_ptid (ptid_t ptid,
 			   struct continuation **continuations_p,
 			   int err)
 {
   struct cleanup *old_chain;
   ptid_t current_thread;
+  int ret;
 
   if (*continuations_p == NULL)
-    return;
+    return -1;
 
   current_thread = inferior_ptid;
 
@@ -192,35 +194,36 @@ do_all_continuations_ptid (ptid_t ptid,
   /* Let the continuation see this thread as selected.  */
   switch_to_thread (ptid);
 
-  do_my_continuations (continuations_p, err);
+  ret = do_my_continuations (continuations_p, err, NULL);
 
   do_cleanups (old_chain);
+
+  return ret;
 }
 
 /* Callback for iterate over threads.  */
 
 static int
-do_all_continuations_thread_callback (struct thread_info *thread, void *data)
+find_thread_with_continuation_callback (struct thread_info *thread, void *data)
 {
-  int err = * (int *) data;
-  do_all_continuations_ptid (thread->ptid, &thread->continuations, err);
-  return 0;
+  return (thread->continuations != NULL);
 }
 
-/* Do all continuations of thread THREAD.  */
-
-void
-do_all_continuations_thread (struct thread_info *thread, int err)
+struct thread_info *
+find_thread_with_continuation (void)
 {
-  do_all_continuations_thread_callback (thread, &err);
+  struct thread_info *tp;
+
+  return iterate_over_threads (find_thread_with_continuation_callback, NULL);
 }
 
-/* Do all continuations of all threads.  */
 
-void
-do_all_continuations (int err)
+/* Do all continuations of thread THREAD.  */
+
+int
+do_all_continuations_thread (struct thread_info *thread, int err)
 {
-  iterate_over_threads (do_all_continuations_thread_callback, &err);
+  return do_all_continuations_ptid (thread->ptid, &thread->continuations, err);
 }
 
 /* Callback for iterate over threads.  */
@@ -249,80 +252,25 @@ discard_all_continuations (void)
   iterate_over_threads (discard_all_continuations_thread_callback, NULL);
 }
 
+
 
-/* Add a continuation to the intermediate continuation list of THREAD.
-   The new continuation will be added at the front.  */
+static struct continuation *interpreter_continuations;
 
-void
-add_intermediate_continuation (struct thread_info *thread,
-			       continuation_ftype *hook,
-			       void *args,
-			       continuation_free_arg_ftype *free_arg)
-{
-  make_continuation (&thread->intermediate_continuations, hook,
-		     args, free_arg);
-}
-
-/* Walk down the cmd_continuation list, and execute all the
-   continuations.  There is a problem though.  In some cases new
-   continuations may be added while we are in the middle of this
-   loop.  If this happens they will be added in the front, and done
-   before we have a chance of exhausting those that were already
-   there.  We need to then save the beginning of the list in a pointer
-   and do the continuations from there on, instead of using the
-   global beginning of list as our iteration pointer.  */
-
-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, err);
-  return 0;
-}
-
-/* Do all intermediate continuations of thread THREAD.  */
-
-void
-do_all_intermediate_continuations_thread (struct thread_info *thread, int err)
-{
-  do_all_intermediate_continuations_thread_callback (thread, &err);
-}
-
-/* Do all intermediate continuations of all threads.  */
-
-void
-do_all_intermediate_continuations (int err)
-{
-  iterate_over_threads (do_all_intermediate_continuations_thread_callback,
-			&err);
-}
-
-/* Callback for iterate over threads.  */
-
-static int
-discard_all_intermediate_continuations_thread_callback (struct thread_info *thread,
-							void *data)
-{
-  discard_my_continuations (&thread->intermediate_continuations);
-  return 0;
-}
-
-/* Get rid of all the intermediate continuations of THREAD.  */
+/* Add a continuation to the command list continuation list of THREAD.
+   The new continuation will be added at the front.  */
 
-void
-discard_all_intermediate_continuations_thread (struct thread_info *thread)
+struct continuation *
+add_interpreter_continuation (continuation_ftype *hook, void *args,
+			      continuation_free_arg_ftype *free_args)
 {
-  discard_all_intermediate_continuations_thread_callback (thread, NULL);
+  return make_continuation (&interpreter_continuations,
+			    hook, args, free_args);
 }
 
-/* Get rid of all the intermediate continuations of all threads.  */
+/* Do all interpreter continuations.  */
 
-void
-discard_all_intermediate_continuations (void)
+int
+do_interpreter_continuations (int err, struct continuation *old_chain)
 {
-  iterate_over_threads (discard_all_intermediate_continuations_thread_callback,
-			NULL);
+  return do_my_continuations (&interpreter_continuations, err, old_chain);
 }
--- a/gdb/continuations.h
+++ b/gdb/continuations.h
@@ -23,6 +23,7 @@
 
 struct thread_info;
 struct inferior;
+struct continuation;
 
 /* To continue the execution commands when running gdb asynchronously.
    A continuation structure contains a pointer to a function to be called
@@ -39,7 +40,7 @@ struct inferior;
    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);
+typedef int (continuation_ftype) (void *arg, int err);
 
 /* Prototype of the function responsible for releasing the argument
    passed to the continuation callback functions, either when the
@@ -48,28 +49,26 @@ typedef void (continuation_free_arg_ftype) (void *);
 
 /* Thread specific continuations.  */
 
-extern void add_continuation (struct thread_info *,
-			      continuation_ftype *, void *,
-			      continuation_free_arg_ftype *);
-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 (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 *);
+extern struct continuation *add_continuation (struct thread_info *,
+					      continuation_ftype *, void *,
+					      continuation_free_arg_ftype *);
+extern int do_all_continuations_thread (struct thread_info *, int err);
+
+/* Continuations called between commands in a command list.  */
+
+extern struct continuation *
+  add_interpreter_continuation (continuation_ftype *, void *,
+				continuation_free_arg_ftype *);
+
+extern int do_interpreter_continuations (int err,
+					 struct continuation *old_chain);
 
 /* Inferior specific (any thread) continuations.  */
 
 extern void add_inferior_continuation (continuation_ftype *,
 				       void *,
 				       continuation_free_arg_ftype *);
-extern void do_all_inferior_continuations (int err);
+extern int do_all_inferior_continuations (int err);
 extern void discard_all_inferior_continuations (struct inferior *inf);
 
 #endif
--- a/gdb/event-top.c
+++ b/gdb/event-top.c
@@ -450,8 +450,6 @@ stdin_event_handler (int error, gdb_client_data client_data)
     {
       printf_unfiltered (_("error detected on stdin\n"));
       delete_file_handler (input_fd);
-      discard_all_continuations ();
-      discard_all_intermediate_continuations ();
       /* If stdin died, we may as well kill gdb.  */
       quit_command ((char *) 0, stdin == instream);
     }
--- a/gdb/gdbcmd.h
+++ b/gdb/gdbcmd.h
@@ -131,7 +131,7 @@ extern struct cmd_list_element *save_cmdlist;
 extern void execute_command (char *, int);
 extern char *execute_command_to_string (char *p, int from_tty);
 
-enum command_control_type execute_control_command (struct command_line *);
+void execute_control_command (struct command_line *);
 
 extern void print_command_line (struct command_line *, unsigned int,
 				struct ui_file *);
--- a/gdb/gdbthread.h
+++ b/gdb/gdbthread.h
@@ -197,11 +197,8 @@ struct thread_info
      support async execution.  Several execution commands use it.  */
   struct continuation *continuations;
 
-  /* Similar to the above, but used when a single execution command
-     requires several resume/stop iterations.  Used by the step
-     command.  */
-  struct continuation *intermediate_continuations;
-
+  struct continuation *command_list_continuations;
+  
   /* If stepping, nonzero means step count is > 1 so don't print frame
      next time inferior stops if it stops due to stepping.  */
   int step_multi;
--- a/gdb/inf-loop.c
+++ b/gdb/inf-loop.c
@@ -32,6 +32,8 @@
 
 static int fetch_inferior_event_wrapper (gdb_client_data client_data);
 
+extern void cancel_previous_execution_command (void);
+
 /* General function to handle events in the inferior.  So far it just
    takes care of detecting errors reported by select() or poll(),
    otherwise it assumes that all is OK, and goes on reading data from
@@ -55,15 +57,13 @@ inferior_event_handler (enum inferior_event_type event_type,
 			 client_data, "", RETURN_MASK_ALL))
 	{
 	  bpstat_clear_actions ();
-	  do_all_intermediate_continuations (1);
-	  do_all_continuations (1);
+	  cancel_previous_execution_command ();
 	  async_enable_stdin ();
 	  display_gdb_prompt (0);
 	}
       break;
 
     case INF_EXEC_COMPLETE:
-
       if (!non_stop)
 	{
 	  /* Unregister the inferior from the event loop.  This is done
@@ -73,47 +73,17 @@ inferior_event_handler (enum inferior_event_type event_type,
 	    target_async (NULL, 0);
 	}
 
+      if (info_verbose
+	  && current_language != expected_language
+	  && language_mode == language_mode_auto)
+	language_info (1);	/* Print what changed.  */
+
       /* The call to async_enable_stdin below resets 'sync_execution'.
 	 However, if sync_execution is 1 now, we also need to show the
 	 prompt below, so save the current value.  */
       was_sync = sync_execution;
       async_enable_stdin ();
 
-      /* Do all continuations associated with the whole inferior (not
-	 a particular thread).  */
-      if (!ptid_equal (inferior_ptid, null_ptid))
-	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
-	 continuations.  The continuation itself is responsible for
-	 distinguishing the cases.  The continuations are allowed to
-	 touch the inferior memory, e.g. to remove breakpoints, so run
-	 them before running breakpoint commands, which may resume the
-	 target.  */
-      if (non_stop
-	  && target_has_execution
-	  && !ptid_equal (inferior_ptid, null_ptid))
-	do_all_intermediate_continuations_thread (inferior_thread (), 0);
-      else
-	do_all_intermediate_continuations (0);
-
-      /* Always finish the previous command before running any
-	 breakpoint commands.  Any stop cancels the previous command.
-	 E.g. a "finish" or "step-n" command interrupted by an
-	 unrelated breakpoint is canceled.  */
-      if (non_stop
-	  && target_has_execution
-	  && !ptid_equal (inferior_ptid, null_ptid))
-	do_all_continuations_thread (inferior_thread (), 0);
-      else
-	do_all_continuations (0);
-
-      if (info_verbose
-	  && current_language != expected_language
-	  && language_mode == language_mode_auto)
-	language_info (1);	/* Print what changed.  */
-
       /* Don't propagate breakpoint commands errors.  Either we're
 	 stopping or some command resumes the inferior.  The user will
 	 be informed.  */
@@ -130,16 +100,6 @@ inferior_event_handler (enum inferior_event_type event_type,
 	printf_unfiltered (_("completed.\n"));
       break;
 
-    case INF_EXEC_CONTINUE:
-      /* Is there anything left to do for the command issued to
-         complete?  */
-
-      if (non_stop)
-	do_all_intermediate_continuations_thread (inferior_thread (), 0);
-      else
-	do_all_intermediate_continuations (0);
-      break;
-
     case INF_TIMER:
     default:
       printf_unfiltered (_("Event type not recognized.\n"));
--- a/gdb/infcall.c
+++ b/gdb/infcall.c
@@ -404,9 +404,8 @@ run_inferior_call (struct thread_info *call_thread, CORE_ADDR real_pc)
       proceed (real_pc, TARGET_SIGNAL_0, 0);
 
       /* Inferior function calls are always synchronous, even if the
-	 target supports asynchronous execution.  Do here what
-	 `proceed' itself does in sync mode.  */
-      if (target_can_async_p () && is_running (inferior_ptid))
+	 target supports asynchronous execution.  */
+      if (is_running (inferior_ptid))
 	{
 	  wait_for_inferior ();
 	  normal_stop ();
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -108,8 +108,8 @@ static void signal_command (char *, int);
 static void jump_command (char *, int);
 
 static void step_1 (int, int, char *);
-static void step_once (int skip_subroutines, int single_inst,
-		       int count, int thread);
+struct step_1_continuation_args;
+static int step_once (struct step_1_continuation_args *args);
 
 static void next_command (char *, int);
 
@@ -610,6 +610,12 @@ run_command_1 (char *args, int from_tty, int tbreak_at_main)
      breakpoint right at the entry point.  */
   proceed (regcache_read_pc (get_current_regcache ()), TARGET_SIGNAL_0, 0);
 
+  if (!target_can_async_p () && is_running (inferior_ptid))
+    {
+      wait_for_inferior ();
+      normal_stop ();
+    }
+
   /* Since there was no error, there's no need to finish the thread
      states here.  */
   discard_cleanups (old_chain);
@@ -710,6 +716,12 @@ continue_1 (int all_threads)
       ensure_not_running ();
       clear_proceed_status ();
       proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 0);
+
+      if (!target_can_async_p () && is_running (inferior_ptid))
+	{
+	  wait_for_inferior ();
+	  normal_stop ();
+	}
     }
 }
 
@@ -852,6 +864,53 @@ delete_longjmp_breakpoint_cleanup (void *arg)
   delete_longjmp_breakpoint (thread);
 }
 
+struct step_1_continuation_args
+{
+  int count;
+  int skip_subroutines;
+  int single_inst;
+  int thread;
+};
+
+static int step_1_continuation (void *args, int err);
+
+void
+sync_run_command (void)
+{
+  extern struct thread_info *find_thread_with_continuation (void);
+  extern void cancel_previous_execution_command (void);
+  int done;
+
+  while (1)
+    {
+      struct thread_info *tp;
+
+      wait_for_inferior ();
+
+      if (!target_has_execution)
+	{
+	  cancel_previous_execution_command ();
+	  done = -1;
+	  break;
+	}
+
+      tp = find_thread_with_continuation ();
+
+      /* Cancel previous command, if it had started on some
+	 other thread.  */
+      if (tp != NULL && tp != inferior_thread ())
+	do_all_continuations_thread (tp, 1);
+
+      /* Do any continuation for this thread.  */
+      done = do_all_continuations_thread (inferior_thread (), 0);
+      if (done)
+	break;
+    }
+
+  if (done == -1)
+    normal_stop ();
+}
+
 static void
 step_1 (int skip_subroutines, int single_inst, char *count_string)
 {
@@ -859,6 +918,9 @@ step_1 (int skip_subroutines, int single_inst, char *count_string)
   struct cleanup *cleanups = make_cleanup (null_cleanup, NULL);
   int async_exec = 0;
   int thread = -1;
+  struct step_1_continuation_args *args;
+  struct thread_info *tp;
+  int done;
 
   ERROR_NO_INFERIOR;
   ensure_not_tfind_mode ();
@@ -881,78 +943,74 @@ step_1 (int skip_subroutines, int single_inst, char *count_string)
       async_disable_stdin ();
     }
 
+  tp = inferior_thread ();
+
   count = count_string ? parse_and_eval_long (count_string) : 1;
 
   if (!single_inst || skip_subroutines)		/* Leave si command alone.  */
     {
-      struct thread_info *tp = inferior_thread ();
-
-      if (in_thread_list (inferior_ptid))
- 	thread = pid_to_thread_id (inferior_ptid);
+      thread = tp->num;
 
       set_longjmp_breakpoint (tp, get_frame_id (get_current_frame ()));
 
       make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
     }
 
-  /* In synchronous case, all is well; each step_once call will step once.  */
-  if (!target_can_async_p ())
-    {
-      for (; count > 0; count--)
-	{
-	  step_once (skip_subroutines, single_inst, count, thread);
-
-	  if (!target_has_execution)
-	    break;
-	  else
-	    {
-	      struct thread_info *tp = inferior_thread ();
+  /* Register a continuation to do any additional steps.  */
+  args = xmalloc (sizeof (*args));
+  args->skip_subroutines = skip_subroutines;
+  args->single_inst = single_inst;
+  args->count = count;
+  args->thread = thread;
 
-	      if (!tp->control.stop_step || !tp->step_multi)
-		{
-		  /* If we stopped for some reason that is not stepping
-		     there are no further steps to make.  */
-		  tp->step_multi = 0;
-		  break;
-		}
-	    }
-	}
+  make_cleanup (xfree, args);
 
+  /* In the case of an asynchronous target things get complicated;
+     do only one step for now, before returning control to the
+     event loop.  Let the continuation figure out how many other
+     steps we need to do, and handle them one at the time, through
+     step_once.  */
+  done = step_once (args);
+  if (done < 0)
+    {
+      do_cleanups (cleanups);
+    }
+  else if (done > 0)
+    {
       do_cleanups (cleanups);
+      normal_stop ();
+      if (target_can_async_p ())
+	inferior_event_handler (INF_EXEC_COMPLETE, NULL);
     }
   else
     {
-      /* In the case of an asynchronous target things get complicated;
-	 do only one step for now, before returning control to the
-	 event loop.  Let the continuation figure out how many other
-	 steps we need to do, and handle them one at the time, through
-	 step_once.  */
-      step_once (skip_subroutines, single_inst, count, thread);
-
+      add_continuation (tp, step_1_continuation, args, xfree);
       /* We are running, and the continuation is installed.  It will
 	 disable the longjmp breakpoint as appropriate.  */
       discard_cleanups (cleanups);
+
+      if (!target_can_async_p ())
+	{
+	  /* We get to do our mini event-loop here.  */
+	  sync_run_command ();
+	}
     }
 }
 
-struct step_1_continuation_args
-{
-  int count;
-  int skip_subroutines;
-  int single_inst;
-  int thread;
-};
-
 /* Called after we are done with one step operation, to check whether
    we need to step again, before we print the prompt and return control
    to the user.  If count is > 1, we will need to do one more call to
    proceed(), via step_once().  Basically it is like step_once and
    step_1_continuation are co-recursive.  */
-static void
+static int
 step_1_continuation (void *args, int err)
 {
   struct step_1_continuation_args *a = args;
 
+  if (debug_infrun)
+    fprintf_unfiltered (gdb_stdlog, "infcmd: step_1_continuation (%s, %d)\n",
+			host_address_to_string (args), err);
+
   if (target_has_execution)
     {
       struct thread_info *tp;
@@ -961,11 +1019,12 @@ step_1_continuation (void *args, int err)
       if (!err
 	  && tp->step_multi && tp->control.stop_step)
 	{
-	  /* There are more steps to make, and we did stop due to
+	  a->count--;
+
+	  /* There were more steps to make, and we did stop due to
 	     ending a stepping range.  Do another step.  */
-	  step_once (a->skip_subroutines, a->single_inst,
-		     a->count - 1, a->thread);
-	  return;
+	  if (!step_once (a))
+	    return 0;
 	}
       tp->step_multi = 0;
     }
@@ -975,6 +1034,10 @@ step_1_continuation (void *args, int err)
      Cleanup.  */
   if (!a->single_inst || a->skip_subroutines)
     delete_longjmp_breakpoint (a->thread);
+
+  if (!err)
+    normal_stop ();
+  return 1;
 }
 
 /* Do just one step operation.  This is useful to implement the 'step
@@ -983,12 +1046,12 @@ step_1_continuation (void *args, int err)
    this one step).  For synch targets, the caller handles further
    stepping.  */
 
-static void
-step_once (int skip_subroutines, int single_inst, int count, int thread)
+static int
+step_once (struct step_1_continuation_args *args)
 {
   struct frame_info *frame = get_current_frame ();
 
-  if (count > 0)
+  if (args->count > 0)
     {
       /* Don't assume THREAD is a valid thread id.  It is set to -1 if
 	 the longjmp breakpoint was not required.  Use the
@@ -999,12 +1062,12 @@ step_once (int skip_subroutines, int single_inst, int count, int thread)
       clear_proceed_status ();
       set_step_frame ();
 
-      if (!single_inst)
+      if (!args->single_inst)
 	{
 	  CORE_ADDR pc;
 
 	  /* Step at an inlined function behaves like "down".  */
-	  if (!skip_subroutines && !single_inst
+	  if (!args->skip_subroutines && !args->single_inst
 	      && inline_skipped_frames (inferior_ptid))
 	    {
 	      ptid_t resume_ptid;
@@ -1014,17 +1077,16 @@ step_once (int skip_subroutines, int single_inst, int count, int thread)
 	      set_running (resume_ptid, 1);
 
 	      step_into_inline_frame (inferior_ptid);
-	      if (count > 1)
-		step_once (skip_subroutines, single_inst, count - 1, thread);
+	      if (args->count > 1)
+		{
+		  args->count--;
+		  return step_once (args);
+		}
 	      else
 		{
-		  /* Pretend that we've stopped.  */
-		  normal_stop ();
-
-		  if (target_can_async_p ())
-		    inferior_event_handler (INF_EXEC_COMPLETE, NULL);
+		  /* Done.  */
+		  return 1;
 		}
-	      return;
 	    }
 
 	  pc = get_frame_pc (frame);
@@ -1054,35 +1116,21 @@ step_once (int skip_subroutines, int single_inst, int count, int thread)
 	{
 	  /* Say we are stepping, but stop after one insn whatever it does.  */
 	  tp->control.step_range_start = tp->control.step_range_end = 1;
-	  if (!skip_subroutines)
+	  if (!args->skip_subroutines)
 	    /* It is stepi.
 	       Don't step over function calls, not even to functions lacking
 	       line numbers.  */
 	    tp->control.step_over_calls = STEP_OVER_NONE;
 	}
 
-      if (skip_subroutines)
+      if (args->skip_subroutines)
 	tp->control.step_over_calls = STEP_OVER_ALL;
 
-      tp->step_multi = (count > 1);
+      tp->step_multi = (args->count > 1);
       proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 1);
-
-      /* For async targets, register a continuation to do any
-	 additional steps.  For sync targets, the caller will handle
-	 further stepping.  */
-      if (target_can_async_p ())
-	{
-	  struct step_1_continuation_args *args;
-
-	  args = xmalloc (sizeof (*args));
-	  args->skip_subroutines = skip_subroutines;
-	  args->single_inst = single_inst;
-	  args->count = count;
-	  args->thread = thread;
-
-	  add_intermediate_continuation (tp, step_1_continuation, args, xfree);
-	}
+      return is_running (inferior_ptid) ? 0 : -1;
     }
+  return 1;
 }
 
 
@@ -1177,6 +1225,12 @@ jump_command (char *arg, int from_tty)
 
   clear_proceed_status ();
   proceed (addr, TARGET_SIGNAL_0, 0);
+
+  if (!target_can_async_p () && is_running (inferior_ptid))
+    {
+      wait_for_inferior ();
+      normal_stop ();
+    }
 }
 
 
@@ -1255,6 +1309,12 @@ signal_command (char *signum_exp, int from_tty)
 
   clear_proceed_status ();
   proceed ((CORE_ADDR) -1, oursig, 0);
+
+  if (!target_can_async_p () && is_running (inferior_ptid))
+    {
+      wait_for_inferior ();
+      normal_stop ();
+    }
 }
 
 /* Continuation args to be passed to the "until" command
@@ -1267,12 +1327,16 @@ struct until_next_continuation_args
 
 /* A continuation callback for until_next_command.  */
 
-static void
+static int
 until_next_continuation (void *arg, int err)
 {
   struct until_next_continuation_args *a = arg;
 
   delete_longjmp_breakpoint (a->thread);
+  if (!err)
+    normal_stop ();
+
+  return 1;
 }
 
 /* Proceed until we reach a different source line with pc greater than
@@ -1333,15 +1397,19 @@ until_next_command (int from_tty)
 
   proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 1);
 
-  if (target_can_async_p () && is_running (inferior_ptid))
+  if (is_running (inferior_ptid))
     {
       struct until_next_continuation_args *cont_args;
 
-      discard_cleanups (old_chain);
       cont_args = XNEW (struct until_next_continuation_args);
       cont_args->thread = inferior_thread ()->num;
 
       add_continuation (tp, until_next_continuation, cont_args, xfree);
+
+      discard_cleanups (old_chain);
+
+      if (!target_can_async_p ())
+	sync_run_command ();
     }
   else
     do_cleanups (old_chain);
@@ -1493,7 +1561,7 @@ struct finish_command_continuation_args
   struct symbol *function;
 };
 
-static void
+static int
 finish_command_continuation (void *arg, int err)
 {
   struct finish_command_continuation_args *a = arg;
@@ -1503,6 +1571,8 @@ finish_command_continuation (void *arg, int err)
       struct thread_info *tp = NULL;
       bpstat bs = NULL;
 
+      normal_stop ();
+
       if (!ptid_equal (inferior_ptid, null_ptid)
 	  && target_has_execution
 	  && is_stopped (inferior_ptid))
@@ -1546,6 +1616,8 @@ finish_command_continuation (void *arg, int err)
 
   delete_breakpoint (a->breakpoint);
   delete_longjmp_breakpoint (a->thread);
+
+  return 1;
 }
 
 static void
@@ -1605,6 +1677,9 @@ finish_backward (struct symbol *function)
       tp->control.step_range_start = tp->control.step_range_end = 1;
       proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 1);
     }
+
+  if (!target_can_async_p () && is_running (inferior_ptid))
+    sync_run_command ();
 }
 
 /* finish_forward -- helper function for finish_command.  */
@@ -1617,7 +1692,6 @@ finish_forward (struct symbol *function, struct frame_info *frame)
   struct thread_info *tp = inferior_thread ();
   struct breakpoint *breakpoint;
   struct cleanup *old_chain;
-  struct finish_command_continuation_args *cargs;
   int thread = tp->num;
 
   sal = find_pc_line (get_frame_pc (frame), 0);
@@ -1634,18 +1708,27 @@ finish_forward (struct symbol *function, struct frame_info *frame)
 
   /* We want stop_registers, please...  */
   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,
-                    finish_command_continuation_free_arg);
   proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 0);
 
-  discard_cleanups (old_chain);
-  if (!target_can_async_p ())
-    do_all_continuations (0);
+  if (is_running (inferior_ptid))
+    {
+      struct finish_command_continuation_args *cargs;
+
+      cargs = xmalloc (sizeof (*cargs));
+      cargs->thread = thread;
+      cargs->breakpoint = breakpoint;
+      cargs->function = function;
+      add_continuation (tp, finish_command_continuation, cargs,
+			finish_command_continuation_free_arg);
+
+      discard_cleanups (old_chain);
+
+      if (!target_can_async_p ())
+	sync_run_command ();
+    }
+  else
+    do_cleanups (old_chain);
 }
 
 /* "finish": Set a temporary breakpoint at the place the selected
@@ -1719,6 +1802,12 @@ finish_command (char *arg, int from_tty)
 	}
 
       proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 1);
+
+      if (!target_can_async_p () && is_running (inferior_ptid))
+	{
+	  wait_for_inferior ();
+	  normal_stop ();
+	}
       return;
     }
 
@@ -2437,15 +2526,16 @@ struct attach_command_continuation_args
   int async_exec;
 };
 
-static void
+static int
 attach_command_continuation (void *args, int err)
 {
   struct attach_command_continuation_args *a = args;
 
   if (err)
-    return;
+    return 1;
 
   attach_command_post_wait (a->args, a->from_tty, a->async_exec);
+  return 1;
 }
 
 static void
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -2034,6 +2034,31 @@ prepare_to_proceed (int step)
   return 0;
 }
 
+void
+cancel_previous_execution_command (void)
+{
+  extern struct thread_info *find_thread_with_continuation ();
+  struct thread_info *tp;
+
+  if (debug_infrun)
+    fprintf_unfiltered (gdb_stdlog,
+			"infrun: cancel_previous_execution_command\n");
+
+  do_all_inferior_continuations (1);
+  if (non_stop)
+    {
+      if (target_has_execution)
+	tp = inferior_thread ();
+      else
+	tp = NULL;
+    }
+  else
+    tp = find_thread_with_continuation ();
+
+  if (tp != NULL)
+    do_all_continuations_thread (tp, 1);
+}
+
 /* Basic routine for continuing the program in various fashions.
 
    ADDR is the address to resume at, or -1 for resume where stopped.
@@ -2064,7 +2089,10 @@ proceed (CORE_ADDR addr, enum target_signal siggnal, int step)
       /* The target for some reason decided not to resume.  */
       normal_stop ();
       if (target_can_async_p ())
-	inferior_event_handler (INF_EXEC_COMPLETE, NULL);
+	{
+	  cancel_previous_execution_command ();
+	  inferior_event_handler (INF_EXEC_COMPLETE, NULL);
+	}
       return;
     }
 
@@ -2226,16 +2254,6 @@ proceed (CORE_ADDR addr, enum target_signal siggnal, int step)
 
   /* Resume inferior.  */
   resume (oneproc || step || bpstat_should_step (), tp->suspend.stop_signal);
-
-  /* Wait for it to stop (if not standalone)
-     and in any case decode why it stopped, and act accordingly.  */
-  /* Do this only if we are not using the event loop, or if the target
-     does not support asynchronous execution.  */
-  if (!target_can_async_p ())
-    {
-      wait_for_inferior ();
-      normal_stop ();
-    }
 }
 
 
@@ -2391,12 +2409,11 @@ infrun_thread_stop_requested_callback (struct thread_info *info, void *arg)
 	{
 	  struct thread_info *tp;
 
-	  normal_stop ();
-
-	  /* Finish off the continuations.  */
+	  /* Cancel any ongoing command.  */
 	  tp = inferior_thread ();
-	  do_all_intermediate_continuations_thread (tp, 1);
 	  do_all_continuations_thread (tp, 1);
+
+	  normal_stop ();
 	}
 
       do_cleanups (old_chain);
@@ -2788,22 +2805,72 @@ fetch_inferior_event (void *client_data)
 
   if (!ecs->wait_some_more)
     {
+      extern struct thread_info *find_thread_with_continuation ();
+
       struct inferior *inf = find_inferior_pid (ptid_get_pid (ecs->ptid));
+      int command_done;
 
       delete_step_thread_step_resume_breakpoint ();
 
-      /* We may not find an inferior if this was a process exit.  */
-      if (inf == NULL || inf->control.stop_soon == NO_STOP_QUIETLY)
-	normal_stop ();
+      if (target_has_execution)
+	{
+	  do_all_inferior_continuations (0);
+
+	  if (non_stop)
+	    command_done
+	      = do_all_continuations_thread (inferior_thread (), 0);
+	  else
+	    {
+	      struct thread_info *tp;
 
-      if (target_has_execution
-	  && ecs->ws.kind != TARGET_WAITKIND_EXITED
-	  && ecs->ws.kind != TARGET_WAITKIND_SIGNALLED
-	  && ecs->event_thread->step_multi
-	  && ecs->event_thread->control.stop_step)
-	inferior_event_handler (INF_EXEC_CONTINUE, NULL);
+	      tp = find_thread_with_continuation ();
+
+	      /* Cancel previous command, if it had started on some
+		 other thread.  */
+	      if (tp != NULL && tp != inferior_thread ())
+		do_all_continuations_thread (tp, 1);
+
+	      /* Do any continuation for this thread.  */
+	      command_done
+		= do_all_continuations_thread (inferior_thread (), 0);
+	    }
+	}
       else
-	inferior_event_handler (INF_EXEC_COMPLETE, NULL);
+	{
+	  do_all_inferior_continuations (1);
+
+	  if (!non_stop)
+	    {
+	      struct thread_info *tp;
+
+	      tp = find_thread_with_continuation ();
+
+	      /* Cancel any previous execution command.  */
+	      if (tp != NULL)
+		do_all_continuations_thread (tp, 1);
+	    }
+
+	  command_done = -1;
+	}
+
+      if (command_done)
+	{
+	  /* There was no continuation installed.  Simply let the user
+	     know where we stopped.  */
+	  if (command_done == -1
+	      /* We may not find an inferior if this was a process
+		 exit.  */
+	      && (inf == NULL || inf->control.stop_soon == NO_STOP_QUIETLY))
+	    normal_stop ();
+
+	  /* A single execution command is done.  Now maybe move to
+	     the next command in a command list.  */
+	  command_done = do_interpreter_continuations (0, NULL);
+
+	  /* We're only done when all the commands are done.  */
+	  if (command_done)
+	    inferior_event_handler (INF_EXEC_COMPLETE, NULL);
+	}
     }
 
   /* No error, don't finish the thread states yet.  */
@@ -5749,6 +5816,9 @@ normal_stop (void)
   ptid_t last_ptid;
   struct cleanup *old_chain = make_cleanup (null_cleanup, NULL);
 
+  if (debug_infrun)
+    fprintf_unfiltered (gdb_stdlog, "infrun: normal_stop\n");
+
   get_last_target_status (&last_ptid, &last);
 
   /* If an exception is thrown from this point on, make sure to
@@ -5803,15 +5873,6 @@ normal_stop (void)
   if (stopped_by_random_signal)
     disable_current_display ();
 
-  /* Don't print a message if in the middle of doing a "step n"
-     operation for n > 1 */
-  if (target_has_execution
-      && last.kind != TARGET_WAITKIND_SIGNALLED
-      && last.kind != TARGET_WAITKIND_EXITED
-      && inferior_thread ()->step_multi
-      && inferior_thread ()->control.stop_step)
-    goto done;
-
   target_terminal_ours ();
 
   /* Set the current source location.  This will also happen if we
@@ -5826,8 +5887,15 @@ normal_stop (void)
   /* Look up the hook_stop and run it (CLI internally handles problem
      of stop_command's pre-hook not existing).  */
   if (stop_command)
-    catch_errors (hook_stop_stub, stop_command,
-		  "Error while running hook_stop:\n", RETURN_MASK_ALL);
+    {
+      catch_errors (hook_stop_stub, stop_command,
+		    "Error while running hook_stop:\n", RETURN_MASK_ALL);
+      /* If the hook-stop set the program running, */
+      if (target_is_async_p ()
+	  && target_has_execution
+	  && is_running (inferior_ptid))
+	return;
+    }
 
   if (!has_stack_frames ())
     goto done;
@@ -5971,8 +6039,7 @@ done:
   if (!target_has_execution
       || last.kind == TARGET_WAITKIND_SIGNALLED
       || last.kind == TARGET_WAITKIND_EXITED
-      || (!inferior_thread ()->step_multi
-	  && !(inferior_thread ()->control.stop_bpstat
+      || (!(inferior_thread ()->control.stop_bpstat
 	       && inferior_thread ()->control.proceed_to_finish)
 	  && !inferior_thread ()->control.in_infcall))
     {
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -208,11 +208,6 @@ enum inferior_event_type
     INF_TIMER,
     /* We are called to do stuff after the inferior stops.  */
     INF_EXEC_COMPLETE,
-    /* We are called to do some stuff after the inferior stops, but we
-       are expected to reenter the proceed() and
-       handle_inferior_event() functions.  This is used only in case of
-       'step n' like commands.  */
-    INF_EXEC_CONTINUE
   };
 
 /* Target objects which can be transfered using target_read,
--- a/gdb/testsuite/gdb.base/define.exp
+++ b/gdb/testsuite/gdb.base/define.exp
@@ -291,5 +291,45 @@ gdb_test_multiple "set prompt \\(gdb\\) " "reset gdb_prompt" {
     }
 }
 
+clean_restart ${testfile}
+
+if ![runto_main] then { fail "define tests suppressed" }
+
+gdb_test_multiple "define nextnext" "define user command: nextnext" {
+    -re "Type commands for definition of \"nextnext\".\r\nEnd with a line saying just \"end\".\r\n>$" {
+	gdb_test "next\nnext\nend" "" \
+	    "define user command: nextnext"
+    }
+}
+
+# Verify that those commands work as expected.
+#
+gdb_test "nextnext" \
+    "$bp_location1\[ \t\]*printf.*\[ \t\].*$bp_location11.*marker1.*" \
+    "use user command: nextnext"
+
+
+clean_restart ${testfile}
+
+if ![runto_main] then { fail "define tests suppressed" }
+
+if [gdb_detect_async] {
+    gdb_test_multiple "define nextbprint" "define user command: nextbprint" {
+	-re "Type commands for definition of \"nextbprint\".\r\nEnd with a line saying just \"end\".\r\n>$" {
+	    gdb_test "next&\nprint 1\nend" "" \
+		"define user command: nextnprint"
+	}
+    }
+
+    # Verify that those commands work as expected.
+    #
+    set test "use user command: nextbprint"
+    gdb_test_multiple "nextbprint" $test {
+	-re " = 1\r\n$gdb_prompt " {
+	    pass $test
+	}
+    }
+}
+
 gdb_exit
 return 0
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -187,6 +187,24 @@ proc delete_breakpoints {} {
     }
 }
 
+proc gdb_detect_async {} {
+    global gdb_prompt
+    set async 0
+
+    send_gdb "show target-async\n"
+    gdb_expect {
+        -re ".*Controlling the inferior in asynchronous mode is on\..*$gdb_prompt$" {
+            set async 1
+        }
+        -re ".*$gdb_prompt$" {
+            set async 0
+        }
+        timeout {
+            set async 0
+        }
+    }
+    return $async
+}
 
 #
 # Generic run command.
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -125,7 +125,6 @@ clear_thread_inferior_resources (struct thread_info *tp)
 
   bpstat_clear (&tp->control.stop_bpstat);
 
-  do_all_intermediate_continuations_thread (tp, 1);
   do_all_continuations_thread (tp, 1);
 
   delete_longjmp_breakpoint (tp->num);
--- a/gdb/top.c
+++ b/gdb/top.c
@@ -46,6 +46,7 @@
 #include "gdb_assert.h"
 #include "main.h"
 #include "event-loop.h"
+#include "continuations.h"
 #include "gdbthread.h"
 #include "python/python.h"
 
@@ -362,20 +363,154 @@ prepare_execute_command (void)
   return cleanup;
 }
 
+enum execute_command_state
+{
+  EXEC_CMD_STATE_PRE_HOOK,
+  EXEC_CMD_STATE_COMMAND,
+  EXEC_CMD_STATE_POST_HOOK,
+  EXEC_CMD_STATE_DONE
+};
+
+struct execute_command_continuation
+{
+  enum execute_command_state state;
+  struct cmd_list_element *cmd;
+  struct value *mark;
+  char *arg;
+  char *line;
+  int from_tty;
+};
+
+static void
+execute_command_continuation_free (void *arg)
+{
+  struct execute_command_continuation *c = arg;
+
+  xfree (c);
+}
+
+static int warned = 0;
+
+static void
+execute_command_done (void)
+{
+  /* Tell the user if the language has changed (except first
+     time).  First make sure that a new frame has been selected,
+     in case this command or the hooks changed the program
+     state.  */
+  deprecated_safe_get_selected_frame ();
+  if (current_language != expected_language)
+    {
+      if (language_mode == language_mode_auto && info_verbose)
+	{
+	  language_info (1);	/* Print what changed.  */
+	}
+      warned = 0;
+    }
+
+  /* Warn the user if the working language does not match the
+     language of the current frame.  Only warn the user if we are
+     actually running the program, i.e. there is a stack.
+
+     FIXME: This should be cacheing the frame and only running
+     when the frame changes.  */
+
+  if (has_stack_frames ())
+    {
+      enum language flang;
+
+      flang = get_frame_language ();
+      if (!warned
+	  && flang != language_unknown
+	  && flang != current_language->la_language)
+	{
+	  printf_filtered ("%s\n", lang_frame_mismatch_warn);
+	  warned = 1;
+	}
+    }
+}
+
+static int
+execute_command_continuation (void *args, int err)
+{
+  struct execute_command_continuation *cont = args;
+  struct cmd_list_element *c = cont->cmd;
+  char *arg = cont->arg;
+  int from_tty = cont->from_tty;
+
+  if (err)
+    {
+      value_free_to_mark (cont->mark);
+      return 1;
+    }
+
+  while (cont->state != EXEC_CMD_STATE_DONE)
+    {
+      switch (cont->state)
+	{
+	case EXEC_CMD_STATE_PRE_HOOK:
+	  if (c->flags & DEPRECATED_WARN_USER)
+	    deprecated_cmd_warning (&cont->line);
+
+	  if (c->class == class_user)
+	    execute_user_command (c, arg);
+	  else if (c->type == set_cmd || c->type == show_cmd)
+	    do_setshow_command (arg, from_tty & caution, c);
+	  else if (!cmd_func_p (c))
+	    error (_("That is not a command, just a help topic."));
+	  else if (deprecated_call_command_hook)
+	    deprecated_call_command_hook (c, arg, from_tty & caution);
+	  else
+	    cmd_func (c, arg, from_tty & caution);
+
+	  cont->state = EXEC_CMD_STATE_COMMAND;
+	  break;
+
+	case EXEC_CMD_STATE_COMMAND:
+	  /* If this command has been post-hooked, run the hook last.  */
+	  execute_cmd_post_hook (c);
+	  cont->state = EXEC_CMD_STATE_POST_HOOK;
+	  break;
+
+	case EXEC_CMD_STATE_POST_HOOK:
+	  execute_command_done ();
+	  cont->state = EXEC_CMD_STATE_DONE;
+	  break;
+	}
+
+      if (cont->state != EXEC_CMD_STATE_DONE
+	  && target_can_async_p ()
+	  && sync_execution
+	  && target_has_execution
+	  && is_running (inferior_ptid))
+	return 0;
+    }
+
+  value_free_to_mark (cont->mark);
+  return 1;
+}
+
 /* Execute the line P as a command, in the current user context.
    Pass FROM_TTY as second argument to the defining function.  */
 
 void
 execute_command (char *p, int from_tty)
 {
+  struct value *mark;
   struct cleanup *cleanup_if_error, *cleanup;
   struct cmd_list_element *c;
-  enum language flang;
-  static int warned = 0;
   char *line;
 
+  mark = value_mark ();
   cleanup_if_error = make_bpstat_clear_actions_cleanup ();
-  cleanup = prepare_execute_command ();
+  cleanup = make_cleanup_value_free_to_mark (mark);
+
+  /* With multiple threads running while the one we're examining is
+     stopped, the dcache can get stale without us being able to detect
+     it.  For the duration of the command, though, use the dcache to
+     help things like backtrace.  */
+  if (non_stop)
+    target_dcache_invalidate ();
 
   /* Force cleanup of any alloca areas if using C alloca instead of
      a builtin alloca.  */
@@ -385,6 +520,7 @@ execute_command (char *p, int from_tty)
   if (p == NULL)
     {
       do_cleanups (cleanup);
+      discard_cleanups (cleanup_if_error);
       return;
     }
 
@@ -394,7 +530,10 @@ execute_command (char *p, int from_tty)
     p++;
   if (*p)
     {
+      struct continuation *old_chain;
+      struct execute_command_continuation *cont;
       char *arg;
+
       line = p;
 
       /* If trace-commands is set then this will print this command.  */
@@ -425,61 +564,39 @@ execute_command (char *p, int from_tty)
 	  *(p + 1) = '\0';
 	}
 
+      cont = XNEW (struct execute_command_continuation);
+
+      cont->state = EXEC_CMD_STATE_PRE_HOOK;
+      cont->mark = mark;
+      cont->cmd = c;
+      cont->line = line;
+      cont->arg = arg;
+      cont->from_tty = from_tty;
+
+      old_chain = add_interpreter_continuation (execute_command_continuation,
+						cont,
+						execute_command_continuation_free);
+
       /* If this command has been pre-hooked, run the hook first.  */
       execute_cmd_pre_hook (c);
 
-      if (c->flags & DEPRECATED_WARN_USER)
-	deprecated_cmd_warning (&line);
-
-      if (c->class == class_user)
-	execute_user_command (c, arg);
-      else if (c->type == set_cmd || c->type == show_cmd)
-	do_setshow_command (arg, from_tty & caution, c);
-      else if (!cmd_func_p (c))
-	error (_("That is not a command, just a help topic."));
-      else if (deprecated_call_command_hook)
-	deprecated_call_command_hook (c, arg, from_tty & caution);
-      else
-	cmd_func (c, arg, from_tty & caution);
-       
-      /* If this command has been post-hooked, run the hook last.  */
-      execute_cmd_post_hook (c);
+      discard_cleanups (cleanup);
+      discard_cleanups (cleanup_if_error);
 
-    }
+      if (target_can_async_p ()
+	  && sync_execution
+	  && target_has_execution
+	  && is_running (inferior_ptid))
+	return;
 
-  /* Tell the user if the language has changed (except first time).
-     First make sure that a new frame has been selected, in case this
-     command or the hooks changed the program state.  */
-  deprecated_safe_get_selected_frame ();
-  if (current_language != expected_language)
-    {
-      if (language_mode == language_mode_auto && info_verbose)
-	{
-	  language_info (1);	/* Print what changed.  */
-	}
-      warned = 0;
+      do_interpreter_continuations (0, old_chain);
     }
-
-  /* Warn the user if the working language does not match the
-     language of the current frame.  Only warn the user if we are
-     actually running the program, i.e. there is a stack.  */
-  /* FIXME:  This should be cacheing the frame and only running when
-     the frame changes.  */
-
-  if (has_stack_frames ())
+  else
     {
-      flang = get_frame_language ();
-      if (!warned
-	  && flang != language_unknown
-	  && flang != current_language->la_language)
-	{
-	  printf_filtered ("%s\n", lang_frame_mismatch_warn);
-	  warned = 1;
-	}
+      execute_command_done ();
+      do_cleanups (cleanup);
+      discard_cleanups (cleanup_if_error);
     }
-
-  do_cleanups (cleanup);
-  discard_cleanups (cleanup_if_error);
 }
 
 /* Run execute_command for P and FROM_TTY.  Capture its output into the


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]