diff options
author | Pedro Alves <palves@redhat.com> | 2016-04-12 16:49:30 +0100 |
---|---|---|
committer | Pedro Alves <palves@redhat.com> | 2016-04-12 16:54:03 +0100 |
commit | f0881b37b6734328118a5683e1e18f65a8987c89 (patch) | |
tree | 107155131f2b8453e41bf9d6b83d116523585956 /gdb/extension.c | |
parent | 5cc3ce8b5fffa7413557b7e071d8471ae6e2fc88 (diff) | |
download | gdb-f0881b37b6734328118a5683e1e18f65a8987c89.zip gdb-f0881b37b6734328118a5683e1e18f65a8987c89.tar.gz gdb-f0881b37b6734328118a5683e1e18f65a8987c89.tar.bz2 |
Introduce interruptible_select
We have places where we call a blocking gdb_select expecting that a
Ctrl-C will unblock it. However, if the Ctrl-C is pressed just before
gdb_select, the SIGINT handler runs before gdb_select, and thus
gdb_select won't return.
For example gdb_readline_no_editing:
QUIT;
/* Wait until at least one byte of data is available. Control-C
can interrupt gdb_select, but not fgetc. */
FD_ZERO (&readfds);
FD_SET (fd, &readfds);
if (gdb_select (fd + 1, &readfds, NULL, NULL, NULL) == -1)
and stdio_file_read:
/* 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. */
{
fd_set readfds;
FD_ZERO (&readfds);
FD_SET (stdio->fd, &readfds);
if (gdb_select (stdio->fd + 1, &readfds, NULL, NULL, NULL) == -1)
return -1;
}
return read (stdio->fd, buf, length_buf);
This is a race classically fixed with either the self-pipe trick, or
by blocking SIGINT and then using pselect instead of select.
Blocking SIGINT most of the time would mean that check_quit_flag (and
thus QUIT) would need to do a syscall every time it is called, which
sounds best avoided, since QUIT is called in many loops. Thus we take
the self-pipe trick route (wrapped in a serial event).
Instead of having all places that need this manually add an extra file
descriptor to the set of gdb_select's watched file descriptors, we
introduce a wrapper, interruptible_select, that does that.
The Windows version of gdb_select actually does not suffer from this,
because mingw-hdep.c:gdb_call_async_signal_handler sets a Windows
event that gdb_select always waits on. So this patch can be seen as
generalization of that technique. We can't remove that extra event
from mingw-hdep.c until we get rid of immediate_quit though.
gdb/ChangeLog:
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.
Diffstat (limited to 'gdb/extension.c')
-rw-r--r-- | gdb/extension.c | 15 |
1 files changed, 14 insertions, 1 deletions
diff --git a/gdb/extension.c b/gdb/extension.c index d2c5669..c00db47 100644 --- a/gdb/extension.c +++ b/gdb/extension.c @@ -828,7 +828,16 @@ set_quit_flag (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. @@ -852,6 +861,10 @@ check_quit_flag (void) /* 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; } |