About mixing Fl::awake(Fl_Awake_Handler, void*) and Fl::awake(void*)

17 views
Skip to first unread message

Manolo

unread,
Jul 5, 2021, 9:09:35 AM7/5/21
to fltk.coredev

Does any fellow developer know why the FLTK threading doc states :
"Don't intermix use of Fl::awake(Fl_Awake_Handler cb, void* userdata) and Fl::awake(void* message) calls in the same program as they may interact unpredictably on some platforms" ?

Do these platforms include Linux?

I'm asking because I'm exploring the use of Posix timers to implement Fl::add_timeout() 
for the Wayland platform (see the timer_create() and timer_settime() functions).
These functions allow to trigger the timer either as a signal or starting a thread.
I've chosen the thread option, and have the thread call Fl::awake(cb, data)
so that the timer's callback gets then called by the main thread in its event loop.

This solution works nicely in my tests, but has Fl::awake(cb, data) called by the library
when the app calls Fl::add_timeout(). Thus, a user of both Fl::add_timer()
and Fl::awake(void*) may find herself to intermix both forms of Fl::awake()
without knowing it if that solution is used to implement Wayland timers.

Ian MacArthur

unread,
Jul 5, 2021, 3:07:41 PM7/5/21
to coredev fltk
On 5 Jul 2021, at 14:09, Manolo wrote:
>
>
> Does any fellow developer know why the FLTK threading doc states :
> "Don't intermix use of Fl::awake(Fl_Awake_Handler cb, void* userdata) and Fl::awake(void* message) calls in the same program as they may interact unpredictably on some platforms” ?


A lot of that text was written by me - but ISTR the warning about mixing the two styles of awake() method predates my updates and came (maybe?) from Matt...
Who I think added the Fl::awake(Fl_Awake_Handler cb, void* userdata) stuff, whereas the Fl::awake(void* message) is older...

>
> Do these platforms include Linux?

I’m not sure - I stared at the code for a bit, and I’m not seeing anything that would obviously cause it to fail, but then again... If Matt is around (he sometimes is) then he might know more about this, I think.



Albrecht Schlosser

unread,
Jul 5, 2021, 3:33:09 PM7/5/21
to fltkc...@googlegroups.com
On 7/5/21 3:09 PM Manolo wrote:

Does any fellow developer know why the FLTK threading doc states :
"Don't intermix use of Fl::awake(Fl_Awake_Handler cb, void* userdata) and Fl::awake(void* message) calls in the same program as they may interact unpredictably on some platforms" ?

Do these platforms include Linux?

I'm asking because I'm exploring the use of Posix timers to implement Fl::add_timeout() 
for the Wayland platform (see the timer_create() and timer_settime() functions).
These functions allow to trigger the timer either as a signal or starting a thread.
I've chosen the thread option, and have the thread call Fl::awake(cb, data)
so that the timer's callback gets then called by the main thread in its event loop.

Despite the warning in the docs I believe that using Fl::awake(Fl_Awake_Handler cb, void* userdata) is always "on the safe side". As Ian wrote already, the warning may be from an older part of the docs.

I believe the warning is platform independent and the part "on some platforms" can probably be removed. I would add that Fl::awake(void* message) is always kinda unreliable but using Fl::awake(Fl_Awake_Handler cb, void* userdata) is always reliable as long as you don't use the same userdata pointer during the lifetime of such an Fl::awake() request. With "lifetime" I mean the time from writing to the memory pointed to by userdata until it is processed by the awake handler. This is the essential part.

The docs of Fl::awake(void* message) say: "The default message handler saves the last message which can be accessed using the Fl::thread_message() function." This means that there is only one (likely static) storage location which will be overwritten if you call Fl::awake(void* message) more frequently than the awake call can be serviced. This will drop all intermediate messages and only the last message can ever be retrieved by the user program with Fl::thread_message(). Hence, the user program is responsible for not calling Fl::awake(void* message) more often than can be serviced by the FLTK main loop.

Note: I'm currently not able to verify this in the source code, but I believe this is the case.

If you add to this load by using Fl::awake(void* message) internally this would increase the load and unpredictable behavior would be the result.

However, if you only use Fl::awake(Fl_Awake_Handler cb, void* userdata) with its specific handler and userdata I don't think that there would be a conflict with user code, even if the user program used Fl::awake(void* message).

The problem with mixing both types of calls is maybe caused by potentially inverting the order of events, hence this should always be avoided, but if since the timer callbacks would likely not be related to any user code ... it might not effect the user code at all. Hopefully.

Bill Spitzak

unread,
Jul 6, 2021, 4:34:37 PM7/6/21
to fltkc...@googlegroups.com
I'm pretty certain that Fl::awake(void* message) is exactly the same as Fl::awake(F, message) where F is a "Fl_Awake_Handler" that is internal to fltk. Basically it does not conflict or have any problems different from any other implementation of an Fl_Awake_Handler. This one just stores message in a static variable.

--
You received this message because you are subscribed to the Google Groups "fltk.coredev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to fltkcoredev...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/fltkcoredev/ffd0b794-0cca-37ba-d9b6-a723ed35bc56%40online.de.

Albrecht Schlosser

unread,
Jul 6, 2021, 4:54:59 PM7/6/21
to fltkc...@googlegroups.com
On 7/6/21 10:34 PM Bill Spitzak wrote:
I'm pretty certain that Fl::awake(void* message) is exactly the same as Fl::awake(F, message) where F is a "Fl_Awake_Handler" that is internal to fltk. Basically it does not conflict or have any problems different from any other implementation of an Fl_Awake_Handler. This one just stores message in a static variable.

The static variable is the problem, independent of any interaction or incompatibility with the other form of Fl::awake(). Since there is only one static variable callling Fl::thread_message() later in user code can only retrieve the last value assigned to this variable. Hence Fl::awake(void* message) is IMHO always unreliable unless you know that messages are queued infrequently, i.e. less than can be handled by events. A user moving the mouse permanently would likely lead to an "event overflow" dropping messages, whereas Fl::awake(F, message) queues all messages so no message can ever be lost.

imm

unread,
Jul 6, 2021, 5:08:06 PM7/6/21
to coredev fltk
On Tue, 6 Jul 2021, 21:55 Albrecht Schlosser wrote:

The static variable is the problem, independent of any interaction or incompatibility with the other form of Fl::awake(). Since there is only one static variable callling Fl::thread_message() later in user code can only retrieve the last value assigned to this variable. Hence Fl::awake(void* message) is IMHO always unreliable unless you know that messages are queued infrequently, i.e. less than can be handled by events. A user moving the mouse permanently would likely lead to an "event overflow" dropping messages, whereas Fl::awake(F, message) queues all messages so no message can ever be lost.

Well, strictly the queue holds the last 1024 messages. Once it is full, older messages will be lost to make room for newer ones... But if the user code is lagging 1000 events behind then there are bigger problems I'd hazard....

I was never really clear why the "old" method wasn't adapted to use the queue, though.
Perhaps it was so that legacy code would still behave in the same manner or something?
--
Ian
From my Fairphone FP3
 

Albrecht Schlosser

unread,
Jul 6, 2021, 6:05:11 PM7/6/21
to fltkc...@googlegroups.com
On Am 7/6/21 11:07 PM schrieb imm wrote:
On Tue, 6 Jul 2021, 21:55 Albrecht Schlosser wrote:

The static variable is the problem, independent of any interaction or incompatibility with the other form of Fl::awake(). Since there is only one static variable callling Fl::thread_message() later in user code can only retrieve the last value assigned to this variable. Hence Fl::awake(void* message) is IMHO always unreliable unless you know that messages are queued infrequently, i.e. less than can be handled by events. A user moving the mouse permanently would likely lead to an "event overflow" dropping messages, whereas Fl::awake(F, message) queues all messages so no message can ever be lost.

Well, strictly the queue holds the last 1024 messages.

Got me! ;-)


Once it is full, older messages will be lost to make room for newer ones... But if the user code is lagging 1000 events behind then there are bigger problems I'd hazard....

I agree.


I was never really clear why the "old" method wasn't adapted to use the queue, though.
Perhaps it was so that legacy code would still behave in the same manner or something?

Or because nobody thought of it or took a stab at it?

This was indeed a thought I had when I wrote my message above. Wouldn't it be useful to queue Fl::awake(message) pointers in a similar way (FIFO)? Fl::thread_message() would then retrieve the first queued message. If we wanted to keep compatibility we could limit the FIFO to exactly one entry as a user option. void Fl::awake_message_queue_length(int).

Bill Spitzak

unread,
Jul 6, 2021, 6:07:47 PM7/6/21
to fltkc...@googlegroups.com
You are probably right, I did not look at it. But I think it would make sense to have the Fl::awake(message) function just call the hander version, reusing any queue or other details.

--
You received this message because you are subscribed to the Google Groups "fltk.coredev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to fltkcoredev...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages