On 29.12.23 11:57, Will Godfrey wrote: Yes I spent the whole of
Christmas day at my sister's, along with my other sister, a few nephews and
nieces and two grandchildren - the youngest of which is about 15... I think!

How was your time?

Hi Will,

We had suddenly some quite spring-like weather here in Munich, after all
the snow was washed away by enduring rain. In other parts of Germany the
rain continues and people are fighting against the floods, while here
we've gotten some mediterranean air and the spring birds started singing.


On Thu, 28 Dec 2023 16:20:52 +0100 ichthyo <p...@ichthyostega.de> wrote:
However, this could be mitigated by using a combination of "push" and
"pull". How about that?
...
Yes I like this!
...so I'll look into building the required parts step by step,
using a feature branch, which can be rebased frequently on top of
current master....

====

So far this seems to be straight forward and rather robust. There is one
remaining issue: possible interference with other GUI processing.

More specifically, the question I'll have to find out about is: do we need
locking? Which means, is there /anything/ going on concurrently in Yoshimi's
GUI, or are all GUI operations confined to a single thread?

The latter would be the standard way to approach it today, since it turned out
to be the simplest way. Obviously, this requires to hand-off any substantial
work into background threads (or the core in our case, which does the bulk of
the work anyway).

Most UI toolkits I am aware of today use the model of "only one GUI
thread", because this turned out to be the most robust way to build a
message driven system. Thus typically there is somewhere a event-loop or a
 event-pulling in some GUI thread. Do you know how this is handled in
Yoshimi? Is this GUI event processing hidden in the FL::wait() call which
I see in mainThrad()
Just poking into FL.h, I see a FL::run(), but this seems not to be invoked anywhere...

On 29.12.23 11:57, Will Godfrey wrote:
I'm not sure but it seems to work in private space per instance. For example
the colour map is definitely per instance. If you start two completely
separate instances of Yoshimi and set the theme of one to the demo, it
doesn't have any effect on the other. Also things like the Alert window
don't block a second instance.
...
The instances we create *within* a main start do interact and can block each
other. In a sense I wish Andrew had never done that - it caused all sorts of
complexities, ....

No we don't use 'run', it's one of two ways to start
FLTK. We use 'lock()' and 'unlock()'.

That seems like an interesting hint. As such, lock() and unlock() do not seem
to /perform/ any GUI event processing, rather they are described as yet another
locking mechanism, just provided by FLTK this time and wrapped up conveniently.

Quoting from the FLTK manual:
https://www.fltk.org/doc-1.3/advanced.html
In a multithreaded program, drawing of widgets (in the main() thread) happens
asynchronously to widgets being updated by worker threads, so no drawing can
occur safely whilst a widget is being modified (and no widget should be
modified whilst drawing is in progress).
FLTK supports multithreaded applications using a locking mechanism
internally. This allows a worker thread to lock the rendering context,
preventing any drawing from taking place, whilst it changes the value of its
widget.

Using the FLTK library, the main() thread holds the lock whenever it is
processing events or redrawing the display. It acquires (locks) and releases
(unlocks) the FLTK lock automatically and no "user intervention" is
required. Indeed, a function that runs in the context of the main() thread
ideally should not acquire / release the FLTK lock explicitly.

However, this page mentions that FLTK event processing happens with "run"
or "wait" -- and the latter one is what we seem to do.

Quoting from:
https://www.fltk.org/doc-1.3/classFl.html#a108a84216f0b3fa1cb0c46ab7449a312
static int Fl::wait()
Waits until "something happens" and then returns.
Call this repeatedly to "run" your program. You can also check what happened
each time after this returns, which is quite useful for managing program
state.

What this really does is call all idle callbacks, all elapsed timeouts, call
Fl::flush() to get the screen to update, and then wait some time (zero if
there are idle callbacks, the shortest of all pending timeouts, or infinity),
for any events from the user or any Fl::add_fd() callbacks. It then handles
the events and calls the callbacks and then returns.

Fl::wait(time) waits a maximum of time seconds. It can return much sooner if
something happens. The return value is positive if an event or fd happens
before the time elapsed. It is zero if nothing happens

I'll have to look into that definitively to find out how our GUI event
processing actually happens. The fact that FL::lock() and FL::unlock()
is used indicates at the least that at some point in the past someone
considered it necessary since there might be concurrent updates to
data in the widgets. However, once all updates to the GUI are pushed
to the ringbuffer, there won't be any other threads interfering with
the GUI any more, and we won't need any locking at all in this part
of the application.

-- Hermann


_______________________________________________
Yoshimi-devel mailing list
Yoshimi-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/yoshimi-devel

Reply via email to