+2021-03-26  Simon Marchi  <simon.marchi@efficios.com>
+           Pedro Alves  <pedro@palves.net>
+
+       * async-event.c: Include "infrun.h".
+       (async_event_handler_marked): New.
+       * async-event.h (async_event_handler_marked): Declare.
+       * infrun.c (maybe_set_commit_resumed_all_targets): Switch to
+       inferior before calling target method.  Don't commit-resumed if
+       target_has_pending_events is true.
+       * remote.c (remote_target::has_pending_events): New.
+       * target-delegates.c: Regenerate.
+       * target.c (target_has_pending_events): New.
+       * target.h (target_ops::has_pending_events): New target method.
+       (target_has_pending_events): New.
+
 2021-03-26  Simon Marchi  <simon.marchi@efficios.com>
            Pedro Alves  <pedro@palves.net>
 
 
   async_handler_ptr->ready = 0;
 }
 
+/* See event-loop.h.  */
+
+bool
+async_event_handler_marked (async_event_handler *handler)
+{
+  return handler->ready;
+}
+
 /* Check if asynchronous event handlers are ready, and call the
    handler function for one that is.  */
 
 
    loop.  */
 extern void mark_async_event_handler (struct async_event_handler *handler);
 
+/* Return true if HANDLER is marked.  */
+extern bool async_event_handler_marked (async_event_handler *handler);
+
 /* Mark the handler (ASYNC_HANDLER_PTR) as NOT ready.  */
 
 extern void clear_async_event_handler (struct async_event_handler *handler);
 
 static void
 maybe_set_commit_resumed_all_targets ()
 {
+  scoped_restore_current_thread restore_thread;
+
   for (inferior *inf : all_non_exited_inferiors ())
     {
       process_stratum_target *proc_target = inf->process_target ();
          continue;
        }
 
+      switch_to_inferior_no_thread (inf);
+
+      if (target_has_pending_events ())
+       {
+         infrun_debug_printf ("not requesting commit-resumed for target %s, "
+                              "target has pending events",
+                              proc_target->shortname ());
+         continue;
+       }
+
       infrun_debug_printf ("enabling commit-resumed for target %s",
                           proc_target->shortname ());
 
 
   void commit_resumed () override;
   void resume (ptid_t, int, enum gdb_signal) override;
   ptid_t wait (ptid_t, struct target_waitstatus *, target_wait_flags) override;
+  bool has_pending_events () override;
 
   void fetch_registers (struct regcache *, int) override;
   void store_registers (struct regcache *, int) override;
   vcont_builder.flush ();
 }
 
+/* Implementation of target_has_pending_events.  */
+
+bool
+remote_target::has_pending_events ()
+{
+  if (target_can_async_p ())
+    {
+      remote_state *rs = get_remote_state ();
+
+      if (async_event_handler_marked (rs->remote_async_inferior_event_token))
+       return true;
+
+      /* Note that BUFCNT can be negative, indicating sticky
+        error.  */
+      if (rs->remote_desc->bufcnt != 0)
+       return true;
+    }
+  return false;
+}
+
 \f
 
 /* Non-stop version of target_stop.  Uses `vCont;t' to stop a remote
 
   bool is_async_p () override;
   void async (int arg0) override;
   int async_wait_fd () override;
+  bool has_pending_events () override;
   void thread_events (int arg0) override;
   bool supports_non_stop () override;
   bool always_non_stop_p () override;
   bool is_async_p () override;
   void async (int arg0) override;
   int async_wait_fd () override;
+  bool has_pending_events () override;
   void thread_events (int arg0) override;
   bool supports_non_stop () override;
   bool always_non_stop_p () override;
   return result;
 }
 
+bool
+target_ops::has_pending_events ()
+{
+  return this->beneath ()->has_pending_events ();
+}
+
+bool
+dummy_target::has_pending_events ()
+{
+  return false;
+}
+
+bool
+debug_target::has_pending_events ()
+{
+  bool result;
+  fprintf_unfiltered (gdb_stdlog, "-> %s->has_pending_events (...)\n", this->beneath ()->shortname ());
+  result = this->beneath ()->has_pending_events ();
+  fprintf_unfiltered (gdb_stdlog, "<- %s->has_pending_events (", this->beneath ()->shortname ());
+  fputs_unfiltered (") = ", gdb_stdlog);
+  target_debug_print_bool (result);
+  fputs_unfiltered ("\n", gdb_stdlog);
+  return result;
+}
+
 void
 target_ops::thread_events (int arg0)
 {
 
   current_inferior ()->top_target ()->commit_resumed ();
 }
 
+/* See target.h.  */
+
+bool
+target_has_pending_events ()
+{
+  return current_inferior ()->top_target ()->has_pending_events ();
+}
+
 void
 target_pass_signals (gdb::array_view<const unsigned char> pass_signals)
 {
 
       TARGET_DEFAULT_NORETURN (tcomplain ());
     virtual int async_wait_fd ()
       TARGET_DEFAULT_NORETURN (noprocess ());
+    /* Return true if the target has pending events to report to the
+       core.  If true, then GDB avoids resuming the target until all
+       pending events are consumed, so that multiple resumptions can
+       be coalesced as an optimization.  Most targets can't tell
+       whether they have pending events without calling target_wait,
+       so we default to returning false.  The only downside is that a
+       potential optimization is missed.  */
+    virtual bool has_pending_events ()
+      TARGET_DEFAULT_RETURN (false);
     virtual void thread_events (int)
       TARGET_DEFAULT_IGNORE ();
     /* This method must be implemented in some situations.  See the
                                   struct target_waitstatus *status,
                                   target_wait_flags options);
 
+/* Return true if the target has pending events to report to the core.
+   See target_ops::has_pending_events().  */
+
+extern bool target_has_pending_events ();
+
 /* Fetch at least register REGNO, or all regs if regno == -1.  No result.  */
 
 extern void target_fetch_registers (struct regcache *regcache, int regno);