+/* 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 "set target-async" command.
+ Someday, linux will always be async. */
+ if (!target_async_permitted)
+ return 0;
+
+ /* See target.h/target_async_mask. */
+ return linux_nat_async_mask_value;
+}
+
+/* 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 "set target-async" command.
+ Someday, linux will always be async. */
+ if (!target_async_permitted)
+ return 0;
+
+ /* See target.h/target_async_mask. */
+ return linux_nat_async_mask_value;
+}
+
+static int
+linux_nat_supports_non_stop (void)
+{
+ return 1;
+}
+
+/* True if we want to support multi-process. To be removed when GDB
+ supports multi-exec. */
+
+int linux_multi_process = 0;
+
+static int
+linux_nat_supports_multi_process (void)
+{
+ return linux_multi_process;
+}
+
+/* target_async_mask implementation. */
+
+static int
+linux_nat_async_mask (int new_mask)
+{
+ int curr_mask = linux_nat_async_mask_value;
+
+ if (curr_mask != new_mask)
+ {
+ if (new_mask == 0)
+ {
+ linux_nat_async (NULL, 0);
+ linux_nat_async_mask_value = new_mask;
+ }
+ else
+ {
+ linux_nat_async_mask_value = new_mask;
+
+ /* If we're going out of async-mask in all-stop, then the
+ inferior is stopped. The next resume will call
+ target_async. In non-stop, the target event source
+ should be always registered in the event loop. Do so
+ now. */
+ if (non_stop)
+ linux_nat_async (inferior_event_handler, 0);
+ }
+ }
+
+ return curr_mask;
+}
+
+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. */
+
+static 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;
+
+/* SIGCHLD handler that serves two purposes: In non-stop/async mode,
+ so we notice when any child changes state, and notify the
+ event-loop; it allows us to use sigsuspend in linux_nat_wait_1
+ above to wait for the arrival of a SIGCHLD. */
+
+static void
+sigchld_handler (int signo)
+{
+ int old_errno = errno;
+
+ if (debug_linux_nat_async)
+ fprintf_unfiltered (gdb_stdlog, "sigchld\n");
+
+ if (signo == SIGCHLD
+ && linux_nat_event_pipe[0] != -1)
+ async_file_mark (); /* Let the event loop know that there are
+ events to handle. */
+
+ errno = old_errno;
+}
+
+/* Callback registered with the target events file descriptor. */
+
+static void
+handle_target_event (int error, gdb_client_data client_data)
+{
+ (*async_client_callback) (INF_REG_EVENT, async_client_context);
+}
+
+/* Create/destroy the target events pipe. Returns previous state. */
+
+static int
+linux_async_pipe (int enable)
+{
+ int previous = (linux_nat_event_pipe[0] != -1);
+
+ if (previous != enable)
+ {
+ sigset_t prev_mask;
+
+ block_child_signals (&prev_mask);
+
+ if (enable)
+ {
+ 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
+ {
+ close (linux_nat_event_pipe[0]);
+ close (linux_nat_event_pipe[1]);
+ linux_nat_event_pipe[0] = -1;
+ linux_nat_event_pipe[1] = -1;
+ }
+
+ restore_child_signals_mask (&prev_mask);
+ }
+
+ return previous;
+}
+
+/* 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 || !target_async_permitted)
+ internal_error (__FILE__, __LINE__,
+ "Calling target_async when async is masked");
+
+ if (callback != NULL)
+ {
+ async_client_callback = callback;
+ async_client_context = context;
+ if (!linux_async_pipe (1))
+ {
+ add_file_handler (linux_nat_event_pipe[0],
+ handle_target_event, NULL);
+ /* There may be pending events to handle. Tell the event loop
+ to poll them. */
+ async_file_mark ();
+ }
+ }
+ else
+ {
+ async_client_callback = callback;
+ async_client_context = context;
+ delete_file_handler (linux_nat_event_pipe[0]);
+ linux_async_pipe (0);
+ }
+ return;
+}
+
+/* Stop an LWP, and push a TARGET_SIGNAL_0 stop status if no other
+ event came out. */
+
+static int
+linux_nat_stop_lwp (struct lwp_info *lwp, void *data)
+{
+ if (!lwp->stopped)
+ {
+ int pid, status;
+ ptid_t ptid = lwp->ptid;
+
+ if (debug_linux_nat)
+ fprintf_unfiltered (gdb_stdlog,
+ "LNSL: running -> suspending %s\n",
+ target_pid_to_str (lwp->ptid));
+
+
+ stop_callback (lwp, NULL);
+ stop_wait_callback (lwp, NULL);
+
+ /* If the lwp exits while we try to stop it, there's nothing
+ else to do. */
+ lwp = find_lwp_pid (ptid);
+ if (lwp == NULL)
+ return 0;
+
+ /* If we didn't collect any signal other than SIGSTOP while
+ stopping the LWP, push a SIGNAL_0 event. In either case, the
+ event-loop will end up calling target_wait which will collect
+ these. */
+ if (lwp->status == 0)
+ lwp->status = W_STOPCODE (0);
+ async_file_mark ();
+ }
+ else
+ {
+ /* Already known to be stopped; do nothing. */
+
+ if (debug_linux_nat)
+ {
+ if (find_thread_pid (lwp->ptid)->stop_requested)
+ fprintf_unfiltered (gdb_stdlog, "\
+LNSL: already stopped/stop_requested %s\n",
+ target_pid_to_str (lwp->ptid));
+ else
+ fprintf_unfiltered (gdb_stdlog, "\
+LNSL: already stopped/no stop_requested yet %s\n",
+ target_pid_to_str (lwp->ptid));
+ }
+ }
+ return 0;
+}
+
+static void
+linux_nat_stop (ptid_t ptid)
+{
+ if (non_stop)
+ iterate_over_lwps (ptid, linux_nat_stop_lwp, NULL);
+ else
+ linux_ops->to_stop (ptid);
+}
+
+static void
+linux_nat_close (int quitting)
+{
+ /* Unregister from the event loop. */
+ if (target_is_async_p ())
+ target_async (NULL, 0);
+
+ /* Reset the async_masking. */
+ linux_nat_async_mask_value = 1;
+
+ if (linux_ops->to_close)
+ linux_ops->to_close (quitting);
+}
+