+2016-04-12 Pedro Alves <palves@redhat.com>
+
+ * defs.h: Extend QUIT-related comments to mention
+ interruptible_select.
+ (quit_serial_event_set, quit_serial_event_clear): Declare.
+ * event-top.c: Include "ser-event.h" and "gdb_select.h".
+ (quit_serial_event): New global.
+ (async_init_signals): Make quit_serial_event.
+ (quit_serial_event_set, quit_serial_event_clear)
+ (quit_serial_event_fd, interruptible_select): New functions.
+ * extension.c (set_quit_flag): Set the quit serial event.
+ (check_quit_flag): Clear the quit serial event.
+ * gdb_select.h (interruptible_select): New declaration.
+ * guile/scm-ports.c (ioscm_input_waiting): Use
+ interruptible_select instead of gdb_select.
+ * top.c (gdb_readline_no_editing): Likewise.
+ * ui-file.c (stdio_file_read): Likewise.
+
2016-04-12 Pedro Alves <palves@redhat.com>
* event-loop.c: Include "ser-event.h".
take a long time, and which ought to be interruptible, checks this
flag using the QUIT macro.
+ In addition to setting a flag, the SIGINT handler also marks a
+ select/poll-able file descriptor as read-ready. That is used by
+ interruptible_select in order to support interrupting blocking I/O
+ in a race-free manner.
+
These functions use the extension_language_ops API to allow extension
language(s) and GDB SIGINT handling to coexist seamlessly. */
connection. */
#define QUIT maybe_quit ()
+/* Set the serial event associated with the quit flag. */
+extern void quit_serial_event_set (void);
+
+/* Clear the serial event associated with the quit flag. */
+extern void quit_serial_event_clear (void);
+
/* * Languages represented in the symbol table and elsewhere.
This should probably be in language.h, but since enum's can't
be forward declared to satisfy opaque references before their
#include "annotate.h"
#include "maint.h"
#include "buffer.h"
+#include "ser-event.h"
+#include "gdb_select.h"
/* readline include files. */
#include "readline/readline.h"
}
\f
+/* The serial event associated with the QUIT flag. set_quit_flag sets
+ this, and check_quit_flag clears it. Used by interruptible_select
+ to be able to do interruptible I/O with no race with the SIGINT
+ handler. */
+static struct serial_event *quit_serial_event;
+
/* Initialization of signal handlers and tokens. There is a function
handle_sig* for each of the signals GDB cares about. Specifically:
SIGINT, SIGFPE, SIGQUIT, SIGTSTP, SIGHUP, SIGWINCH. These
{
initialize_async_signal_handlers ();
+ quit_serial_event = make_serial_event ();
+
signal (SIGINT, handle_sigint);
sigint_token =
create_async_signal_handler (async_request_quit, NULL);
#endif
}
-/* Tell the event loop what to do if SIGINT is received.
- See event-signal.c. */
+/* See defs.h. */
+
+void
+quit_serial_event_set (void)
+{
+ serial_event_set (quit_serial_event);
+}
+
+/* See defs.h. */
+
+void
+quit_serial_event_clear (void)
+{
+ serial_event_clear (quit_serial_event);
+}
+
+/* Return the selectable file descriptor of the serial event
+ associated with the quit flag. */
+
+static int
+quit_serial_event_fd (void)
+{
+ return serial_event_fd (quit_serial_event);
+}
+
+/* Handle a SIGINT. */
+
void
handle_sigint (int sig)
{
gdb_call_async_signal_handler (sigint_token, immediate_quit);
}
+/* See gdb_select.h. */
+
+int
+interruptible_select (int n,
+ fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
+ struct timeval *timeout)
+{
+ fd_set my_readfds;
+ int fd;
+ int res;
+
+ if (readfds == NULL)
+ {
+ readfds = &my_readfds;
+ FD_ZERO (&my_readfds);
+ }
+
+ fd = quit_serial_event_fd ();
+ FD_SET (fd, readfds);
+ if (n <= fd)
+ n = fd + 1;
+
+ do
+ {
+ res = gdb_select (n, readfds, writefds, exceptfds, timeout);
+ }
+ while (res == -1 && errno == EINTR);
+
+ if (res == 1 && FD_ISSET (fd, readfds))
+ {
+ errno = EINTR;
+ return -1;
+ }
+ return res;
+}
+
/* Handle GDB exit upon receiving SIGTERM if target_can_async_p (). */
static void
&& active_ext_lang->ops->set_quit_flag != NULL)
active_ext_lang->ops->set_quit_flag (active_ext_lang);
else
- quit_flag = 1;
+ {
+ quit_flag = 1;
+
+ /* Now wake up the event loop, or any interruptible_select. Do
+ this after setting the flag, because signals on Windows
+ actually run on a separate thread, and thus otherwise the
+ main code could be woken up and find quit_flag still
+ clear. */
+ quit_serial_event_set ();
+ }
}
/* Return true if the quit flag has been set, false otherwise.
/* This is written in a particular way to avoid races. */
if (quit_flag)
{
+ /* No longer need to wake up the event loop or any
+ interruptible_select. The caller handles the quit
+ request. */
+ quit_serial_event_clear ();
quit_flag = 0;
result = 1;
}
extern int gdb_select (int n, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
+/* Convenience wrapper around gdb_select that returns -1/EINTR if
+ set_quit_flag is set, either on entry or from a signal handler or
+ from a different thread while select is blocked. The quit flag is
+ not cleared on exit -- the caller is responsible to check it with
+ check_quit_flag or QUIT.
+
+ Note this does NOT return -1/EINTR if any signal handler other than
+ SIGINT runs, nor if the current SIGINT handler does not call
+ set_quit_flag. */
+extern int interruptible_select (int n,
+ fd_set *readfds,
+ fd_set *writefds,
+ fd_set *exceptfds,
+ struct timeval *timeout);
+
#endif /* !defined(GDB_SELECT_H) */
FD_ZERO (&input_fds);
FD_SET (fdes, &input_fds);
- num_found = gdb_select (num_fds, &input_fds, NULL, NULL, &timeout);
+ num_found = interruptible_select (num_fds,
+ &input_fds, NULL, NULL,
+ &timeout);
if (num_found < 0)
{
/* Guile doesn't export SIGINT hooks like Python does.
QUIT;
/* Wait until at least one byte of data is available. Control-C
- can interrupt gdb_select, but not fgetc. */
+ can interrupt interruptible_select, but not fgetc. */
FD_ZERO (&readfds);
FD_SET (fd, &readfds);
- if (gdb_select (fd + 1, &readfds, NULL, NULL, NULL) == -1)
+ if (interruptible_select (fd + 1, &readfds, NULL, NULL, NULL) == -1)
{
if (errno == EINTR)
{
internal_error (__FILE__, __LINE__,
_("stdio_file_read: bad magic number"));
- /* For the benefit of Windows, call gdb_select before reading from
- the file. Wait until at least one byte of data is available.
- Control-C can interrupt gdb_select, but not read. */
+ /* Wait until at least one byte of data is available, or we get
+ interrupted with Control-C. */
{
fd_set readfds;
+
FD_ZERO (&readfds);
FD_SET (stdio->fd, &readfds);
- if (gdb_select (stdio->fd + 1, &readfds, NULL, NULL, NULL) == -1)
+ if (interruptible_select (stdio->fd + 1, &readfds, NULL, NULL, NULL) == -1)
return -1;
}