gdbsupport/event-loop: add a timeout parameter to gdb_do_one_event
authorPatrick Monnerat <patrick@monnerat.net>
Fri, 15 Jul 2022 15:18:32 +0000 (17:18 +0200)
committerPatrick Monnerat <patrick@monnerat.net>
Thu, 18 Aug 2022 09:56:38 +0000 (11:56 +0200)
Since commit b2d8657, having a per-interpreter event/command loop is not
possible anymore.

As Insight uses a GUI that has its own event loop, gdb and GUI event
loops have then to be "merged" (i.e.: work together). But this is
problematic as gdb_do_one_event is not aware of this alternate event
loop and thus may wait forever.

A solution is to delegate GUI events handling to the gdb events handler.
Insight uses Tck/Tk as GUI and the latter offers a "notifier" feature to
implement such a delegation. The Tcl notifier spec requires the event wait
function to support a timeout parameter. Unfortunately gdb_do_one_event
does not feature such a parameter.
This timeout cannot be implemented externally with a gdb timer, because
it would become an event by itself and thus can cause a legitimate event to
be missed if the timeout is 0.
Tcl implements "idle events" that are (internally) triggered only when no
other event is pending. For this reason, it can call the event wait function
with a 0 timeout quite often.

This patch implements a wait timeout to gdb_do_one_event. The initial
pending events monitoring is performed as before without the possibility
to enter a wait state. If no pending event has been found during this
phase, a timer is then created for the given timeout in order to re-use
the implemented timeout logic and the event wait is then performed.
This "internal" timer only limits the wait time and should never be triggered.
It is deleted upon gdb_do_one_event exit.

The new parameter defaults to "no timeout" (-1): as it is used by Insight
only, there is no need to update calls from the gdb source tree.

gdbsupport/event-loop.cc
gdbsupport/event-loop.h

index f078c1278a85da76435ea113ca36773212d2e219..1f01d73849f132857655c21115476389bb041bdb 100644 (file)
@@ -33,6 +33,8 @@
 #include <sys/types.h>
 #include "gdbsupport/gdb_sys_time.h"
 #include "gdbsupport/gdb_select.h"
+#include "gdbsupport/gdb_optional.h"
+#include "gdbsupport/scope-exit.h"
 
 /* See event-loop.h.  */
 
@@ -175,12 +177,17 @@ static int update_wait_timeout (void);
 static int poll_timers (void);
 \f
 /* Process one high level event.  If nothing is ready at this time,
-   wait for something to happen (via gdb_wait_for_event), then process
-   it.  Returns >0 if something was done otherwise returns <0 (this
-   can happen if there are no event sources to wait for).  */
+   wait at most MSTIMEOUT milliseconds for something to happen (via
+   gdb_wait_for_event), then process it.  Returns >0 if something was
+   done, <0 if there are no event sources to wait for, =0 if timeout occurred.
+   A timeout of 0 allows to serve an already pending event, but does not
+   wait if none found.
+   Setting the timeout to a negative value disables it.
+   The timeout is never used by gdb itself, it is however needed to
+   integrate gdb event handling within Insight's GUI event loop. */
 
 int
-gdb_do_one_event (void)
+gdb_do_one_event (int mstimeout)
 {
   static int event_source_head = 0;
   const int number_of_sources = 3;
@@ -227,17 +234,35 @@ gdb_do_one_event (void)
        return 1;
     }
 
+  if (!mstimeout)
+    return 0;  /* Null timeout: do not wait for an event. */
+
   /* Block waiting for a new event.  If gdb_wait_for_event returns -1,
      we should get out because this means that there are no event
      sources left.  This will make the event loop stop, and the
-     application exit.  */
+     application exit.
+     If a timeout has been given, a new timer is set accordingly
+     to abort event wait.  It is deleted upon gdb_wait_for_event
+     termination and thus should never be triggered.
+     When the timeout is reached, events are not monitored again:
+     they already have been checked in the loop above. */
 
-  if (gdb_wait_for_event (1) < 0)
-    return -1;
+  gdb::optional<int> timer_id;
 
-  /* If gdb_wait_for_event has returned 1, it means that one event has
-     been handled.  We break out of the loop.  */
-  return 1;
+  SCOPE_EXIT 
+    {
+      if (timer_id.has_value ())
+       delete_timer (*timer_id);
+    };
+
+  if (mstimeout > 0)
+    timer_id = create_timer (mstimeout,
+                            [] (gdb_client_data arg)
+                            {
+                              ((gdb::optional<int> *) arg)->reset ();
+                            },
+                            &timer_id);
+  return gdb_wait_for_event (1);
 }
 
 /* See event-loop.h  */
index 9ed592877abbd703f69b6d45cfe28d82013d6dce..c82493e9bdf8c880c8415996d7e5035db5de5b37 100644 (file)
@@ -76,7 +76,7 @@ typedef void (timer_handler_func) (gdb_client_data);
 
 /* Exported functions from event-loop.c */
 
-extern int gdb_do_one_event (void);
+extern int gdb_do_one_event (int mstimeout = -1);
 extern void delete_file_handler (int fd);
 
 /* Add a file handler/descriptor to the list of descriptors we are