[PATCH 28/30] target remote: Don't rely on immediate_quit (introduce quit handlers)

Pedro Alves
2016-03-18T19:19:34Z

remote.c is the last user of immediate_quit. It's relied on to
immediately break the initial remote connection sync up, if the user
does Ctrl-C, assuming that was because the target isn't responding.
At that stage, since the connection isn't synced yet, disconnecting is
the only safe thing to do. This commit reworks that, to not rely on
throwing from the SIGINT signal handler.

So, this commit:

- Introduces the concept of a "quit handler". This is used to
override what does the QUIT macro do when the quit flag is set.

- Makes the "struct serial" reachar / write code call QUIT in the
partial read/write loops, so the current quit handler is invoked
whenever a serial->read_prim / serial->write_prim returns EINTR.

- Makes the "struct serial" reachar / write code call
interruptible_select instead of gdb_select, so that QUITs are
detected in a race-free manner.

- Stops remote.c from setting immediate_quit during the initial
connection.

- Instead, we install a custom quit handler whenever we're calling
into the serial code. This custom quit handler knows to immediately
throw a quit when we're in the initial connection setup, and
otherwise defer handling the quit/Ctrl-C request to later, when
we're safely out of a packet command/response sequence. This also
is what is now responsible for handling "double Ctrl-C because
target connection is stuck/wedged."

- remote.c no longer installs a specialized SIGINT handlers, and
instead re-uses the quit flag. Since we want to rely on the QUIT
macro, the SIGINT handler must also set the quit. And the easiest
is just to not install custom SIGINT handler in remote.c. Let the
standard SIGINT handler do its job of setting the quit flag.
Centralizing SIGINT handlers seems like a good thing to me, anyway.

gdb/ChangeLog:
yyyy-mm-dd Pedro Alves <palves@redhat.com>

* defs.h (quit_handler_ftype, quit_handler)
(make_cleanup_override_quit_handler, default_quit_handler): New.
(QUIT): Adjust comments.
* event-top.c (default_quit_handler): New function.
(quit_handler): New global.
(struct quit_handler_cleanup_data): New.
(restore_quit_handler, restore_quit_handler_dtor)
(make_cleanup_override_quit_handler): New.
(async_request_quit): Call QUIT.
* remote.c (struct remote_state) <got_ctrlc_during_io>: New field.
(async_sigint_remote_twice_token, async_sigint_remote_token):
Delete.
(remote_close): Update comments.
(remote_start_remote): Don't set immediate_quit. Set starting_up
earlier.
(remote_serial_quit_handler, remote_unpush_and_throw): New
functions.
(remote_open_1): Clear got_ctrlc_during_io.
(async_initialize_sigint_signal_handler)
(async_handle_remote_sigint, async_handle_remote_sigint_twice)
(remote_check_pending_interrupt, async_remote_interrupt)
(async_remote_interrupt_twice)
(async_cleanup_sigint_signal_handler, ofunc)
(sync_remote_interrupt, sync_remote_interrupt_twice): Delete.
(remote_terminal_inferior, remote_terminal_ours): Remove async
checks.
(remote_wait_as): Don't install a SIGINT handler in sync mode.
(readchar, remote_serial_write): Override the quit handler with
remote_serial_quit_handler.
(getpkt_or_notif_sane_1): Don't call QUIT.
(initialize_remote_ops): Don't install
remote_check_pending_interrupt.
(_initialize_remote): Don't create async_sigint_remote_token and
async_sigint_remote_twice_token.
* ser-base.c (ser_base_wait_for): Call QUIT and use
interruptible_select.
(ser_base_write): Call QUIT.
* ser-go32.c (dos_readchar, dos_write): Call QUIT.
* ser-unix.c (wait_for): Don't use VTIME. Always take the
gdb_select path, but call QUIT and interruptible_select.
* utils.c (maybe_quit): Call the current quit handler. Don't call
target_check_pending_interrupt.
(defaulted_query, prompt_for_continue): Override the quit handler
with the default quit handler.
---
gdb/defs.h | 34 +++++++-
gdb/event-top.c | 66 +++++++++++++-
gdb/remote.c | 263 ++++++++++++++++++++++++--------------------------------
gdb/ser-base.c | 10 ++-
gdb/ser-go32.c | 4 +
gdb/ser-unix.c | 96 +++------------------
gdb/utils.c | 8 +-
7 files changed, 232 insertions(+), 249 deletions(-)

diff --git a/gdb/defs.h b/gdb/defs.h
index 006f660..83e4e11 100644
--- a/gdb/defs.h
+++ b/gdb/defs.h
@@ -145,6 +145,34 @@ extern int check_quit_flag (void);
/* * Set the quit flag. */
extern void set_quit_flag (void);

+/* The current quit handler (and its type). This is called from the
+ QUIT macro. See default_quit_handler below for default behavior.
+ Parts of GDB temporarily override this to e.g., completely suppress
+ Ctrl-C because it would not be safe to throw. E.g., normally, you
+ wouldn't want to quit between a RSP command and its response, as
+ that would break the communication with the target, but you may
+ still want to intercept the Ctrl-C and offer to disconnect if the
+ user presses Ctrl-C multiple times while the target is stuck
+ waiting for the wedged remote stub. */
+typedef void (quit_handler_ftype) (void);
+extern quit_handler_ftype *quit_handler;
+
+/* Override the current quit handler. Sets NEW_QUIT_HANDLER as
+ current quit handler, and installs a cleanup that when run restores
+ the previous quit handler. */
+struct cleanup *
+ make_cleanup_override_quit_handler (quit_handler_ftype *new_quit_handler);
+
+/* The default quit handler. Checks whether Ctrl-C was pressed, and
+ if so:
+
+ - If GDB owns the terminal, throws a quit exception.
+
+ - If GDB does not own the terminal, forwards the Ctrl-C to the
+ target.
+*/
+extern void default_quit_handler (void);
+
/* Flag that function quit should call quit_force. */
extern volatile int sync_quit_force_run;

@@ -156,10 +184,8 @@ extern void quit (void);

extern void maybe_quit (void);

-/* Check whether a Ctrl-C was typed, and if so, call quit. The target
- is given a chance to process the Ctrl-C. E.g., it may detect that
- repeated Ctrl-C requests were issued, and choose to close the
- connection. */
+/* Check whether a Ctrl-C was typed, and if so, call the current quit
+ handler. */
#define QUIT maybe_quit ()

/* Set the serial event associated with the quit flag. */
diff --git a/gdb/event-top.c b/gdb/event-top.c
index 7fedb48..386920e 100644
--- a/gdb/event-top.c
+++ b/gdb/event-top.c
@@ -827,6 +827,68 @@ quit_serial_event_fd (void)
return serial_event_fd (quit_serial_event);
}

+/* See defs.h. */
+
+void
+default_quit_handler (void)
+{
+ if (check_quit_flag ())
+ {
+ if (target_terminal_is_ours ())
+ quit ();
+ else
+ target_pass_ctrlc ();
+ }
+}
+
+/* See defs.h. */
+quit_handler_ftype *quit_handler = default_quit_handler;
+
+/* Data for make_cleanup_override_quit_handler. Wrap the previous
+ handler pointer in a data struct because it's not portable to cast
+ a function pointer to a data pointer, which is what make_cleanup
+ expects. */
+struct quit_handler_cleanup_data
+{
+ /* The previous quit handler. */
+ quit_handler_ftype *prev_handler;
+};
+
+/* Cleanup call that restores the previous quit handler. */
+
+static void
+restore_quit_handler (void *arg)
+{
+ struct quit_handler_cleanup_data *data
+ = (struct quit_handler_cleanup_data *) arg;
+
+ quit_handler = data->prev_handler;
+}
+
+/* Destructor for the quit handler cleanup. */
+
+static void
+restore_quit_handler_dtor (void *arg)
+{
+ xfree (arg);
+}
+
+/* See defs.h. */
+
+struct cleanup *
+make_cleanup_override_quit_handler (quit_handler_ftype *new_quit_handler)
+{
+ struct cleanup *old_chain;
+ struct quit_handler_cleanup_data *data;
+
+ data = XNEW (struct quit_handler_cleanup_data);
+ data->prev_handler = quit_handler;
+ old_chain = make_cleanup_dtor (restore_quit_handler, data,
+ restore_quit_handler_dtor);
+ quit_handler = new_quit_handler;
+ return old_chain;
+}
+
/* Handle a SIGINT. */

void
@@ -920,9 +982,7 @@ async_request_quit (gdb_client_data arg)
back here, that means that an exception was thrown to unwind the
current command before we got back to the event loop. So there
is no reason to call quit again here. */
-
- if (check_quit_flag ())
- quit ();
+ QUIT;
}

#ifdef SIGQUIT
diff --git a/gdb/remote.c b/gdb/remote.c
index 2aa4e07..2607c9d 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -97,14 +97,10 @@ static char *remote_exec_file_var;
enum { REMOTE_ALIGN_WRITES = 16 };

/* Prototypes for local functions. */
-static void async_cleanup_sigint_signal_handler (void *dummy);
static int getpkt_sane (char **buf, long *sizeof_buf, int forever);
static int getpkt_or_notif_sane (char **buf, long *sizeof_buf,
int forever, int *is_notif);

-static void async_handle_remote_sigint (int);
-static void async_handle_remote_sigint_twice (int);
-
static void remote_files_info (struct target_ops *ignore);

static void remote_prepare_to_store (struct target_ops *self,
@@ -141,8 +137,6 @@ static void remote_async (struct target_ops *ops, int enable);

static void remote_thread_events (struct target_ops *ops, int enable);

-static void sync_remote_interrupt_twice (int signo);
-
static void interrupt_query (void);

static void set_general_thread (struct ptid ptid);
@@ -241,6 +235,8 @@ static int stop_reply_queue_length (void);

static void readahead_cache_invalidate (void);

+static void remote_unpush_and_throw (void);
+
/* For "remote". */

static struct cmd_list_element *remote_cmdlist;
@@ -363,6 +359,14 @@ struct remote_state
responded to that. */
int ctrlc_pending_p;

+ /* True if we saw a Ctrl-C while reading or writing from/to the
+ remote descriptor. At that point it is not safe to send a remote
+ interrupt packet, so we instead remember we saw the Ctrl-C and
+ process it once we're done with sending/receiving the current
+ packet, which should be shortly. If however that takes too long,
+ and the user presses Ctrl-C again, we offer to disconnect. */
+ int got_ctrlc_during_io;
+
/* Descriptor for I/O to remote machine. Initialize it to NULL so that
remote_open knows that we don't have a file open when the program
starts. */
@@ -1689,10 +1693,6 @@ remote_remove_exec_catchpoint (struct target_ops *ops, int pid)
return 0;
}

-/* Tokens for use by the asynchronous signal handlers for SIGINT. */
-static struct async_signal_handler *async_sigint_remote_twice_token;
-static struct async_signal_handler *async_sigint_remote_token;
-
\f
/* Asynchronous signal handle registered as event loop source for
when we have pending events ready to be passed to the core. */
@@ -3497,8 +3497,7 @@ remote_close (struct target_ops *self)
if (rs->remote_desc == NULL)
return; /* already closed */

- /* Make sure we leave stdin registered in the event loop, and we
- don't leave the async SIGINT signal handler installed. */
+ /* Make sure we leave stdin registered in the event loop. */
remote_terminal_ours (self);

serial_close (rs->remote_desc);
@@ -3994,6 +3993,8 @@ process_initial_stop_replies (int from_tty)
set_last_target_status (inferior_ptid, thread->suspend.waitstatus);
}

+/* Start the remote connection and sync state. */
+
static void
remote_start_remote (int from_tty, struct target_ops *target, int extended_p)
{
@@ -4001,18 +4002,21 @@ remote_start_remote (int from_tty, struct target_ops *target, int extended_p)
struct packet_config *noack_config;
char *wait_status = NULL;

- immediate_quit++; /* Allow user to interrupt it. */
+ /* Signal other parts that we're going through the initial setup,
+ and so things may not be stable yet. E.g., we don't try to
+ install tracepoints until we've relocated symbols. Also, a
+ Ctrl-C before we're connected and synced up can't interrupt the
+ target. Instead, it offers to drop the (potentially wedged)
+ connection. */
+ rs->starting_up = 1;
+
QUIT;

if (interrupt_on_connect)
send_interrupt_sequence ();

/* Ack any packet which the remote side has already sent. */
- serial_write (rs->remote_desc, "+", 1);
-
- /* Signal other parts that we're going through the initial setup,
- and so things may not be stable yet. */
- rs->starting_up = 1;
+ remote_serial_write ("+", 1);

/* The first packet we send to the target is the optional "supported
packets" request. If the target can answer this, it will tell us
@@ -4197,7 +4201,6 @@ remote_start_remote (int from_tty, struct target_ops *target, int extended_p)
strcpy (rs->buf, wait_status);
rs->cached_wait_status = 1;

- immediate_quit--;
start_remote (from_tty); /* Initialize gdb process mechanisms. */
}
else
@@ -4831,6 +4834,58 @@ remote_query_supported (void)
}
}

+/* Serial QUIT handler for the remote serial descriptor.
+
+ Defers handling a Ctrl-C until we're done with the current
+ command/response packet sequence, unless:
+
+ - We're setting up the connection. Don't send a remote interrupt
+ request, as we're not fully synced yet. Quit immediately
+ instead.
+
+ - The target has been resumed in the foreground
+ (target_terminal_is_ours is false) with a synchronous resume
+ packet, and we're blocked waiting for the stop reply, thus a
+ Ctrl-C should be immediately sent to the target.
+
+ - We get a second Ctrl-C while still within the same serial read or
+ write. In that case the serial is seemingly wedged --- offer to
+ quit/disconnect.
+
+ - We see a second Ctrl-C without target response, after having
+ previously interrupted the target. In that case the target/stub
+ is probably wedged --- offer to quit/disconnect.
+*/
+
+static void
+remote_serial_quit_handler (void)
+{
+ struct remote_state *rs = get_remote_state ();
+
+ if (check_quit_flag ())
+ {
+ /* If we're starting up, we're not fully synced yet. Quit
+ immediately. */
+ if (rs->starting_up)
+ quit ();
+ else if (rs->got_ctrlc_during_io)
+ {
+ if (query (_("The target is not responding to GDB commands.\n"
+ "Stop debugging it? ")))
+ remote_unpush_and_throw ();
+ }
+ /* If ^C has already been sent once, offer to disconnect. */
+ else if (!target_terminal_is_ours () && rs->ctrlc_pending_p)
+ interrupt_query ();
+ /* All-stop protocol, and blocked waiting for stop reply. Send
+ an interrupt request. */
+ else if (!target_terminal_is_ours () && rs->waiting_for_stop_reply)
+ target_interrupt (inferior_ptid);
+ else
+ rs->got_ctrlc_during_io = 1;
+ }
+}
+
/* Remove any of the remote.c targets from target stack. Upper targets depend
on it so remove them first. */

@@ -4841,6 +4896,13 @@ remote_unpush_target (void)
}

static void
+remote_unpush_and_throw (void)
+{
+ remote_unpush_target ();
+ throw_error (TARGET_CLOSE_ERROR, _("Disconnected from target."));
+}
+
+static void
remote_open_1 (const char *name, int from_tty,
struct target_ops *target, int extended_p)
{
@@ -4929,6 +4991,7 @@ remote_open_1 (const char *name, int from_tty,
rs->extended = extended_p;
rs->waiting_for_stop_reply = 0;
rs->ctrlc_pending_p = 0;
+ rs->got_ctrlc_during_io = 0;

rs->general_thread = not_sent_ptid;
rs->continue_thread = not_sent_ptid;
@@ -5635,108 +5698,6 @@ remote_resume (struct target_ops *ops,
}
\f

-/* Set up the signal handler for SIGINT, while the target is
- executing, ovewriting the 'regular' SIGINT signal handler. */
-static void
-async_initialize_sigint_signal_handler (void)
-{
- signal (SIGINT, async_handle_remote_sigint);
-}
-
-/* Signal handler for SIGINT, while the target is executing. */
-static void
-async_handle_remote_sigint (int sig)
-{
- signal (sig, async_handle_remote_sigint_twice);
- /* Note we need to go through gdb_call_async_signal_handler in order
- to wake up the event loop on Windows. */
- gdb_call_async_signal_handler (async_sigint_remote_token, 0);
-}
-
-/* Signal handler for SIGINT, installed after SIGINT has already been
- sent once. It will take effect the second time that the user sends
- a ^C. */
-static void
-async_handle_remote_sigint_twice (int sig)
-{
- signal (sig, async_handle_remote_sigint);
- /* See note in async_handle_remote_sigint. */
- gdb_call_async_signal_handler (async_sigint_remote_twice_token, 0);
-}
-
-/* Implementation of to_check_pending_interrupt. */
-
-static void
-remote_check_pending_interrupt (struct target_ops *self)
-{
- struct async_signal_handler *token = async_sigint_remote_twice_token;
-
- if (async_signal_handler_is_marked (token))
- {
- clear_async_signal_handler (token);
- call_async_signal_handler (token);
- }
-}
-
-/* Perform the real interruption of the target execution, in response
- to a ^C. */
-static void
-async_remote_interrupt (gdb_client_data arg)
-{
- if (remote_debug)
- fprintf_unfiltered (gdb_stdlog, "async_remote_interrupt called\n");
-
- target_interrupt (inferior_ptid);
-}
-
-/* Perform interrupt, if the first attempt did not succeed. Just give
- up on the target alltogether. */
-static void
-async_remote_interrupt_twice (gdb_client_data arg)
-{
- if (remote_debug)
- fprintf_unfiltered (gdb_stdlog, "async_remote_interrupt_twice called\n");
-
- interrupt_query ();
-}
-
-/* Reinstall the usual SIGINT handlers, after the target has
- stopped. */
-static void
-async_cleanup_sigint_signal_handler (void *dummy)
-{
- signal (SIGINT, handle_sigint);
-}
-
-/* Send ^C to target to halt it. Target will respond, and send us a
- packet. */
-static void (*ofunc) (int);
-
-/* The command line interface's interrupt routine. This function is installed
- as a signal handler for SIGINT. The first time a user requests an
- interrupt, we call remote_interrupt to send a break or ^C. If there is no
- response from the target (it didn't stop when the user requested it),
- we ask the user if he'd like to detach from the target. */
-
-static void
-sync_remote_interrupt (int signo)
-{
- /* If this doesn't work, try more severe steps. */
- signal (signo, sync_remote_interrupt_twice);
-
- gdb_call_async_signal_handler (async_sigint_remote_token, 1);
-}
-
-/* The user typed ^C twice. */
-
-static void
-sync_remote_interrupt_twice (int signo)
-{
- signal (signo, ofunc);
- gdb_call_async_signal_handler (async_sigint_remote_twice_token, 1);
- signal (signo, sync_remote_interrupt);
-}
-
/* Non-stop version of target_stop. Uses `vCont;t' to stop a remote
thread, all threads of a remote process, or all threads of all
processes. */
@@ -5926,10 +5887,6 @@ interrupt_query (void)
static void
remote_terminal_inferior (struct target_ops *self)
{
- if (!target_async_permitted)
- /* Nothing to do. */
- return;
-
/* FIXME: cagney/1999-09-27: Make calls to target_terminal_*()
idempotent. The event-loop GDB talking to an asynchronous target
with a synchronous command calls this function from both
@@ -5940,7 +5897,6 @@ remote_terminal_inferior (struct target_ops *self)
return;
delete_file_handler (input_fd);
remote_async_terminal_ours_p = 0;
- async_initialize_sigint_signal_handler ();
/* NOTE: At this point we could also register our selves as the
recipient of all input. Any characters typed could then be
passed on down to the target. */
@@ -5949,14 +5905,9 @@ remote_terminal_inferior (struct target_ops *self)
static void
remote_terminal_ours (struct target_ops *self)
{
- if (!target_async_permitted)
- /* Nothing to do. */
- return;
-
/* See FIXME in remote_terminal_inferior. */
if (remote_async_terminal_ours_p)
return;
- async_cleanup_sigint_signal_handler (NULL);
add_file_handler (input_fd, stdin_event_handler, 0);
remote_async_terminal_ours_p = 1;
}
@@ -6927,15 +6878,6 @@ remote_wait_as (ptid_t ptid, struct target_waitstatus *status, int options)
return minus_one_ptid;
}

- if (!target_is_async_p ())
- {
- ofunc = signal (SIGINT, sync_remote_interrupt);
- /* If the user hit C-c before this packet, or between packets,
- pretend that it was hit right here. */
- if (check_quit_flag ())
- sync_remote_interrupt (SIGINT);
- }
-
/* FIXME: cagney/1999-09-27: If we're in async mode we should
_never_ wait for ever -> test on target_is_async_p().
However, before we do that we need to ensure that the caller
@@ -6943,9 +6885,6 @@ remote_wait_as (ptid_t ptid, struct target_waitstatus *status, int options)
ret = getpkt_or_notif_sane (&rs->buf, &rs->buf_size,
forever, &is_notif);

- if (!target_is_async_p ())
- signal (SIGINT, ofunc);
-
/* GDB gets a notification. Return to core as this event is
not interesting. */
if (ret != -1 && is_notif)
@@ -8163,16 +8102,29 @@ unpush_and_perror (const char *string)
safe_strerror (saved_errno));
}

-/* Read a single character from the remote end. */
+/* Read a single character from the remote end. The current quit
+ handler is overridden to avoid quitting in the middle of packet
+ sequence, as that would break communication with the remote server.
+ See remote_serial_quit_handler for more detail. */

static int
readchar (int timeout)
{
int ch;
struct remote_state *rs = get_remote_state ();
+ struct cleanup *old_chain;
+
+ old_chain = make_cleanup_override_quit_handler (remote_serial_quit_handler);
+
+ rs->got_ctrlc_during_io = 0;

ch = serial_readchar (rs->remote_desc, timeout);

+ if (rs->got_ctrlc_during_io)
+ set_quit_flag ();
+
+ do_cleanups (old_chain);
+
if (ch >= 0)
return ch;

@@ -8193,18 +8145,31 @@ readchar (int timeout)
}

/* Wrapper for serial_write that closes the target and throws if
- writing fails. */
+ writing fails. The current quit handler is overridden to avoid
+ quitting in the middle of packet sequence, as that would break
+ communication with the remote server. See
+ remote_serial_quit_handler for more detail. */

static void
remote_serial_write (const char *str, int len)
{
struct remote_state *rs = get_remote_state ();
+ struct cleanup *old_chain;
+
+ old_chain = make_cleanup_override_quit_handler (remote_serial_quit_handler);
+
+ rs->got_ctrlc_during_io = 0;

if (serial_write (rs->remote_desc, str, len))
{
unpush_and_perror (_("Remote communication error. "
"Target disconnected."));
}
+
+ if (rs->got_ctrlc_during_io)
+ set_quit_flag ();
+
+ do_cleanups (old_chain);
}

/* Send the command in *BUF to the remote machine, and read the reply
@@ -8729,7 +8694,6 @@ getpkt_or_notif_sane_1 (char **buf, long *sizeof_buf, int forever,

if (forever) /* Watchdog went off? Kill the target. */
{
- QUIT;
remote_unpush_target ();
throw_error (TARGET_CLOSE_ERROR,
_("Watchdog timeout has expired. "
@@ -13070,7 +13034,6 @@ Specify the serial device it is connected to\n\
remote_ops.to_stop = remote_stop;
remote_ops.to_interrupt = remote_interrupt;
remote_ops.to_pass_ctrlc = remote_pass_ctrlc;
- remote_ops.to_check_pending_interrupt = remote_check_pending_interrupt;
remote_ops.to_xfer_partial = remote_xfer_partial;
remote_ops.to_rcmd = remote_rcmd;
remote_ops.to_pid_to_exec_file = remote_pid_to_exec_file;
@@ -13471,12 +13434,6 @@ _initialize_remote (void)
when it exits. */
observer_attach_inferior_exit (discard_pending_stop_replies);

- /* Set up signal handlers. */
- async_sigint_remote_token =
- create_async_signal_handler (async_remote_interrupt, NULL);
- async_sigint_remote_twice_token =
- create_async_signal_handler (async_remote_interrupt_twice, NULL);
-
#if 0
init_remote_threadtests ();
#endif
diff --git a/gdb/ser-base.c b/gdb/ser-base.c
index 25af66a..c51be07 100644
--- a/gdb/ser-base.c
+++ b/gdb/ser-base.c
@@ -213,6 +213,7 @@ ser_base_wait_for (struct serial *scb, int timeout)
int numfds;
struct timeval tv;
fd_set readfds, exceptfds;
+ int nfds;

/* NOTE: Some OS's can scramble the READFDS when the select()
call fails (ex the kernel with Red Hat 5.2). Initialize all
@@ -226,10 +227,13 @@ ser_base_wait_for (struct serial *scb, int timeout)
FD_SET (scb->fd, &readfds);
FD_SET (scb->fd, &exceptfds);

+ QUIT;
+
+ nfds = scb->fd + 1;
if (timeout >= 0)
- numfds = gdb_select (scb->fd + 1, &readfds, 0, &exceptfds, &tv);
+ numfds = interruptible_select (nfds, &readfds, 0, &exceptfds, &tv);
else
- numfds = gdb_select (scb->fd + 1, &readfds, 0, &exceptfds, 0);
+ numfds = interruptible_select (nfds, &readfds, 0, &exceptfds, 0);

if (numfds <= 0)
{
@@ -455,6 +459,8 @@ ser_base_write (struct serial *scb, const void *buf, size_t count)

while (count > 0)
{
+ QUIT;
+
cc = scb->ops->write_prim (scb, str, count);

if (cc < 0)
diff --git a/gdb/ser-go32.c b/gdb/ser-go32.c
index 72bdc13..10c79ba 100644
--- a/gdb/ser-go32.c
+++ b/gdb/ser-go32.c
@@ -616,6 +616,8 @@ dos_readchar (struct serial *scb, int timeout)
then = rawclock () + (timeout * RAWHZ);
while ((c = dos_getc (port)) < 0)
{
+ QUIT;
+
if (timeout >= 0 && (rawclock () - then) >= 0)
return SERIAL_TIMEOUT;
}
@@ -794,6 +796,8 @@ dos_write (struct serial *scb, const void *buf, size_t count)

while (count > 0)
{
+ QUIT;
+
/* Send the data, fifosize bytes at a time. */
cnt = fifosize > count ? count : fifosize;
port->txbusy = 1;
diff --git a/gdb/ser-unix.c b/gdb/ser-unix.c
index 562e98b..2e7b1b4 100644
--- a/gdb/ser-unix.c
+++ b/gdb/ser-unix.c
@@ -449,10 +449,7 @@ hardwire_raw (struct serial *scb)
}

/* Wait for input on scb, with timeout seconds. Returns 0 on success,
- otherwise SERIAL_TIMEOUT or SERIAL_ERROR.
-
- For termio{s}, we actually just setup VTIME if necessary, and let the
- timeout occur in the read() in hardwire_read(). */
+ otherwise SERIAL_TIMEOUT or SERIAL_ERROR. */

/* FIXME: cagney/1999-09-16: Don't replace this with the equivalent
ser_base*() until the old TERMIOS/SGTTY/... timer code has been
@@ -466,7 +463,6 @@ hardwire_raw (struct serial *scb)
static int
wait_for (struct serial *scb, int timeout)
{
-#ifdef HAVE_SGTTY
while (1)
{
struct timeval tv;
@@ -483,92 +479,22 @@ wait_for (struct serial *scb, int timeout)
FD_ZERO (&readfds);
FD_SET (scb->fd, &readfds);

+ QUIT;
+
if (timeout >= 0)
- numfds = gdb_select (scb->fd + 1, &readfds, 0, 0, &tv);
+ numfds = interruptible_select (scb->fd + 1, &readfds, 0, 0, &tv);
else
- numfds = gdb_select (scb->fd + 1, &readfds, 0, 0, 0);
+ numfds = interruptible_select (scb->fd + 1, &readfds, 0, 0, 0);

- if (numfds <= 0)
- if (numfds == 0)
- return SERIAL_TIMEOUT;
- else if (errno == EINTR)
- continue;
- else
- return SERIAL_ERROR; /* Got an error from select or poll. */
+ if (numfds == -1 && errno == EINTR)
+ continue;
+ else if (numfds == -1)
+ return SERIAL_ERROR;
+ else if (numfds == 0)
+ return SERIAL_TIMEOUT;

return 0;
}
-#endif /* HAVE_SGTTY */
-
-#if defined HAVE_TERMIO || defined HAVE_TERMIOS
- if (timeout == scb->current_timeout)
- return 0;
-
- scb->current_timeout = timeout;
-
- {
- struct hardwire_ttystate state;
-
- if (get_tty_state (scb, &state))
- fprintf_unfiltered (gdb_stderr, "get_tty_state failed: %s\n",
- safe_strerror (errno));
-
-#ifdef HAVE_TERMIOS
- if (timeout < 0)
- {
- /* No timeout. */
- state.termios.c_cc[VTIME] = 0;
- state.termios.c_cc[VMIN] = 1;
- }
- else
- {
- state.termios.c_cc[VMIN] = 0;
- state.termios.c_cc[VTIME] = timeout * 10;
- if (state.termios.c_cc[VTIME] != timeout * 10)
- {
-
- /* If c_cc is an 8-bit signed character, we can't go
- bigger than this. If it is always unsigned, we could use
- 25. */
-
- scb->current_timeout = 12;
- state.termios.c_cc[VTIME] = scb->current_timeout * 10;
- scb->timeout_remaining = timeout - scb->current_timeout;
- }
- }
-#endif
-
-#ifdef HAVE_TERMIO
- if (timeout < 0)
- {
- /* No timeout. */
- state.termio.c_cc[VTIME] = 0;
- state.termio.c_cc[VMIN] = 1;
- }
- else
- {
- state.termio.c_cc[VMIN] = 0;
- state.termio.c_cc[VTIME] = timeout * 10;
- if (state.termio.c_cc[VTIME] != timeout * 10)
- {
- /* If c_cc is an 8-bit signed character, we can't go
- bigger than this. If it is always unsigned, we could use
- 25. */
-
- scb->current_timeout = 12;
- state.termio.c_cc[VTIME] = scb->current_timeout * 10;
- scb->timeout_remaining = timeout - scb->current_timeout;
- }
- }
-#endif
-
- if (set_tty_state (scb, &state))
- fprintf_unfiltered (gdb_stderr, "set_tty_state failed: %s\n",
- safe_strerror (errno));
-
- return 0;
- }
-#endif /* HAVE_TERMIO || HAVE_TERMIOS */
}

/* Read a character with user-specified timeout. TIMEOUT is number of
diff --git a/gdb/utils.c b/gdb/utils.c
index 46765c5..5d778f6 100644
--- a/gdb/utils.c
+++ b/gdb/utils.c
@@ -1057,11 +1057,13 @@ quit (void)
void
maybe_quit (void)
{
- if (check_quit_flag () || sync_quit_force_run)
+ if (sync_quit_force_run)
quit ();
+
+ quit_handler ();
+
if (deprecated_interactive_hook)
deprecated_interactive_hook ();
- target_check_pending_interrupt ();
}

\f
@@ -1289,6 +1291,7 @@ defaulted_query (const char *ctlstr, const char defchar, va_list args)

/* We'll need to handle input. */
target_terminal_ours ();
+ make_cleanup_override_quit_handler (default_quit_handler);

while (1)
{
@@ -1863,6 +1866,7 @@ prompt_for_continue (void)
/* We'll need to handle input. */
make_cleanup_restore_target_terminal ();
target_terminal_ours ();
+ make_cleanup_override_quit_handler (default_quit_handler);

/* Call gdb_readline_wrapper, not readline, in order to keep an
event loop running. */
--
2.5.0