gdb/
authorPedro Alves <palves@redhat.com>
Fri, 21 Mar 2008 17:09:35 +0000 (17:09 +0000)
committerPedro Alves <palves@redhat.com>
Fri, 21 Mar 2008 17:09:35 +0000 (17:09 +0000)
* target.h (struct target_ops): Delete to_async_mask_value and add
to_async_mask.
(target_is_async_p, target_async): Formatting.
(target_async_mask_value): Delete.
(target_async_mask): Delete function declaration, and add new
target macro with the same name.

* target.c (update_current_target): Replace to_async_mask_value by
to_async_mask.  Default to_async_mask to return_one.
(target_async_mask): Delete.
(find_default_can_async_p, find_default_is_async_p): New.
(init_dummy_target): register find_default_can_async_p and
find_default_is_async_p on the dummy target.

* linux-nat.c: Include inf-loop.h, event-loop.h and event-top.h.
(debug_linux_nat_async): New global.
(show_debug_linux_nat_async): New function.
(linux_nat_async_enabled, linux_nat_async_mask_value)
(linux_nat_event_pipe, linux_nat_num_queued_events)
(linux_nat_async_events_enabled): New globals.
(struct waitpid_result): New struct.
(waitpid_queue): New global.
(queued_waitpid, push_waitpid, drain_queued_events): New.
(my_waitpid): Call queued_waitpid.
(linux_child_follow_fork): Disable async events during the call.
(blocked_mask): Delete.
(sync_sigchld_action, async_sigchld_action): New globals.
(lin_lwp_attach_lwp): In sync mode, don't reblock SIGCHLD.  In
async mode, block events during the call.
(linux_nat_create_inferior): New.
(linux_nat_attach): In sync mode, restore the mask states.  In
async mode, wake the event loop immediatelly.
(detach_callback): Drain all queued events of the lwp we're
detaching from.
(linux_nat_detach): Block async mode, and drain events of the main
process.
(linux_nat_resume): If in async mode, mask async events during the
call.  If short circuiting, force event loop to wake up.  If
resuming, set target_executing, and register target events in the
event loop.
(pipe_to_local_event_queue, local_event_queue_to_pipe): New.
(linux_nat_wait): In async mode, block events during the call.
Only enable/disable passing SIGINT to the inferior in sync mode.
Get events from local waitpid queue.  If no interesting events was
found, return to events loop.  Reregister target events in the
event loop on exit.  In sync mode, no need to reblock SIGCHLD.
(linux_nat_kill): Disable events on entry.
(linux_nat_mourn_inferior): In sync mode, don't restore the masks
here.  Detach async mode from the event loop if there are no more
forks available, otherwise leave it on.
(sigchld_handler): Assure this is called only in sync mode.
(linux_async_permitted, linux_async_permitted_1): New globals.
(set_maintenance_linux_async_permitted)
(show_maintenance_linux_async_permitted): New functions.
(linux_nat_is_async_p, linux_nat_can_async_p)
(linux_nat_async_mask): New.
(linux_nat_event_pipe_pop, linux_nat_event_pipe_push): New.
(get_pending_events, async_sigchld_handler): New.
(linux_nat_async_events): New.
(async_terminal_is_ours): New global.
(linux_nat_terminal_inferior, linux_nat_terminal_ours): New.
(async_client_callback, async_client_context): New.
(linux_nat_async_file_handler, linux_nat_async)
(linux_nat_disable_async, linux_nat_enable_async): New.
(linux_nat_add_target): Register linux_nat_create_inferior,
linux_nat_can_async_p, linux_nat_is_async_p, linux_nat_async,
linux_nat_async_mask, linux_nat_terminal_inferior and
linux_nat_terminal_ours.
(_initialize_linux_nat): Remove local action variable, and update
code that used it to use sync_sigchld_action.  Add new
"lin-lwp-async" debug set/show command.  Put the "lin-lwp" debug
set/show command in the maintenance class.  Add new "linux-async"
maintenance set/show command.  Block SIGCHLD by default.  Setup
async_sichld_action, and sync_sigchld_action.  Install the default
async mode.
(lin_thread_get_thread_signals): Use a local sigset_t for blocking
the cancel signals.

* linux-thread-db.c (re_check_for_thread_db): New.
(clear_lwpid_callback): Handle TARGET_WAITKIND_IGNORE.
(thread_db_can_async_p, thread_db_is_async_p, thread_db_async)
(thread_db_async_mask): New.
(init_thread_db_ops): Register thread_db_can_async_p,
thread_db_is_async_p, thread_db_async and thread_db_async_mask.

* remote.c (remote_async_mask_value): New.
(remote_return_zero): New.
(init_remote_ops): Register remote_return_zero as callbacks of
to_can_async_p and to_is_async_p.
(remote_can_async_p, remote_is_async_p, remote_async): Update to
use remote_async_mask_value.
(remote_async_mask): New.
(init_remote_async_ops): Remove to_async_mask_value setting and
register remote_async_mask as to_async_mask callback in
remote_async_ops.

* Makefile.in (linux-nat.o): Update.

gdb/doc/
* gdb.texinfo (Debugging Output): Document
"set/show debug lin-lwp-async".
(Maintenance Commands): Document "maint set/show linux-async".

gdb/ChangeLog
gdb/Makefile.in
gdb/doc/ChangeLog
gdb/doc/gdb.texinfo
gdb/linux-nat.c
gdb/linux-thread-db.c
gdb/remote.c
gdb/target.c
gdb/target.h

index 30e87406b51ce703f2f65e2fde88f58ce8d3b2a6..58da78af0a1ee7c5a78ffb05ed64dfc1e26598e0 100644 (file)
@@ -1,3 +1,105 @@
+2008-03-21  Pedro Alves  <pedro@codesourcery.com>
+
+       Linux native async support.
+
+       * target.h (struct target_ops): Delete to_async_mask_value and add
+       to_async_mask.
+       (target_is_async_p, target_async): Formatting.
+       (target_async_mask_value): Delete.
+       (target_async_mask): Delete function declaration, and add new
+       target macro with the same name.
+
+       * target.c (update_current_target): Replace to_async_mask_value by
+       to_async_mask.  Default to_async_mask to return_one.
+       (target_async_mask): Delete.
+       (find_default_can_async_p, find_default_is_async_p): New.
+       (init_dummy_target): register find_default_can_async_p and
+       find_default_is_async_p on the dummy target.
+
+       * linux-nat.c: Include inf-loop.h, event-loop.h and event-top.h.
+       (debug_linux_nat_async): New global.
+       (show_debug_linux_nat_async): New function.
+       (linux_nat_async_enabled, linux_nat_async_mask_value)
+       (linux_nat_event_pipe, linux_nat_num_queued_events)
+       (linux_nat_async_events_enabled): New globals.
+       (struct waitpid_result): New struct.
+       (waitpid_queue): New global.
+       (queued_waitpid, push_waitpid, drain_queued_events): New.
+       (my_waitpid): Call queued_waitpid.
+       (linux_child_follow_fork): Disable async events during the call.
+       (blocked_mask): Delete.
+       (sync_sigchld_action, async_sigchld_action): New globals.
+       (lin_lwp_attach_lwp): In sync mode, don't reblock SIGCHLD.  In
+       async mode, block events during the call.
+       (linux_nat_create_inferior): New.
+       (linux_nat_attach): In sync mode, restore the mask states.  In
+       async mode, wake the event loop immediatelly.
+       (detach_callback): Drain all queued events of the lwp we're
+       detaching from.
+       (linux_nat_detach): Block async mode, and drain events of the main
+       process.
+       (linux_nat_resume): If in async mode, mask async events during the
+       call.  If short circuiting, force event loop to wake up.  If
+       resuming, set target_executing, and register target events in the
+       event loop.
+       (pipe_to_local_event_queue, local_event_queue_to_pipe): New.
+       (linux_nat_wait): In async mode, block events during the call.
+       Only enable/disable passing SIGINT to the inferior in sync mode.
+       Get events from local waitpid queue.  If no interesting events was
+       found, return to events loop.  Reregister target events in the
+       event loop on exit.  In sync mode, no need to reblock SIGCHLD.
+       (linux_nat_kill): Disable events on entry.
+       (linux_nat_mourn_inferior): In sync mode, don't restore the masks
+       here.  Detach async mode from the event loop if there are no more
+       forks available, otherwise leave it on.
+       (sigchld_handler): Assure this is called only in sync mode.
+       (linux_async_permitted, linux_async_permitted_1): New globals.
+       (set_maintenance_linux_async_permitted)
+       (show_maintenance_linux_async_permitted): New functions.
+       (linux_nat_is_async_p, linux_nat_can_async_p)
+       (linux_nat_async_mask): New.
+       (linux_nat_event_pipe_pop, linux_nat_event_pipe_push): New.
+       (get_pending_events, async_sigchld_handler): New.
+       (linux_nat_async_events): New.
+       (async_terminal_is_ours): New global.
+       (linux_nat_terminal_inferior, linux_nat_terminal_ours): New.
+       (async_client_callback, async_client_context): New.
+       (linux_nat_async_file_handler, linux_nat_async)
+       (linux_nat_disable_async, linux_nat_enable_async): New.
+       (linux_nat_add_target): Register linux_nat_create_inferior,
+       linux_nat_can_async_p, linux_nat_is_async_p, linux_nat_async,
+       linux_nat_async_mask, linux_nat_terminal_inferior and
+       linux_nat_terminal_ours.
+       (_initialize_linux_nat): Remove local action variable, and update
+       code that used it to use sync_sigchld_action.  Add new
+       "lin-lwp-async" debug set/show command.  Put the "lin-lwp" debug
+       set/show command in the maintenance class.  Add new "linux-async"
+       maintenance set/show command.  Block SIGCHLD by default.  Setup
+       async_sichld_action, and sync_sigchld_action.  Install the default
+       async mode.
+       (lin_thread_get_thread_signals): Use a local sigset_t for blocking
+       the cancel signals.
+
+       * linux-thread-db.c (re_check_for_thread_db): New.
+       (clear_lwpid_callback): Handle TARGET_WAITKIND_IGNORE.
+       (thread_db_can_async_p, thread_db_is_async_p, thread_db_async)
+       (thread_db_async_mask): New.
+       (init_thread_db_ops): Register thread_db_can_async_p,
+       thread_db_is_async_p, thread_db_async and thread_db_async_mask.
+
+       * remote.c (remote_async_mask_value): New.
+       (remote_return_zero): New.
+       (init_remote_ops): Register remote_return_zero as callbacks of
+       to_can_async_p and to_is_async_p.
+       (remote_can_async_p, remote_is_async_p, remote_async): Update to
+       use remote_async_mask_value.
+       (remote_async_mask): New.
+       (init_remote_async_ops): Remove to_async_mask_value setting and
+       register remote_async_mask as to_async_mask callback in
+       remote_async_ops.
+
+       * Makefile.in (linux-nat.o): Update.
+
 2008-03-21  Daniel Jacobowitz  <dan@codesourcery.com>
 
        * gdbthread.h (add_thread_with_info): New.
index e6c8095fc5f1f6ad8bae01e95763dbea60e7f9fa..6b63d79cc975c284caef0c9f920db50d4ec3833f 100644 (file)
@@ -2351,7 +2351,7 @@ linux-nat.o: linux-nat.c $(defs_h) $(inferior_h) $(target_h) $(gdb_string_h) \
        $(gdb_wait_h) $(gdb_assert_h) $(linux_nat_h) $(gdbthread_h) \
        $(gdbcmd_h) $(regcache_h) $(regset_h) $(inf_ptrace_h) $(auxv_h) \
        $(elf_bfd_h) $(gregset_h) $(gdbcore_h) $(gdbthread_h) $(gdb_stat_h) \
-       $(linux_fork_h)
+       $(linux_fork_h) $(inf_loop_h) $(event_loop_h) $(event_top_h)
 linux-thread-db.o: linux-thread-db.c $(defs_h) $(gdb_assert_h) \
        $(gdb_proc_service_h) $(gdb_thread_db_h) $(bfd_h) $(exceptions_h) \
        $(gdbthread_h) $(inferior_h) $(symfile_h) $(objfiles_h) $(target_h) \
index 4b5ae97c69d85ede5fd4f2262260b7cba69384f8..c9e53f8ccc54dadc827c954b47480d340888de8d 100644 (file)
@@ -1,3 +1,9 @@
+2008-03-21  Pedro Alves  <pedro@codesourcery.com>
+
+       * gdb.texinfo (Debugging Output): Document
+       "set/show debug lin-lwp-async".
+       (Maintenance Commands): Document "maint set/show linux-async".
+
 2008-03-21  Daniel Jacobowitz  <dan@codesourcery.com>
 
        * gdb.texinfo (Expressions): Update description of malloced arrays.
index 67c3dee2844becf93a2dd23223a545c72086c89e..1e79e329dd4cf15d4ce27c1653ad05a182c21e95 100644 (file)
@@ -16407,6 +16407,12 @@ Displays the current state of @value{GDBN} inferior debugging.
 Turns on or off debugging messages from the Linux LWP debug support.
 @item show debug lin-lwp
 Show the current state of Linux LWP debugging messages.
+@item set debug lin-lwp-async
+@cindex @sc{gnu}/Linux LWP async debug messages
+@cindex Linux lightweight processes
+Turns on or off debugging messages from the Linux LWP async debug support.
+@item show debug lin-lwp-async
+Show the current state of Linux LWP async debugging messages.
 @item set debug observer
 @cindex observer debugging info
 Turns on or off display of @value{GDBN} observer debugging.  This
@@ -23242,6 +23248,16 @@ data in a @file{gmon.out} file, be sure to move it to a safe location.
 Configuring with @samp{--enable-profiling} arranges for @value{GDBN} to be
 compiled with the @samp{-pg} compiler option.
 
+@kindex maint set linux-async
+@kindex maint show linux-async
+@cindex asynchronous support
+@item maint set linux-async
+@itemx maint show linux-async
+Control the GNU/Linux native asynchronous support of @value{GDBN}.
+
+GNU/Linux native asynchronous support will be disabled until you use
+the @samp{maint set linux-async} command to enable it.
+
 @kindex maint show-debug-regs
 @cindex x86 hardware debug registers
 @item maint show-debug-regs
index 770cf3135b16efe07e8de5f62c7594af10892dd7..b097408a900b53225fa6015a17acb824ddb7f3f4 100644 (file)
@@ -46,6 +46,9 @@
 #include "gdbthread.h"         /* for struct thread_info etc. */
 #include "gdb_stat.h"          /* for struct stat */
 #include <fcntl.h>             /* for O_RDONLY */
+#include "inf-loop.h"
+#include "event-loop.h"
+#include "event-top.h"
 
 #ifndef O_LARGEFILE
 #define O_LARGEFILE 0
@@ -113,6 +116,15 @@ show_debug_linux_nat (struct ui_file *file, int from_tty,
                    value);
 }
 
+static int debug_linux_nat_async = 0;
+static void
+show_debug_linux_nat_async (struct ui_file *file, int from_tty,
+                           struct cmd_list_element *c, const char *value)
+{
+  fprintf_filtered (file, _("Debugging of GNU/Linux async lwp module is %s.\n"),
+                   value);
+}
+
 static int linux_parent_pid;
 
 struct simple_pid_list
@@ -133,6 +145,158 @@ static int linux_supports_tracefork_flag = -1;
 
 static int linux_supports_tracevforkdone_flag = -1;
 
+/* Async mode support */
+
+/* To listen to target events asynchronously, we install a SIGCHLD
+   handler whose duty is to call waitpid (-1, ..., WNOHANG) to get all
+   the pending events into a pipe.  Whenever we're ready to handle
+   events asynchronously, this pipe is registered as the waitable file
+   handle in the event loop.  When we get to entry target points
+   coming out of the common code (target_wait, target_resume, ...),
+   that are going to call waitpid, we block SIGCHLD signals, and
+   remove all the events placed in the pipe into a local queue.  All
+   the subsequent calls to my_waitpid (a waitpid wrapper) check this
+   local queue first.  */
+
+/* True if async mode is currently on.  */
+static int linux_nat_async_enabled;
+
+/* Zero if the async mode, although enabled, is masked, which means
+   linux_nat_wait should behave as if async mode was off.  */
+static int linux_nat_async_mask_value = 1;
+
+/* The read/write ends of the pipe registered as waitable file in the
+   event loop.  */
+static int linux_nat_event_pipe[2] = { -1, -1 };
+
+/* Number of queued events in the pipe.  */
+static volatile int linux_nat_num_queued_events;
+
+/* If async mode is on, true if we're listening for events; false if
+   target events are blocked.  */
+static int linux_nat_async_events_enabled;
+
+static int linux_nat_async_events (int enable);
+static void pipe_to_local_event_queue (void);
+static void local_event_queue_to_pipe (void);
+static void linux_nat_event_pipe_push (int pid, int status, int options);
+static int linux_nat_event_pipe_pop (int* ptr_status, int* ptr_options);
+static void linux_nat_set_async_mode (int on);
+static void linux_nat_async (void (*callback)
+                            (enum inferior_event_type event_type, void *context),
+                            void *context);
+static int linux_nat_async_mask (int mask);
+
+/* Captures the result of a successful waitpid call, along with the
+   options used in that call.  */
+struct waitpid_result
+{
+  int pid;
+  int status;
+  int options;
+  struct waitpid_result *next;
+};
+
+/* A singly-linked list of the results of the waitpid calls performed
+   in the async SIGCHLD handler.  */
+static struct waitpid_result *waitpid_queue = NULL;
+
+static int
+queued_waitpid (int pid, int *status, int flags)
+{
+  struct waitpid_result *msg = waitpid_queue, *prev = NULL;
+
+  if (debug_linux_nat_async)
+    fprintf_unfiltered (gdb_stdlog,
+                       "\
+QWPID: linux_nat_async_events_enabled(%d), linux_nat_num_queued_events(%d)\n",
+                       linux_nat_async_events_enabled,
+                       linux_nat_num_queued_events);
+
+  if (flags & __WALL)
+    {
+      for (; msg; prev = msg, msg = msg->next)
+       if (pid == -1 || pid == msg->pid)
+         break;
+    }
+  else if (flags & __WCLONE)
+    {
+      for (; msg; prev = msg, msg = msg->next)
+       if (msg->options & __WCLONE
+           && (pid == -1 || pid == msg->pid))
+         break;
+    }
+  else
+    {
+      for (; msg; prev = msg, msg = msg->next)
+       if ((msg->options & __WCLONE) == 0
+           && (pid == -1 || pid == msg->pid))
+         break;
+    }
+
+  if (msg)
+    {
+      int pid;
+
+      if (prev)
+       prev->next = msg->next;
+      else
+       waitpid_queue = msg->next;
+
+      msg->next = NULL;
+      if (status)
+       *status = msg->status;
+      pid = msg->pid;
+
+      if (debug_linux_nat_async)
+       fprintf_unfiltered (gdb_stdlog, "QWPID: pid(%d), status(%x)\n",
+                           pid, msg->status);
+      xfree (msg);
+
+      return pid;
+    }
+
+  if (debug_linux_nat_async)
+    fprintf_unfiltered (gdb_stdlog, "QWPID: miss\n");
+
+  if (status)
+    *status = 0;
+  return -1;
+}
+
+static void
+push_waitpid (int pid, int status, int options)
+{
+  struct waitpid_result *event, *new_event;
+
+  new_event = xmalloc (sizeof (*new_event));
+  new_event->pid = pid;
+  new_event->status = status;
+  new_event->options = options;
+  new_event->next = NULL;
+
+  if (waitpid_queue)
+    {
+      for (event = waitpid_queue;
+          event && event->next;
+          event = event->next)
+       ;
+
+      event->next = new_event;
+    }
+  else
+    waitpid_queue = new_event;
+}
+
+/* Drain all queued event of PID.  If PID is -1, the effect is of
+   draining all events.  */
+static void
+drain_queued_events (int pid)
+{
+  while (queued_waitpid (pid, NULL, __WALL) != -1)
+    ;
+}
+
 \f
 /* Trivial list manipulation functions to keep track of a list of
    new stopped processes.  */
@@ -183,12 +347,21 @@ linux_tracefork_child (void)
   _exit (0);
 }
 
-/* Wrapper function for waitpid which handles EINTR.  */
+/* Wrapper function for waitpid which handles EINTR, and checks for
+   locally queued events.  */
 
 static int
 my_waitpid (int pid, int *status, int flags)
 {
   int ret;
+
+  /* There should be no concurrent calls to waitpid.  */
+  gdb_assert (!linux_nat_async_events_enabled);
+
+  ret = queued_waitpid (pid, status, flags);
+  if (ret != -1)
+    return ret;
+
   do
     {
       ret = waitpid (pid, status, flags);
@@ -362,6 +535,9 @@ linux_child_follow_fork (struct target_ops *ops, int follow_child)
   int has_vforked;
   int parent_pid, child_pid;
 
+  if (target_can_async_p ())
+    target_async (NULL, 0);
+
   get_last_target_status (&last_ptid, &last_status);
   has_vforked = (last_status.kind == TARGET_WAITKIND_VFORKED);
   parent_pid = ptid_get_lwp (last_ptid);
@@ -506,9 +682,7 @@ linux_child_follow_fork (struct target_ops *ops, int follow_child)
          fork_save_infrun_state (fp, 0);
        }
       else
-       {
-         target_detach (NULL, 0);
-       }
+       target_detach (NULL, 0);
 
       inferior_ptid = ptid_build (child_pid, child_pid, 0);
 
@@ -523,6 +697,9 @@ linux_child_follow_fork (struct target_ops *ops, int follow_child)
       follow_inferior_reset_breakpoints ();
     }
 
+  if (target_can_async_p ())
+    target_async (inferior_event_handler, 0);
+
   return 0;
 }
 
@@ -611,8 +788,11 @@ static sigset_t normal_mask;
    _initialize_linux_nat.  */
 static sigset_t suspend_mask;
 
-/* Signals to block to make that sigsuspend work.  */
-static sigset_t blocked_mask;
+/* SIGCHLD action for synchronous mode.  */
+struct sigaction sync_sigchld_action;
+
+/* SIGCHLD action for asynchronous mode.  */
+static struct sigaction async_sigchld_action;
 \f
 
 /* Prototypes for local functions.  */
@@ -837,16 +1017,12 @@ int
 lin_lwp_attach_lwp (ptid_t ptid)
 {
   struct lwp_info *lp;
+  int async_events_were_enabled = 0;
 
   gdb_assert (is_lwp (ptid));
 
-  /* Make sure SIGCHLD is blocked.  We don't want SIGCHLD events
-     to interrupt either the ptrace() or waitpid() calls below.  */
-  if (!sigismember (&blocked_mask, SIGCHLD))
-    {
-      sigaddset (&blocked_mask, SIGCHLD);
-      sigprocmask (SIG_BLOCK, &blocked_mask, NULL);
-    }
+  if (target_can_async_p ())
+    async_events_were_enabled = linux_nat_async_events (0);
 
   lp = find_lwp_pid (ptid);
 
@@ -919,9 +1095,38 @@ lin_lwp_attach_lwp (ptid_t ptid)
       lp->stopped = 1;
     }
 
+  if (async_events_were_enabled)
+    linux_nat_async_events (1);
+
   return 0;
 }
 
+static void
+linux_nat_create_inferior (char *exec_file, char *allargs, char **env,
+                          int from_tty)
+{
+  int saved_async = 0;
+
+  /* The fork_child mechanism is synchronous and calls target_wait, so
+     we have to mask the async mode.  */
+
+  if (target_can_async_p ())
+    saved_async = linux_nat_async_mask (0);
+  else
+    {
+      /* Restore the original signal mask.  */
+      sigprocmask (SIG_SETMASK, &normal_mask, NULL);
+      /* Make sure we don't block SIGCHLD during a sigsuspend.  */
+      suspend_mask = normal_mask;
+      sigdelset (&suspend_mask, SIGCHLD);
+    }
+
+  linux_ops->to_create_inferior (exec_file, allargs, env, from_tty);
+
+  if (saved_async)
+    linux_nat_async_mask (saved_async);
+}
+
 static void
 linux_nat_attach (char *args, int from_tty)
 {
@@ -934,6 +1139,15 @@ linux_nat_attach (char *args, int from_tty)
      attach all of them.  */
   linux_ops->to_attach (args, from_tty);
 
+  if (!target_can_async_p ())
+    {
+      /* Restore the original signal mask.  */
+      sigprocmask (SIG_SETMASK, &normal_mask, NULL);
+      /* Make sure we don't block SIGCHLD during a sigsuspend.  */
+      suspend_mask = normal_mask;
+      sigdelset (&suspend_mask, SIGCHLD);
+    }
+
   /* Make sure the initial process is stopped.  The user-level threads
      layer might want to poke around in the inferior, and that won't
      work if things haven't stabilized yet.  */
@@ -961,9 +1175,14 @@ linux_nat_attach (char *args, int from_tty)
   lp->status = W_STOPCODE (SIGSTOP);
   lp->resumed = 1;
   if (debug_linux_nat)
+    fprintf_unfiltered (gdb_stdlog,
+                       "LNA: waitpid %ld, faking SIGSTOP\n", (long) pid);
+  if (target_can_async_p ())
     {
-      fprintf_unfiltered (gdb_stdlog,
-                         "LLA: waitpid %ld, faking SIGSTOP\n", (long) pid);
+      /* Wake event loop with special token, to get to WFI.  */
+      linux_nat_event_pipe_push (-1, -1, -1);
+      /* Register in the event loop.  */
+      target_async (inferior_event_handler, 0);
     }
 }
 
@@ -1020,6 +1239,7 @@ detach_callback (struct lwp_info *lp, void *data)
                            target_pid_to_str (lp->ptid),
                            strsignal (WSTOPSIG (lp->status)));
 
+      drain_queued_events (GET_LWP (lp->ptid));
       delete_lwp (lp->ptid);
     }
 
@@ -1029,6 +1249,10 @@ detach_callback (struct lwp_info *lp, void *data)
 static void
 linux_nat_detach (char *args, int from_tty)
 {
+  int pid;
+  if (target_can_async_p ())
+    linux_nat_async (NULL, 0);
+
   iterate_over_lwps (detach_callback, NULL);
 
   /* Only the initial process should be left right now.  */
@@ -1039,12 +1263,12 @@ linux_nat_detach (char *args, int from_tty)
   /* Destroy LWP info; it's no longer valid.  */
   init_lwp_list ();
 
-  /* Restore the original signal mask.  */
-  sigprocmask (SIG_SETMASK, &normal_mask, NULL);
-  sigemptyset (&blocked_mask);
-
-  inferior_ptid = pid_to_ptid (GET_PID (inferior_ptid));
+  pid = GET_PID (inferior_ptid);
+  inferior_ptid = pid_to_ptid (pid);
   linux_ops->to_detach (args, from_tty);
+
+  if (target_can_async_p ())
+    drain_queued_events (pid);
 }
 
 /* Resume LP.  */
@@ -1098,6 +1322,10 @@ linux_nat_resume (ptid_t ptid, int step, enum target_signal signo)
 
   prune_lwps ();
 
+  if (target_can_async_p ())
+    /* Block events while we're here.  */
+    linux_nat_async_events (0);
+
   /* A specific PTID means `step only this process id'.  */
   resume_all = (PIDGET (ptid) == -1);
 
@@ -1162,6 +1390,13 @@ linux_nat_resume (ptid_t ptid, int step, enum target_signal signo)
                            "LLR: Short circuiting for status 0x%x\n",
                            lp->status);
 
+      if (target_can_async_p ())
+       {
+         /* Wake event loop with special token, to get to WFI.  */
+         linux_nat_event_pipe_push (-1, -1, -1);
+
+         target_async (inferior_event_handler, 0);
+       }
       return;
     }
 
@@ -1181,6 +1416,12 @@ linux_nat_resume (ptid_t ptid, int step, enum target_signal signo)
                        step ? "PTRACE_SINGLESTEP" : "PTRACE_CONT",
                        target_pid_to_str (ptid),
                        signo ? strsignal (signo) : "0");
+
+  if (target_can_async_p ())
+    {
+      target_executing = 1;
+      target_async (inferior_event_handler, 0);
+    }
 }
 
 /* Issue kill to specified lwp.  */
@@ -2032,6 +2273,57 @@ linux_nat_filter_event (int lwpid, int status, int options)
   return lp;
 }
 
+/* Get the events stored in the pipe into the local queue, so they are
+   accessible to queued_waitpid.  We need to do this, since it is not
+   always the case that the event at the head of the pipe is the event
+   we want.  */
+
+static void
+pipe_to_local_event_queue (void)
+{
+  if (debug_linux_nat_async)
+    fprintf_unfiltered (gdb_stdlog,
+                       "PTLEQ: linux_nat_num_queued_events(%d)\n",
+                       linux_nat_num_queued_events);
+  while (linux_nat_num_queued_events)
+    {
+      int lwpid, status, options;
+
+      lwpid = linux_nat_event_pipe_pop (&status, &options);
+      if (lwpid == -1 && status == -1 && options == -1)
+       /* Special wake up event loop token.  */
+       continue;
+
+      gdb_assert (lwpid > 0);
+      push_waitpid (lwpid, status, options);
+    }
+}
+
+/* Get the unprocessed events stored in the local queue back into the
+   pipe, so the event loop realizes there's something else to
+   process.  */
+
+static void
+local_event_queue_to_pipe (void)
+{
+  struct waitpid_result *w = waitpid_queue;
+  while (w)
+    {
+      struct waitpid_result *next = w->next;
+      linux_nat_event_pipe_push (w->pid,
+                                w->status,
+                                w->options);
+      xfree (w);
+      w = next;
+    }
+  waitpid_queue = NULL;
+
+  if (debug_linux_nat_async)
+    fprintf_unfiltered (gdb_stdlog,
+                       "LEQTP: linux_nat_num_queued_events(%d)\n",
+                       linux_nat_num_queued_events);
+}
+
 static ptid_t
 linux_nat_wait (ptid_t ptid, struct target_waitstatus *ourstatus)
 {
@@ -2041,6 +2333,9 @@ linux_nat_wait (ptid_t ptid, struct target_waitstatus *ourstatus)
   pid_t pid = PIDGET (ptid);
   sigset_t flush_mask;
 
+  if (debug_linux_nat_async)
+    fprintf_unfiltered (gdb_stdlog, "LLW: enter\n");
+
   /* The first time we get here after starting a new inferior, we may
      not have added it to the LWP list yet - this is the earliest
      moment at which we know its PID.  */
@@ -2056,12 +2351,9 @@ linux_nat_wait (ptid_t ptid, struct target_waitstatus *ourstatus)
 
   sigemptyset (&flush_mask);
 
-  /* Make sure SIGCHLD is blocked.  */
-  if (!sigismember (&blocked_mask, SIGCHLD))
-    {
-      sigaddset (&blocked_mask, SIGCHLD);
-      sigprocmask (SIG_BLOCK, &blocked_mask, NULL);
-    }
+  if (target_can_async_p ())
+    /* Block events while we're here.  */
+    target_async (NULL, 0);
 
 retry:
 
@@ -2085,7 +2377,7 @@ retry:
                                target_pid_to_str (lp->ptid));
        }
 
-      /* But if we don't fine one, we'll have to wait, and check both
+      /* But if we don't find one, we'll have to wait, and check both
          cloned and uncloned processes.  We start with the cloned
          processes.  */
       options = __WCLONE | WNOHANG;
@@ -2144,15 +2436,24 @@ retry:
       stop_wait_callback (lp, NULL);
     }
 
-  set_sigint_trap ();          /* Causes SIGINT to be passed on to the
-                                  attached process. */
-  set_sigio_trap ();
+  if (!target_can_async_p ())
+    {
+      /* Causes SIGINT to be passed on to the attached process.  */
+      set_sigint_trap ();
+      set_sigio_trap ();
+    }
 
   while (status == 0)
     {
       pid_t lwpid;
 
-      lwpid = my_waitpid (pid, &status, options);
+      if (target_can_async_p ())
+       /* In async mode, don't ever block.  Only look at the locally
+          queued events.  */
+       lwpid = queued_waitpid (pid, &status, options);
+      else
+       lwpid = my_waitpid (pid, &status, options);
+
       if (lwpid > 0)
        {
          gdb_assert (pid == -1 || lwpid == pid);
@@ -2180,17 +2481,38 @@ retry:
          /* Alternate between checking cloned and uncloned processes.  */
          options ^= __WCLONE;
 
-         /* And suspend every time we have checked both.  */
+         /* And every time we have checked both:
+            In async mode, return to event loop;
+            In sync mode, suspend waiting for a SIGCHLD signal.  */
          if (options & __WCLONE)
-           sigsuspend (&suspend_mask);
+           {
+             if (target_can_async_p ())
+               {
+                 /* No interesting event.  */
+                 ourstatus->kind = TARGET_WAITKIND_IGNORE;
+
+                 /* Get ready for the next event.  */
+                 target_async (inferior_event_handler, 0);
+
+                 if (debug_linux_nat_async)
+                   fprintf_unfiltered (gdb_stdlog, "LLW: exit (ignore)\n");
+
+                 return minus_one_ptid;
+               }
+
+             sigsuspend (&suspend_mask);
+           }
        }
 
       /* We shouldn't end up here unless we want to try again.  */
       gdb_assert (status == 0);
     }
 
-  clear_sigio_trap ();
-  clear_sigint_trap ();
+  if (!target_can_async_p ())
+    {
+      clear_sigio_trap ();
+      clear_sigint_trap ();
+    }
 
   gdb_assert (lp);
 
@@ -2287,6 +2609,13 @@ retry:
   else
     store_waitstatus (ourstatus, status);
 
+  /* Get ready for the next event.  */
+  if (target_can_async_p ())
+    target_async (inferior_event_handler, 0);
+
+  if (debug_linux_nat_async)
+    fprintf_unfiltered (gdb_stdlog, "LLW: exit\n");
+
   return lp->ptid;
 }
 
@@ -2366,6 +2695,9 @@ linux_nat_kill (void)
   ptid_t last_ptid;
   int status;
 
+  if (target_can_async_p ())
+    target_async (NULL, 0);
+
   /* If we're stopped while forking and we haven't followed yet,
      kill the other task.  We need to do this first because the
      parent will be sleeping if this is a vfork.  */
@@ -2380,7 +2712,10 @@ linux_nat_kill (void)
     }
 
   if (forks_exist_p ())
-    linux_fork_killall ();
+    {
+      linux_fork_killall ();
+      drain_queued_events (-1);
+    }
   else
     {
       /* Kill all LWP's ...  */
@@ -2401,13 +2736,13 @@ linux_nat_mourn_inferior (void)
   /* Destroy LWP info; it's no longer valid.  */
   init_lwp_list ();
 
-  /* Restore the original signal mask.  */
-  sigprocmask (SIG_SETMASK, &normal_mask, NULL);
-  sigemptyset (&blocked_mask);
-
   if (! forks_exist_p ())
-    /* Normal case, no other forks available.  */
-    linux_ops->to_mourn_inferior ();
+    {
+      /* Normal case, no other forks available.  */
+      if (target_can_async_p ())
+       linux_nat_async (NULL, 0);
+      linux_ops->to_mourn_inferior ();
+    }
   else
     /* Multi-fork case.  The current inferior_ptid has exited, but
        there are other viable forks to debug.  Delete the exiting
@@ -2475,6 +2810,13 @@ linux_nat_pid_to_str (ptid_t ptid)
 static void
 sigchld_handler (int signo)
 {
+  if (linux_nat_async_enabled
+      && linux_nat_async_events_enabled
+      && signo == SIGCHLD)
+    /* It is *always* a bug to hit this.  */
+    internal_error (__FILE__, __LINE__,
+                   "sigchld_handler called when async events are enabled");
+
   /* Do nothing.  The only reason for this handler is that it allows
      us to use sigsuspend in linux_nat_wait above to wait for the
      arrival of a SIGCHLD.  */
@@ -3233,6 +3575,376 @@ linux_trad_target (CORE_ADDR (*register_u_offset)(struct gdbarch *, int, int))
   return t;
 }
 
+/* Controls if async mode is permitted.  */
+static int linux_async_permitted = 0;
+
+/* The set command writes to this variable.  If the inferior is
+   executing, linux_nat_async_permitted is *not* updated.  */
+static int linux_async_permitted_1 = 0;
+
+static void
+set_maintenance_linux_async_permitted (char *args, int from_tty,
+                              struct cmd_list_element *c)
+{
+  if (target_has_execution)
+    {
+      linux_async_permitted_1 = linux_async_permitted;
+      error (_("Cannot change this setting while the inferior is running."));
+    }
+
+  linux_async_permitted = linux_async_permitted_1;
+  linux_nat_set_async_mode (linux_async_permitted);
+}
+
+static void
+show_maintenance_linux_async_permitted (struct ui_file *file, int from_tty,
+                           struct cmd_list_element *c, const char *value)
+{
+  fprintf_filtered (file, _("\
+Controlling the GNU/Linux inferior in asynchronous mode is %s.\n"),
+                   value);
+}
+
+/* target_is_async_p implementation.  */
+
+static int
+linux_nat_is_async_p (void)
+{
+  /* NOTE: palves 2008-03-21: We're only async when the user requests
+     it explicitly with the "maintenance set linux-async" command.
+     Someday, linux will always be async.  */
+  if (!linux_async_permitted)
+    return 0;
+
+  return 1;
+}
+
+/* target_can_async_p implementation.  */
+
+static int
+linux_nat_can_async_p (void)
+{
+  /* NOTE: palves 2008-03-21: We're only async when the user requests
+     it explicitly with the "maintenance set linux-async" command.
+     Someday, linux will always be async.  */
+  if (!linux_async_permitted)
+    return 0;
+
+  /* See target.h/target_async_mask.  */
+  return linux_nat_async_mask_value;
+}
+
+/* target_async_mask implementation.  */
+
+static int
+linux_nat_async_mask (int mask)
+{
+  int current_state;
+  current_state = linux_nat_async_mask_value;
+
+  if (current_state != mask)
+    {
+      if (mask == 0)
+       {
+         linux_nat_async (NULL, 0);
+         linux_nat_async_mask_value = mask;
+         /* We're in sync mode.  Make sure SIGCHLD isn't handled by
+            async_sigchld_handler when we come out of sigsuspend in
+            linux_nat_wait.  */
+         sigaction (SIGCHLD, &sync_sigchld_action, NULL);
+       }
+      else
+       {
+         /* Restore the async handler.  */
+         sigaction (SIGCHLD, &async_sigchld_action, NULL);
+         linux_nat_async_mask_value = mask;
+         linux_nat_async (inferior_event_handler, 0);
+       }
+    }
+
+  return current_state;
+}
+
+/* Pop an event from the event pipe.  */
+
+static int
+linux_nat_event_pipe_pop (int* ptr_status, int* ptr_options)
+{
+  struct waitpid_result event = {0};
+  int ret;
+
+  do
+    {
+      ret = read (linux_nat_event_pipe[0], &event, sizeof (event));
+    }
+  while (ret == -1 && errno == EINTR);
+
+  gdb_assert (ret == sizeof (event));
+
+  *ptr_status = event.status;
+  *ptr_options = event.options;
+
+  linux_nat_num_queued_events--;
+
+  return event.pid;
+}
+
+/* Push an event into the event pipe.  */
+
+static void
+linux_nat_event_pipe_push (int pid, int status, int options)
+{
+  int ret;
+  struct waitpid_result event = {0};
+  event.pid = pid;
+  event.status = status;
+  event.options = options;
+
+  do
+    {
+      ret = write (linux_nat_event_pipe[1], &event, sizeof (event));
+      gdb_assert ((ret == -1 && errno == EINTR) || ret == sizeof (event));
+    } while (ret == -1 && errno == EINTR);
+
+  linux_nat_num_queued_events++;
+}
+
+static void
+get_pending_events (void)
+{
+  int status, options, pid;
+
+  if (!linux_nat_async_enabled || !linux_nat_async_events_enabled)
+    internal_error (__FILE__, __LINE__,
+                   "get_pending_events called with async masked");
+
+  while (1)
+    {
+      status = 0;
+      options = __WCLONE | WNOHANG;
+
+      do
+       {
+         pid = waitpid (-1, &status, options);
+       }
+      while (pid == -1 && errno == EINTR);
+
+      if (pid <= 0)
+       {
+         options = WNOHANG;
+         do
+           {
+             pid = waitpid (-1, &status, options);
+           }
+         while (pid == -1 && errno == EINTR);
+       }
+
+      if (pid <= 0)
+       /* No more children reporting events.  */
+       break;
+
+      if (debug_linux_nat_async)
+       fprintf_unfiltered (gdb_stdlog, "\
+get_pending_events: pid(%d), status(%x), options (%x)\n",
+                           pid, status, options);
+
+      linux_nat_event_pipe_push (pid, status, options);
+    }
+
+  if (debug_linux_nat_async)
+    fprintf_unfiltered (gdb_stdlog, "\
+get_pending_events: linux_nat_num_queued_events(%d)\n",
+                       linux_nat_num_queued_events);
+}
+
+/* SIGCHLD handler for async mode.  */
+
+static void
+async_sigchld_handler (int signo)
+{
+  if (debug_linux_nat_async)
+    fprintf_unfiltered (gdb_stdlog, "async_sigchld_handler\n");
+
+  get_pending_events ();
+}
+
+/* Enable or disable async SIGCHLD handling.  */
+
+static int
+linux_nat_async_events (int enable)
+{
+  int current_state = linux_nat_async_events_enabled;
+
+  if (debug_linux_nat_async)
+    fprintf_unfiltered (gdb_stdlog,
+                       "LNAE: enable(%d): linux_nat_async_events_enabled(%d), "
+                       "linux_nat_num_queued_events(%d)\n",
+                       enable, linux_nat_async_events_enabled,
+                       linux_nat_num_queued_events);
+
+  if (current_state != enable)
+    {
+      sigset_t mask;
+      sigemptyset (&mask);
+      sigaddset (&mask, SIGCHLD);
+      if (enable)
+       {
+         /* Unblock target events.  */
+         linux_nat_async_events_enabled = 1;
+
+         local_event_queue_to_pipe ();
+         /* While in masked async, we may have not collected all the
+            pending events.  Get them out now.  */
+         get_pending_events ();
+         sigprocmask (SIG_UNBLOCK, &mask, NULL);
+       }
+      else
+       {
+         /* Block target events.  */
+         sigprocmask (SIG_BLOCK, &mask, NULL);
+         linux_nat_async_events_enabled = 0;
+         /* Get events out of queue, and make them available to
+            queued_waitpid / my_waitpid.  */
+         pipe_to_local_event_queue ();
+       }
+    }
+
+  return current_state;
+}
+
+static int async_terminal_is_ours = 1;
+
+/* target_terminal_inferior implementation.  */
+
+static void
+linux_nat_terminal_inferior (void)
+{
+  if (!target_is_async_p ())
+    {
+      /* Async mode is disabled.  */
+      terminal_inferior ();
+      return;
+    }
+
+  /* GDB should never give the terminal to the inferior, if the
+     inferior is running in the background (run&, continue&, etc.).
+     This check can be removed when the common code is fixed.  */
+  if (!sync_execution)
+    return;
+
+  terminal_inferior ();
+
+  if (!async_terminal_is_ours)
+    return;
+
+  delete_file_handler (input_fd);
+  async_terminal_is_ours = 0;
+  set_sigint_trap ();
+}
+
+/* target_terminal_ours implementation.  */
+
+void
+linux_nat_terminal_ours (void)
+{
+  if (!target_is_async_p ())
+    {
+      /* Async mode is disabled.  */
+      terminal_ours ();
+      return;
+    }
+
+  /* GDB should never give the terminal to the inferior if the
+     inferior is running in the background (run&, continue&, etc.),
+     but claiming it sure should.  */
+  terminal_ours ();
+
+  if (!sync_execution)
+    return;
+
+  if (async_terminal_is_ours)
+    return;
+
+  clear_sigint_trap ();
+  add_file_handler (input_fd, stdin_event_handler, 0);
+  async_terminal_is_ours = 1;
+}
+
+static void (*async_client_callback) (enum inferior_event_type event_type,
+                                     void *context);
+static void *async_client_context;
+
+static void
+linux_nat_async_file_handler (int error, gdb_client_data client_data)
+{
+  async_client_callback (INF_REG_EVENT, async_client_context);
+}
+
+/* target_async implementation.  */
+
+static void
+linux_nat_async (void (*callback) (enum inferior_event_type event_type,
+                                  void *context), void *context)
+{
+  if (linux_nat_async_mask_value == 0 || !linux_nat_async_enabled)
+    internal_error (__FILE__, __LINE__,
+                   "Calling target_async when async is masked");
+
+  if (callback != NULL)
+    {
+      async_client_callback = callback;
+      async_client_context = context;
+      add_file_handler (linux_nat_event_pipe[0],
+                       linux_nat_async_file_handler, NULL);
+
+      linux_nat_async_events (1);
+    }
+  else
+    {
+      async_client_callback = callback;
+      async_client_context = context;
+
+      linux_nat_async_events (0);
+      delete_file_handler (linux_nat_event_pipe[0]);
+    }
+  return;
+}
+
+/* Enable/Disable async mode.  */
+
+static void
+linux_nat_set_async_mode (int on)
+{
+  if (linux_nat_async_enabled != on)
+    {
+      if (on)
+       {
+         gdb_assert (waitpid_queue == NULL);
+         sigaction (SIGCHLD, &async_sigchld_action, NULL);
+
+         if (pipe (linux_nat_event_pipe) == -1)
+           internal_error (__FILE__, __LINE__,
+                           "creating event pipe failed.");
+
+         fcntl (linux_nat_event_pipe[0], F_SETFL, O_NONBLOCK);
+         fcntl (linux_nat_event_pipe[1], F_SETFL, O_NONBLOCK);
+       }
+      else
+       {
+         sigaction (SIGCHLD, &sync_sigchld_action, NULL);
+
+         drain_queued_events (-1);
+
+         linux_nat_num_queued_events = 0;
+         close (linux_nat_event_pipe[0]);
+         close (linux_nat_event_pipe[1]);
+         linux_nat_event_pipe[0] = linux_nat_event_pipe[1] = -1;
+
+       }
+    }
+  linux_nat_async_enabled = on;
+}
+
 void
 linux_nat_add_target (struct target_ops *t)
 {
@@ -3244,6 +3956,7 @@ linux_nat_add_target (struct target_ops *t)
   linux_ops = &linux_ops_saved;
 
   /* Override some methods for multithreading.  */
+  t->to_create_inferior = linux_nat_create_inferior;
   t->to_attach = linux_nat_attach;
   t->to_detach = linux_nat_detach;
   t->to_resume = linux_nat_resume;
@@ -3255,6 +3968,13 @@ linux_nat_add_target (struct target_ops *t)
   t->to_pid_to_str = linux_nat_pid_to_str;
   t->to_has_thread_control = tc_schedlock;
 
+  t->to_can_async_p = linux_nat_can_async_p;
+  t->to_is_async_p = linux_nat_is_async_p;
+  t->to_async = linux_nat_async;
+  t->to_async_mask = linux_nat_async_mask;
+  t->to_terminal_inferior = linux_nat_terminal_inferior;
+  t->to_terminal_ours = linux_nat_terminal_ours;
+
   /* We don't change the stratum; this target will sit at
      process_stratum and thread_db will set at thread_stratum.  This
      is a little strange, since this is a multi-threaded-capable
@@ -3292,7 +4012,7 @@ linux_nat_get_siginfo (ptid_t ptid)
 void
 _initialize_linux_nat (void)
 {
-  struct sigaction action;
+  sigset_t mask;
 
   add_info ("proc", linux_nat_info_proc_cmd, _("\
 Show /proc process information about any running process.\n\
@@ -3303,27 +4023,63 @@ Specify any of the following keywords for detailed info:\n\
   status   -- list a different bunch of random process info.\n\
   all      -- list all available /proc info."));
 
-  /* Save the original signal mask.  */
+  add_setshow_zinteger_cmd ("lin-lwp", class_maintenance,
+                           &debug_linux_nat, _("\
+Set debugging of GNU/Linux lwp module."), _("\
+Show debugging of GNU/Linux lwp module."), _("\
+Enables printf debugging output."),
+                           NULL,
+                           show_debug_linux_nat,
+                           &setdebuglist, &showdebuglist);
+
+  add_setshow_zinteger_cmd ("lin-lwp-async", class_maintenance,
+                           &debug_linux_nat_async, _("\
+Set debugging of GNU/Linux async lwp module."), _("\
+Show debugging of GNU/Linux async lwp module."), _("\
+Enables printf debugging output."),
+                           NULL,
+                           show_debug_linux_nat_async,
+                           &setdebuglist, &showdebuglist);
+
+  add_setshow_boolean_cmd ("linux-async", class_maintenance,
+                          &linux_async_permitted_1, _("\
+Set whether gdb controls the GNU/Linux inferior in asynchronous mode."), _("\
+Show whether gdb controls the GNU/Linux inferior in asynchronous mode."), _("\
+Tells gdb whether to control the GNU/Linux inferior in asynchronous mode."),
+                          set_maintenance_linux_async_permitted,
+                          show_maintenance_linux_async_permitted,
+                          &maintenance_set_cmdlist,
+                          &maintenance_show_cmdlist);
+
+  /* Block SIGCHLD by default.  Doing this early prevents it getting
+     unblocked if an exception is thrown due to an error while the
+     inferior is starting (sigsetjmp/siglongjmp).  */
+  sigemptyset (&mask);
+  sigaddset (&mask, SIGCHLD);
+  sigprocmask (SIG_BLOCK, &mask, NULL);
+
+  /* Save this mask as the default.  */
   sigprocmask (SIG_SETMASK, NULL, &normal_mask);
 
-  action.sa_handler = sigchld_handler;
-  sigemptyset (&action.sa_mask);
-  action.sa_flags = SA_RESTART;
-  sigaction (SIGCHLD, &action, NULL);
+  /* The synchronous SIGCHLD handler.  */
+  sync_sigchld_action.sa_handler = sigchld_handler;
+  sigemptyset (&sync_sigchld_action.sa_mask);
+  sync_sigchld_action.sa_flags = SA_RESTART;
+
+  /* Make it the default.  */
+  sigaction (SIGCHLD, &sync_sigchld_action, NULL);
 
   /* Make sure we don't block SIGCHLD during a sigsuspend.  */
   sigprocmask (SIG_SETMASK, NULL, &suspend_mask);
   sigdelset (&suspend_mask, SIGCHLD);
 
-  sigemptyset (&blocked_mask);
+  /* SIGCHLD handler for async mode.  */
+  async_sigchld_action.sa_handler = async_sigchld_handler;
+  sigemptyset (&async_sigchld_action.sa_mask);
+  async_sigchld_action.sa_flags = SA_RESTART;
 
-  add_setshow_zinteger_cmd ("lin-lwp", no_class, &debug_linux_nat, _("\
-Set debugging of GNU/Linux lwp module."), _("\
-Show debugging of GNU/Linux lwp module."), _("\
-Enables printf debugging output."),
-                           NULL,
-                           show_debug_linux_nat,
-                           &setdebuglist, &showdebuglist);
+  /* Install the default mode.  */
+  linux_nat_set_async_mode (linux_async_permitted);
 }
 \f
 
@@ -3359,7 +4115,9 @@ lin_thread_get_thread_signals (sigset_t *set)
 {
   struct sigaction action;
   int restart, cancel;
+  sigset_t blocked_mask;
 
+  sigemptyset (&blocked_mask);
   sigemptyset (set);
 
   restart = get_signo ("__pthread_sig_restart");
index a222d8bbc4210607283be340682f699ad7e787af..534c9b97a3f0e3ee184ec8342e59745c8d92b8df 100644 (file)
@@ -824,6 +824,9 @@ thread_db_wait (ptid_t ptid, struct target_waitstatus *ourstatus)
 
   ptid = target_beneath->to_wait (ptid, ourstatus);
 
+  if (ourstatus->kind == TARGET_WAITKIND_IGNORE)
+    return ptid;
+
   if (ourstatus->kind == TARGET_WAITKIND_EXITED
     || ourstatus->kind == TARGET_WAITKIND_SIGNALLED)
     return pid_to_ptid (-1);
@@ -884,6 +887,31 @@ thread_db_mourn_inferior (void)
   using_thread_db = 0;
 }
 
+static int
+thread_db_can_async_p (void)
+{
+  return target_beneath->to_can_async_p ();
+}
+
+static int
+thread_db_is_async_p (void)
+{
+  return target_beneath->to_is_async_p ();
+}
+
+static void
+thread_db_async (void (*callback) (enum inferior_event_type event_type,
+                                  void *context), void *context)
+{
+  return target_beneath->to_async (callback, context);
+}
+
+static int
+thread_db_async_mask (int mask)
+{
+  return target_beneath->to_async_mask (mask);
+}
+
 static int
 find_new_threads_callback (const td_thrhandle_t *th_p, void *data)
 {
@@ -1056,6 +1084,10 @@ init_thread_db_ops (void)
   thread_db_ops.to_get_thread_local_address
     = thread_db_get_thread_local_address;
   thread_db_ops.to_extra_thread_info = thread_db_extra_thread_info;
+  thread_db_ops.to_can_async_p = thread_db_can_async_p;
+  thread_db_ops.to_is_async_p = thread_db_is_async_p;
+  thread_db_ops.to_async = thread_db_async;
+  thread_db_ops.to_async_mask = thread_db_async_mask;
   thread_db_ops.to_magic = OPS_MAGIC;
 }
 
index 73fcc2a60e97d0e0217266abae49a47b1ca09030..43ca90f3b6170d770323ec9199a105909b4e5111 100644 (file)
@@ -481,6 +481,8 @@ static struct target_ops extended_remote_ops;
    extended_remote_ops, but with asynchronous support.  */
 static struct target_ops remote_async_ops;
 
+static int remote_async_mask_value = 1;
+
 static struct target_ops extended_async_remote_ops;
 
 /* FIXME: cagney/1999-09-23: Even though getpkt was called with
@@ -7176,6 +7178,12 @@ remote_command (char *args, int from_tty)
   help_list (remote_cmdlist, "remote ", -1, gdb_stdout);
 }
 
+static int
+remote_return_zero (void)
+{
+  return 0;
+}
+
 static void
 init_remote_ops (void)
 {
@@ -7229,6 +7237,8 @@ Specify the serial device it is connected to\n\
   remote_ops.to_flash_erase = remote_flash_erase;
   remote_ops.to_flash_done = remote_flash_done;
   remote_ops.to_read_description = remote_read_description;
+  remote_ops.to_can_async_p = remote_return_zero;
+  remote_ops.to_is_async_p = remote_return_zero;
 }
 
 /* Set up the extended remote vector by making a copy of the standard
@@ -7256,14 +7266,14 @@ static int
 remote_can_async_p (void)
 {
   /* We're async whenever the serial device is.  */
-  return (current_target.to_async_mask_value) && serial_can_async_p (remote_desc);
+  return remote_async_mask_value && serial_can_async_p (remote_desc);
 }
 
 static int
 remote_is_async_p (void)
 {
   /* We're async whenever the serial device is.  */
-  return (current_target.to_async_mask_value) && serial_is_async_p (remote_desc);
+  return remote_async_mask_value && serial_is_async_p (remote_desc);
 }
 
 /* Pass the SERIAL event on and up to the client.  One day this code
@@ -7287,7 +7297,7 @@ static void
 remote_async (void (*callback) (enum inferior_event_type event_type,
                                void *context), void *context)
 {
-  if (current_target.to_async_mask_value == 0)
+  if (remote_async_mask_value == 0)
     internal_error (__FILE__, __LINE__,
                    _("Calling remote_async when async is masked"));
 
@@ -7301,6 +7311,14 @@ remote_async (void (*callback) (enum inferior_event_type event_type,
     serial_async (remote_desc, NULL, NULL);
 }
 
+static int
+remote_async_mask (int new_mask)
+{
+  int curr_mask = remote_async_mask_value;
+  remote_async_mask_value = new_mask;
+  return curr_mask;
+}
+
 /* Target async and target extended-async.
 
    This are temporary targets, until it is all tested.  Eventually
@@ -7360,7 +7378,7 @@ Specify the serial device it is connected to (e.g. /dev/ttya).";
   remote_async_ops.to_can_async_p = remote_can_async_p;
   remote_async_ops.to_is_async_p = remote_is_async_p;
   remote_async_ops.to_async = remote_async;
-  remote_async_ops.to_async_mask_value = 1;
+  remote_async_ops.to_async_mask = remote_async_mask;
   remote_async_ops.to_magic = OPS_MAGIC;
   remote_async_ops.to_memory_map = remote_memory_map;
   remote_async_ops.to_flash_erase = remote_flash_erase;
index 4168c05959b3806d12fcc50777719bb98e2caec0..7cf6cf3bd7a785b5d80e76c5ed3c6b73a91aa457 100644 (file)
@@ -464,7 +464,7 @@ update_current_target (void)
       INHERIT (to_can_async_p, t);
       INHERIT (to_is_async_p, t);
       INHERIT (to_async, t);
-      INHERIT (to_async_mask_value, t);
+      INHERIT (to_async_mask, t);
       INHERIT (to_find_memory_regions, t);
       INHERIT (to_make_corefile_notes, t);
       INHERIT (to_get_thread_local_address, t);
@@ -637,6 +637,9 @@ update_current_target (void)
   de_fault (to_async,
            (void (*) (void (*) (enum inferior_event_type, void*), void*))
            tcomplain);
+  de_fault (to_async_mask,
+           (int (*) (int))
+           return_one);
   current_target.to_read_description = NULL;
 #undef de_fault
 
@@ -1694,14 +1697,6 @@ target_disconnect (char *args, int from_tty)
   tcomplain ();
 }
 
-int
-target_async_mask (int mask)
-{
-  int saved_async_masked_status = target_async_mask_value;
-  target_async_mask_value = mask;
-  return saved_async_masked_status;
-}
-
 /* Look through the list of possible targets for a target that can
    follow forks.  */
 
@@ -1835,6 +1830,28 @@ find_default_create_inferior (char *exec_file, char *allargs, char **env,
   return;
 }
 
+int
+find_default_can_async_p (void)
+{
+  struct target_ops *t;
+
+  t = find_default_run_target ("async");
+  if (t->to_can_async_p)
+    return (t->to_can_async_p) ();
+  return 0;
+}
+
+int
+find_default_is_async_p (void)
+{
+  struct target_ops *t;
+
+  t = find_default_run_target ("async");
+  if (t->to_is_async_p)
+    return (t->to_is_async_p) ();
+  return 0;
+}
+
 static int
 default_region_ok_for_hw_watchpoint (CORE_ADDR addr, int len)
 {
@@ -2099,6 +2116,8 @@ init_dummy_target (void)
   dummy_target.to_doc = "";
   dummy_target.to_attach = find_default_attach;
   dummy_target.to_create_inferior = find_default_create_inferior;
+  dummy_target.to_can_async_p = find_default_can_async_p;
+  dummy_target.to_is_async_p = find_default_is_async_p;
   dummy_target.to_pid_to_str = normal_pid_to_str;
   dummy_target.to_stratum = dummy_stratum;
   dummy_target.to_find_memory_regions = dummy_find_memory_regions;
index c3da3d57210a2dc78491b3020beb8550fc6f407b..8176a03e1bddde503e6e1fff980e02e5f084ec74 100644 (file)
@@ -414,9 +414,8 @@ struct target_ops
     /* ASYNC target controls */
     int (*to_can_async_p) (void);
     int (*to_is_async_p) (void);
-    void (*to_async) (void (*cb) (enum inferior_event_type, void *context),
-                     void *context);
-    int to_async_mask_value;
+    void (*to_async) (void (*) (enum inferior_event_type, void *), void *);
+    int (*to_async_mask) (int);
     int (*to_find_memory_regions) (int (*) (CORE_ADDR,
                                            unsigned long,
                                            int, int, int,
@@ -938,11 +937,11 @@ int target_follow_fork (int follow_child);
 #define target_can_async_p() (current_target.to_can_async_p ())
 
 /* Is the target in asynchronous execution mode? */
-#define target_is_async_p() (current_target.to_is_async_p())
+#define target_is_async_p() (current_target.to_is_async_p ())
 
 /* Put the target in async mode with the specified callback function. */
 #define target_async(CALLBACK,CONTEXT) \
-     (current_target.to_async((CALLBACK), (CONTEXT)))
+     (current_target.to_async ((CALLBACK), (CONTEXT)))
 
 /* This is to be used ONLY within call_function_by_hand(). It provides
    a workaround, to have inferior function calls done in sychronous
@@ -958,10 +957,8 @@ int target_follow_fork (int follow_child);
    the turning async on and off to the single execution commands,
    from where it is done currently, in remote_resume().  */
 
-#define        target_async_mask_value \
-     (current_target.to_async_mask_value)
-
-extern int target_async_mask (int mask);
+#define target_async_mask(MASK)        \
+  (current_target.to_async_mask (MASK))
 
 /* Converts a process id to a string.  Usually, the string just contains
    `process xyz', but on some systems it may contain