diff options
author | Pedro Alves <pedro@palves.net> | 2020-07-10 23:39:34 +0100 |
---|---|---|
committer | Pedro Alves <palves@redhat.com> | 2020-07-10 23:39:34 +0100 |
commit | 96118d114e3c53aadaf3fe5b5cf94979dbf56d87 (patch) | |
tree | 3353dc8f58455ff61922c7b2ff1f266dbd954f9d | |
parent | 54904d818b4d6b71101d4eec4bfab6342617ea5e (diff) | |
download | binutils-96118d114e3c53aadaf3fe5b5cf94979dbf56d87.zip binutils-96118d114e3c53aadaf3fe5b5cf94979dbf56d87.tar.gz binutils-96118d114e3c53aadaf3fe5b5cf94979dbf56d87.tar.bz2 |
Fix spurious unhandled remote %Stop notifications
In non-stop mode, remote targets mark an async event source whose
callback is supposed to result in calling remote_target::wait_ns to
either process the event queue, or acknowledge an incoming %Stop
notification.
The callback in question is remote_async_inferior_event_handler, where
we call inferior_event_handler, to end up in fetch_inferior_event ->
target_wait -> remote_target::wait -> remote_target::wait_ns.
A problem here however is that when debugging multiple targets,
fetch_inferior_event can pull events out of any target picked at
random, for event fairness. This means that when
remote_async_inferior_event_handler returns, remote_target::wait may
have not been called at all, and thus pending notifications may have
not been acked. Because async event sources auto-clear, when
remote_async_inferior_event_handler returns the async event handler is
no longer marked, so the event loop won't automatically call
remote_async_inferior_event_handler again to try to process the
pending remote notifications/queue. The result is that stop events
may end up not processed, e.g., "interrupt -a" seemingly not managing
to stop all threads.
Fix this by making remote_async_inferior_event_handler mark the event
handler again before returning, if necessary.
Maybe a better fix would be to make async event handlers not
auto-clear themselves, make that the responsibility of the callback,
so that the event loop would keep calling the callback automatically.
Or, we could try making so that fetch_inferior_event would optionally
handle events only for the target that it got passed down via
parameter. However, I don't think now just before branching is the
time to try to do any such change.
gdb/ChangeLog:
PR gdb/26199
* remote.c (remote_target::open_1): Pass remote target pointer as
data to create_async_event_handler.
(remote_async_inferior_event_handler): Mark async event handler
before returning if the remote target still has either pending
events or unacknowledged notifications.
-rw-r--r-- | gdb/ChangeLog | 9 | ||||
-rw-r--r-- | gdb/remote.c | 15 |
2 files changed, 23 insertions, 1 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog index c292927..b90e455 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,12 @@ +2020-07-10 Pedro Alves <pedro@palves.net> + + PR gdb/26199 + * remote.c (remote_target::open_1): Pass remote target pointer as + data to create_async_event_handler. + (remote_async_inferior_event_handler): Mark async event handler + before returning if the remote target still has either pending + events or unacknowledged notifications. + 2020-07-10 John Baldwin <jhb@FreeBSD.org> * fbsd-nat.h (fbsd_nat_target::supports_multi_process): New diff --git a/gdb/remote.c b/gdb/remote.c index f7f99dc..59075cb 100644 --- a/gdb/remote.c +++ b/gdb/remote.c @@ -5605,7 +5605,7 @@ remote_target::open_1 (const char *name, int from_tty, int extended_p) /* Register extra event sources in the event loop. */ rs->remote_async_inferior_event_token - = create_async_event_handler (remote_async_inferior_event_handler, NULL); + = create_async_event_handler (remote_async_inferior_event_handler, remote); rs->notif_state = remote_notif_state_allocate (remote); /* Reset the target state; these things will be queried either by @@ -14164,6 +14164,19 @@ static void remote_async_inferior_event_handler (gdb_client_data data) { inferior_event_handler (INF_REG_EVENT); + + remote_target *remote = (remote_target *) data; + remote_state *rs = remote->get_remote_state (); + + /* inferior_event_handler may have consumed an event pending on the + infrun side without calling target_wait on the REMOTE target, or + may have pulled an event out of a different target. Keep trying + for this remote target as long it still has either pending events + or unacknowledged notifications. */ + + if (rs->notif_state->pending_event[notif_client_stop.id] != NULL + || !rs->stop_reply_queue.empty ()) + mark_async_event_handler (rs->remote_async_inferior_event_token); } int |