From bac814af175e89d73b3bdda98d25d684ab5b3e7b Mon Sep 17 00:00:00 2001 From: Patrick Monnerat Date: Fri, 15 Jul 2022 17:18:32 +0200 Subject: [PATCH] gdbsupport/event-loop: add a timeout parameter to gdb_do_one_event 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 | 45 +++++++++++++++++++++++++++++++--------- gdbsupport/event-loop.h | 2 +- 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/gdbsupport/event-loop.cc b/gdbsupport/event-loop.cc index f078c1278a8..1f01d73849f 100644 --- a/gdbsupport/event-loop.cc +++ b/gdbsupport/event-loop.cc @@ -33,6 +33,8 @@ #include #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); /* 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 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 *) arg)->reset (); + }, + &timer_id); + return gdb_wait_for_event (1); } /* See event-loop.h */ diff --git a/gdbsupport/event-loop.h b/gdbsupport/event-loop.h index 9ed592877ab..c82493e9bdf 100644 --- a/gdbsupport/event-loop.h +++ b/gdbsupport/event-loop.h @@ -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 -- 2.30.2