public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH] Add a timeout parameter to gdb_do_one_event
@ 2021-08-23 18:23 Patrick Monnerat
  2021-08-26  3:24 ` Simon Marchi
  0 siblings, 1 reply; 10+ messages in thread
From: Patrick Monnerat @ 2021-08-23 18:23 UTC (permalink / raw)
  To: gdb-patches

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.

The solution is to implement a wait timeout to gdb_do_one_event. This
cannot be done externally as timers are event sources themselves.

The new parameter defaults to "no timeout": 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, 33 insertions(+), 14 deletions(-)

diff --git a/gdbsupport/event-loop.cc b/gdbsupport/event-loop.cc
index 98d1ada52cd..72c64dcdb72 100644
--- a/gdbsupport/event-loop.cc
+++ b/gdbsupport/event-loop.cc
@@ -177,16 +177,21 @@ 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.
+   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 some external (i.e.: 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;
   int current = 0;
+  int res = 0;
 
   /* First let's see if there are any asynchronous signal handlers
      that are ready.  These would be the result of invoking any of the
@@ -198,8 +203,6 @@ gdb_do_one_event (void)
      round-robin fashion.  */
   for (current = 0; current < number_of_sources; current++)
     {
-      int res;
-
       switch (event_source_head)
 	{
 	case 0:
@@ -232,14 +235,30 @@ gdb_do_one_event (void)
   /* 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.  */
-
-  if (gdb_wait_for_event (1) < 0)
-    return -1;
+     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 (mstimeout != 0)
+    {
+      int timerid = 0;
+
+      if (mstimeout > 0)
+	timerid = create_timer (mstimeout,
+				[] (gdb_client_data timeridp)
+				  {
+				    *((int *) timeridp) = 0;
+				  },
+				&timerid);
+      res = gdb_wait_for_event (1);
+      if (timerid)
+	delete_timer (timerid);
+    }
 
-  /* If gdb_wait_for_event has returned 1, it means that one event has
-     been handled.  We break out of the loop.  */
-  return 1;
+  return res;
 }
 
 /* See event-loop.h  */
diff --git a/gdbsupport/event-loop.h b/gdbsupport/event-loop.h
index dc4e4d59f03..cf62f654c1c 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.31.1


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH] Add a timeout parameter to gdb_do_one_event
  2021-08-23 18:23 [PATCH] Add a timeout parameter to gdb_do_one_event Patrick Monnerat
@ 2021-08-26  3:24 ` Simon Marchi
  2021-08-26 11:36   ` Patrick Monnerat
  0 siblings, 1 reply; 10+ messages in thread
From: Simon Marchi @ 2021-08-26  3:24 UTC (permalink / raw)
  To: Patrick Monnerat, gdb-patches

On 2021-08-23 2:23 p.m., Patrick Monnerat via Gdb-patches wrote:
> 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.
> 
> The solution is to implement a wait timeout to gdb_do_one_event. This
> cannot be done externally as timers are event sources themselves.
> 
> The new parameter defaults to "no timeout": as it is used by Insight
> only, there is no need to update calls from the gdb source tree.

So, Insight's main loop looks like:

  while True:
    call gdb_do_one_event with a timeout
    call gui_do_one_event with a timeout

?

> ---
>  gdbsupport/event-loop.cc | 45 ++++++++++++++++++++++++++++------------
>  gdbsupport/event-loop.h  |  2 +-
>  2 files changed, 33 insertions(+), 14 deletions(-)
> 
> diff --git a/gdbsupport/event-loop.cc b/gdbsupport/event-loop.cc
> index 98d1ada52cd..72c64dcdb72 100644
> --- a/gdbsupport/event-loop.cc
> +++ b/gdbsupport/event-loop.cc
> @@ -177,16 +177,21 @@ 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

mstimeout -> MSTIMEOUT

> +   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.
> +   Setting the timeout to a negative value disables it.

Does setting the timeout to 0 mean return immediately if nothing is
available?

> +   The timeout is never used by gdb itself, it is however needed to
> +   integrate gdb event handling within some external (i.e.: GUI) event
> +   loop. */

Err, you can probably say "Insight" here.  It's not like we want to
support programs doing this in general.  We make an exception for
Insight because of historical reasons, I suppose.

>  int
> -gdb_do_one_event (void)
> +gdb_do_one_event (int mstimeout)
>  {
>    static int event_source_head = 0;
>    const int number_of_sources = 3;
>    int current = 0;
> +  int res = 0;
>  
>    /* First let's see if there are any asynchronous signal handlers
>       that are ready.  These would be the result of invoking any of the
> @@ -198,8 +203,6 @@ gdb_do_one_event (void)
>       round-robin fashion.  */
>    for (current = 0; current < number_of_sources; current++)
>      {
> -      int res;
> -
>        switch (event_source_head)
>  	{
>  	case 0:
> @@ -232,14 +235,30 @@ gdb_do_one_event (void)
>    /* 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.  */
> -
> -  if (gdb_wait_for_event (1) < 0)
> -    return -1;
> +     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 (mstimeout != 0)
> +    {
> +      int timerid = 0;
> +
> +      if (mstimeout > 0)
> +	timerid = create_timer (mstimeout,
> +				[] (gdb_client_data timeridp)
> +				  {
> +				    *((int *) timeridp) = 0;
> +				  },
> +				&timerid);
> +      res = gdb_wait_for_event (1);
> +      if (timerid)
> +	delete_timer (timerid);

Probably not a practical concern, but by creating timers over and over,
for a reaaaaaaally long GDB session, the timer id will eventually wrap
and create_timer might return 0.  I don't know what will happen then.

Simon

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH] Add a timeout parameter to gdb_do_one_event
  2021-08-26  3:24 ` Simon Marchi
@ 2021-08-26 11:36   ` Patrick Monnerat
  2021-08-26 13:47     ` Simon Marchi
  2021-08-27 18:08     ` [PATCH] " Tom Tromey
  0 siblings, 2 replies; 10+ messages in thread
From: Patrick Monnerat @ 2021-08-26 11:36 UTC (permalink / raw)
  To: Simon Marchi, gdb-patches


On 8/26/21 5:24 AM, Simon Marchi wrote:
> On 2021-08-23 2:23 p.m., Patrick Monnerat via Gdb-patches wrote:
>> 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.
>>
>> The solution is to implement a wait timeout to gdb_do_one_event. This
>> cannot be done externally as timers are event sources themselves.
>>
>> The new parameter defaults to "no timeout": as it is used by Insight
>> only, there is no need to update calls from the gdb source tree.
> So, Insight's main loop looks like:
>
>    while True:
>      call gdb_do_one_event with a timeout
>      call gui_do_one_event with a timeout
>
> ?

Not exactly, although this is the first idea that emerges. But this 
approach is not reactive enough and consumes CPU uselessly.

The real implementation makes the GUI event loop call gdb_do_one_event 
and recursively. The actual event waiting is performed by 
gdb_do_one_event, but the GUI may define a timeout for it. The hard task 
here is to avoid infinite recursion.

As Insight GUI is Tcl/Tk, a Tcl C API feature called a notifier 
(https://www.tcl.tk/man/tcl8.4/TclLib/Notifier.html) allowed me to 
design such a strategy. See 
https://sourceware.org/git/?p=insight.git;a=blob;f=gdbtk/generic/gdbtk.c 
line 247 and under to satisfy your curiosity! But it is a quite large 
part of the interface between the 2 subsystems that was not needed 
before gdb commit b2d8657. Note that an additional patch (unsubmitted 
yet) is needed to map Tcl file events into gdb file handlers.

The alternate solution would have been to run the GUI in a separate 
thread, but that's even a bigger work!

>
>> ---
>>   gdbsupport/event-loop.cc | 45 ++++++++++++++++++++++++++++------------
>>   gdbsupport/event-loop.h  |  2 +-
>>   2 files changed, 33 insertions(+), 14 deletions(-)
>>
>> diff --git a/gdbsupport/event-loop.cc b/gdbsupport/event-loop.cc
>> index 98d1ada52cd..72c64dcdb72 100644
>> --- a/gdbsupport/event-loop.cc
>> +++ b/gdbsupport/event-loop.cc
>> @@ -177,16 +177,21 @@ 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
> mstimeout -> MSTIMEOUT
Even if not a constant but the name of a parameter?
>
>> +   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.
>> +   Setting the timeout to a negative value disables it.
> Does setting the timeout to 0 mean return immediately if nothing is
> available?
Yes,
>> +   The timeout is never used by gdb itself, it is however needed to
>> +   integrate gdb event handling within some external (i.e.: GUI) event
>> +   loop. */
> Err, you can probably say "Insight" here.  It's not like we want to
> support programs doing this in general.  We make an exception for
> Insight because of historical reasons, I suppose.
OK.
>
>>   int
>> -gdb_do_one_event (void)
>> +gdb_do_one_event (int mstimeout)
>>   {
>>     static int event_source_head = 0;
>>     const int number_of_sources = 3;
>>     int current = 0;
>> +  int res = 0;
>>   
>>     /* First let's see if there are any asynchronous signal handlers
>>        that are ready.  These would be the result of invoking any of the
>> @@ -198,8 +203,6 @@ gdb_do_one_event (void)
>>        round-robin fashion.  */
>>     for (current = 0; current < number_of_sources; current++)
>>       {
>> -      int res;
>> -
>>         switch (event_source_head)
>>   	{
>>   	case 0:
>> @@ -232,14 +235,30 @@ gdb_do_one_event (void)
>>     /* 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.  */
>> -
>> -  if (gdb_wait_for_event (1) < 0)
>> -    return -1;
>> +     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 (mstimeout != 0)
>> +    {
>> +      int timerid = 0;
>> +
>> +      if (mstimeout > 0)
>> +	timerid = create_timer (mstimeout,
>> +				[] (gdb_client_data timeridp)
>> +				  {
>> +				    *((int *) timeridp) = 0;
>> +				  },
>> +				&timerid);
>> +      res = gdb_wait_for_event (1);
>> +      if (timerid)
>> +	delete_timer (timerid);
> Probably not a practical concern, but by creating timers over and over,
> for a reaaaaaaally long GDB session, the timer id will eventually wrap
> and create_timer might return 0.  I don't know what will happen then.

Yes, you're right. I'll change it. Maybe an RAII class here too?

Merci,

Patrick


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH] Add a timeout parameter to gdb_do_one_event
  2021-08-26 11:36   ` Patrick Monnerat
@ 2021-08-26 13:47     ` Simon Marchi
  2021-08-26 15:14       ` Patrick Monnerat
  2021-08-27 18:08     ` [PATCH] " Tom Tromey
  1 sibling, 1 reply; 10+ messages in thread
From: Simon Marchi @ 2021-08-26 13:47 UTC (permalink / raw)
  To: Patrick Monnerat, gdb-patches



On 2021-08-26 7:36 a.m., Patrick Monnerat wrote:
> 
> On 8/26/21 5:24 AM, Simon Marchi wrote:
>> On 2021-08-23 2:23 p.m., Patrick Monnerat via Gdb-patches wrote:
>>> 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.
>>>
>>> The solution is to implement a wait timeout to gdb_do_one_event. This
>>> cannot be done externally as timers are event sources themselves.
>>>
>>> The new parameter defaults to "no timeout": as it is used by Insight
>>> only, there is no need to update calls from the gdb source tree.
>> So, Insight's main loop looks like:
>>
>>    while True:
>>      call gdb_do_one_event with a timeout
>>      call gui_do_one_event with a timeout
>>
>> ?
> 
> Not exactly, although this is the first idea that emerges. But this approach is not reactive enough and consumes CPU uselessly.

Indeed, happy to know it's not that.

> The real implementation makes the GUI event loop call gdb_do_one_event and recursively. The actual event waiting is performed by gdb_do_one_event, but the GUI may define a timeout for it. The hard task here is to avoid infinite recursion.
> 
> As Insight GUI is Tcl/Tk, a Tcl C API feature called a notifier (https://www.tcl.tk/man/tcl8.4/TclLib/Notifier.html) allowed me to design such a strategy. See https://sourceware.org/git/?p=insight.git;a=blob;f=gdbtk/generic/gdbtk.c line 247 and under to satisfy your curiosity! But it is a quite large part of the interface between the 2 subsystems that was not needed before gdb commit b2d8657. Note that an additional patch (unsubmitted yet) is needed to map Tcl file events into gdb file handlers.

Ok, so you use GDB's event loop, registering some additional events in
it.  For example, the user clicking a button will generate an event,
wake up gdb_wait_for_event and call your handler to go do the GUI work.
Is that it?

And the timeout is needed to call into the GUI subsystem to do some
periodic work?

>>> ---
>>>   gdbsupport/event-loop.cc | 45 ++++++++++++++++++++++++++++------------
>>>   gdbsupport/event-loop.h  |  2 +-
>>>   2 files changed, 33 insertions(+), 14 deletions(-)
>>>
>>> diff --git a/gdbsupport/event-loop.cc b/gdbsupport/event-loop.cc
>>> index 98d1ada52cd..72c64dcdb72 100644
>>> --- a/gdbsupport/event-loop.cc
>>> +++ b/gdbsupport/event-loop.cc
>>> @@ -177,16 +177,21 @@ 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
>> mstimeout -> MSTIMEOUT
> Even if not a constant but the name of a parameter?

Yes, if you look around, this is how we refer to parameter names in
comment.  Like it or hate it, that's how it is.

>>> +   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.
>>> +   Setting the timeout to a negative value disables it.
>> Does setting the timeout to 0 mean return immediately if nothing is
>> available?
> Yes,

Ok, can you mention it in the comment?  I don't think it is mentioned.

>> Probably not a practical concern, but by creating timers over and over,
>> for a reaaaaaaally long GDB session, the timer id will eventually wrap
>> and create_timer might return 0.  I don't know what will happen then.
> 
> Yes, you're right. I'll change it. Maybe an RAII class here too?

Not sure, event with an RAII class you need a way to flag whether a
timer was created or not.  Maybe optional?

  gdb::optional<int> timer_id;

  if (mstimeout > 0)
    timer_id = create_timer (...);

  res = gdb_wait_for_event (1);

  if (timer_id.has_value ())
    delete_timer (*timer_id);

Simon

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH] Add a timeout parameter to gdb_do_one_event
  2021-08-26 13:47     ` Simon Marchi
@ 2021-08-26 15:14       ` Patrick Monnerat
  2022-03-14 14:49         ` [PING] " Patrick Monnerat
  0 siblings, 1 reply; 10+ messages in thread
From: Patrick Monnerat @ 2021-08-26 15:14 UTC (permalink / raw)
  To: Simon Marchi, gdb-patches


On 8/26/21 3:47 PM, Simon Marchi wrote:
>
> On 2021-08-26 7:36 a.m., Patrick Monnerat wrote:
>> On 8/26/21 5:24 AM, Simon Marchi wrote:
>>> On 2021-08-23 2:23 p.m., Patrick Monnerat via Gdb-patches wrote:
>>>> 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.
>>>>
>>>> The solution is to implement a wait timeout to gdb_do_one_event. This
>>>> cannot be done externally as timers are event sources themselves.
>>>>
>>>> The new parameter defaults to "no timeout": as it is used by Insight
>>>> only, there is no need to update calls from the gdb source tree.
>>> So, Insight's main loop looks like:
>>>
>>>     while True:
>>>       call gdb_do_one_event with a timeout
>>>       call gui_do_one_event with a timeout
>>>
>>> ?
>> Not exactly, although this is the first idea that emerges. But this approach is not reactive enough and consumes CPU uselessly.
> Indeed, happy to know it's not that.
>
>> The real implementation makes the GUI event loop call gdb_do_one_event and recursively. The actual event waiting is performed by gdb_do_one_event, but the GUI may define a timeout for it. The hard task here is to avoid infinite recursion.
>>
>> As Insight GUI is Tcl/Tk, a Tcl C API feature called a notifier (https://www.tcl.tk/man/tcl8.4/TclLib/Notifier.html) allowed me to design such a strategy. See https://sourceware.org/git/?p=insight.git;a=blob;f=gdbtk/generic/gdbtk.c line 247 and under to satisfy your curiosity! But it is a quite large part of the interface between the 2 subsystems that was not needed before gdb commit b2d8657. Note that an additional patch (unsubmitted yet) is needed to map Tcl file events into gdb file handlers.
> Ok, so you use GDB's event loop, registering some additional events in
> it.  For example, the user clicking a button will generate an event,
> wake up gdb_wait_for_event and call your handler to go do the GUI work.
> Is that it?

To be short, yes. But a mouse click is a bad example as it is not 
handled exactly the same way in Unixes and W$.

Tcl "*events" are also timed calls (with their own queue) and "idle" 
(something that should be executed whenever no event is pending). The 
Tcl notifier is informed by the interpreter of the max time to wait, 
taking into account these 2 event sources.

I tried to implement this timeout with a gdb timer created before 
calling gdb_wait_for_event, but this failed as, if 0, it may be 
triggered before any other pending event because of the round-robin 
strategy of gdb_do_one_event initial loop.

>
> And the timeout is needed to call into the GUI subsystem to do some
> periodic work?
That's it. And as noted above, the actual timeout value is determined by 
Tcl.
>
>>>> ---
>>>>    gdbsupport/event-loop.cc | 45 ++++++++++++++++++++++++++++------------
>>>>    gdbsupport/event-loop.h  |  2 +-
>>>>    2 files changed, 33 insertions(+), 14 deletions(-)
>>>>
>>>> diff --git a/gdbsupport/event-loop.cc b/gdbsupport/event-loop.cc
>>>> index 98d1ada52cd..72c64dcdb72 100644
>>>> --- a/gdbsupport/event-loop.cc
>>>> +++ b/gdbsupport/event-loop.cc
>>>> @@ -177,16 +177,21 @@ 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
>>> mstimeout -> MSTIMEOUT
>> Even if not a constant but the name of a parameter?
> Yes, if you look around, this is how we refer to parameter names in
> comment.  Like it or hate it, that's how it is.
No problem: I just wanted to be sure it was not a misunderstanding.
>
>>>> +   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.
>>>> +   Setting the timeout to a negative value disables it.
>>> Does setting the timeout to 0 mean return immediately if nothing is
>>> available?
>> Yes,
> Ok, can you mention it in the comment?  I don't think it is mentioned.
Will do!
>
>>> Probably not a practical concern, but by creating timers over and over,
>>> for a reaaaaaaally long GDB session, the timer id will eventually wrap
>>> and create_timer might return 0.  I don't know what will happen then.
>> Yes, you're right. I'll change it. Maybe an RAII class here too?
> Not sure, event with an RAII class you need a way to flag whether a
> timer was created or not.  Maybe optional?
>
>    gdb::optional<int> timer_id;
>
>    if (mstimeout > 0)
>      timer_id = create_timer (...);
>
>    res = gdb_wait_for_event (1);
>
>    if (timer_id.has_value ())
>      delete_timer (*timer_id);
>
Thanks for the hint. I think both together would be optimal.

Patrick


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH] Add a timeout parameter to gdb_do_one_event
  2021-08-26 11:36   ` Patrick Monnerat
  2021-08-26 13:47     ` Simon Marchi
@ 2021-08-27 18:08     ` Tom Tromey
  2021-08-28  0:07       ` Patrick Monnerat
  1 sibling, 1 reply; 10+ messages in thread
From: Tom Tromey @ 2021-08-27 18:08 UTC (permalink / raw)
  To: Patrick Monnerat via Gdb-patches

>>>>> "Patrick" == Patrick Monnerat via Gdb-patches <gdb-patches@sourceware.org> writes:

Patrick> The real implementation makes the GUI event loop call gdb_do_one_event
Patrick> and recursively. The actual event waiting is performed by 
Patrick> gdb_do_one_event, but the GUI may define a timeout for it. The hard
Patrick> task here is to avoid infinite recursion.

I wonder sometimes how we'll handle integrating event loops when we want
to let users integrate Python await with GDB APIs.

Patrick> The alternate solution would have been to run the GUI in a separate
Patrick> thread, but that's even a bigger work!

This is what my Python GUI does, but indeed it's complicated.  It needed
a hack (to block some signals in the GUI thread), and it has to send
Python code back and forth between the GUI thread and the main thread,
because GDB isn't thread-safe.

Tom

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH] Add a timeout parameter to gdb_do_one_event
  2021-08-27 18:08     ` [PATCH] " Tom Tromey
@ 2021-08-28  0:07       ` Patrick Monnerat
  0 siblings, 0 replies; 10+ messages in thread
From: Patrick Monnerat @ 2021-08-28  0:07 UTC (permalink / raw)
  To: Tom Tromey, Patrick Monnerat via Gdb-patches


On 8/27/21 8:08 PM, Tom Tromey wrote:
>>>>>> "Patrick" == Patrick Monnerat via Gdb-patches <gdb-patches@sourceware.org> writes:
> Patrick> The real implementation makes the GUI event loop call gdb_do_one_event
> Patrick> and recursively. The actual event waiting is performed by
> Patrick> gdb_do_one_event, but the GUI may define a timeout for it. The hard
> Patrick> task here is to avoid infinite recursion.
>
> I wonder sometimes how we'll handle integrating event loops when we want
> to let users integrate Python await with GDB APIs.

I don't know much about it. Would you monitor gdb events from Python or 
an asyncio event loop from gdb?

In case you haven't seen it yet, I found that: 
https://docs.python.org/3/c-api/typeobj.html#async-object-structures.

>
> Patrick> The alternate solution would have been to run the GUI in a separate
> Patrick> thread, but that's even a bigger work!
>
> This is what my Python GUI does, but indeed it's complicated.  It needed
> a hack (to block some signals in the GUI thread), and it has to send
> Python code back and forth between the GUI thread and the main thread,
> because GDB isn't thread-safe.

Thanks for the comment Tom: it confirms what I "sniffed" !

I have to admit that, without the Tcl notifier feature, threads would 
have been the only solution.

It was (almost) easy to use gdb event system from Tcl because the latter 
provides support for it. The opposite is not true: gdb events cannot be 
delegated to some external event monitoring handler. If Insight is not 
the only piece of software having to support alternate concurrent event 
loops, shouldn't we dream of such a gdb feature? Just my 2 cents!


Patrick


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PING] Add a timeout parameter to gdb_do_one_event
  2021-08-26 15:14       ` Patrick Monnerat
@ 2022-03-14 14:49         ` Patrick Monnerat
  0 siblings, 0 replies; 10+ messages in thread
From: Patrick Monnerat @ 2022-03-14 14:49 UTC (permalink / raw)
  To: gdb-patches


If no additional modification is needed, I'm still interested in having 
this patch pushed.

Will rebase and resend it for ease of use.

Thanks in advance.

Patrick


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PING] Add a timeout parameter to gdb_do_one_event
@ 2022-07-15 17:22 Patrick Monnerat
  0 siblings, 0 replies; 10+ messages in thread
From: Patrick Monnerat @ 2022-07-15 17:22 UTC (permalink / raw)
  To: gdb-patches

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 <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  */
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.35.3


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PING] Add a timeout parameter to gdb_do_one_event
  2022-03-17 13:07 Re. " Patrick Monnerat
@ 2022-05-20 11:39 ` Patrick Monnerat
  0 siblings, 0 replies; 10+ messages in thread
From: Patrick Monnerat @ 2022-05-20 11:39 UTC (permalink / raw)
  To: gdb-patches


Patch at https://sourceware.org/pipermail/gdb-patches/2022-March/186734.html


If no more remarks and accepted, I'll appreciate a commit.

Thanks in advance,

Patrick


^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2022-07-15 17:23 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-08-23 18:23 [PATCH] Add a timeout parameter to gdb_do_one_event Patrick Monnerat
2021-08-26  3:24 ` Simon Marchi
2021-08-26 11:36   ` Patrick Monnerat
2021-08-26 13:47     ` Simon Marchi
2021-08-26 15:14       ` Patrick Monnerat
2022-03-14 14:49         ` [PING] " Patrick Monnerat
2021-08-27 18:08     ` [PATCH] " Tom Tromey
2021-08-28  0:07       ` Patrick Monnerat
2022-03-17 13:07 Re. " Patrick Monnerat
2022-05-20 11:39 ` [PING] " Patrick Monnerat
2022-07-15 17:22 Patrick Monnerat

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).