Hi,

  I got your message. But It will take a couple of days to figure this out.

Best,
Scuri


2018-04-16 16:18 GMT-03:00 Eric Wing <[email protected]>:

> This is a proposal to introduce a way to post and run events on the
> main UI thread.
>
> Currently (as far as I know), IUP is hands-off on threads.
>
> The problem I need to solve is that there are many libraries or
> situations where you are on another thread, but then need to change
> something in the GUI which is on the main thread. This can lead to
> disaster depending on the platform and implementation since most are
> not thread-safe. Examples of libraries that I often encounter that
> like to callback on background threads are networking and audio. (In
> fact, the native speech synthesizer I presented in my IUP Next talk at
> the Lua Workshop struggled with these issues since many of the speech
> synth implementation utilized background threads.)
>
> Since threads are a fact of life (even the web is getting web workers
> now), and since platform thread-safety GUI issues involve native
> implementation details that leak out everywhere, we need a
> cross-platform solution in IUP to tame these issues.
>
> So my proposal is very small and simple, “post message”  mechanism
> that I believe can be implemented for all the major platforms moving
> forward. To be clear, this is NOT a proposal to introduce threads to
> IUP, but merely be able to message/dispatch something on the main
> thread for those who find themselves on a background thread because of
> any variety of reasons (including sometimes out of their control).
>
>
>
>
> To set up some context about the backends:
>
> Mac, iOS, and Android *must* do UI stuff on the main UI thread.
> Attempts to call GUI APIs on other threads will lead to crashes,
> halting, etc. But because not-blocking the UI thread is so important
> for responsiveness, all these platforms are aggressive about finding
> non-UI things to compute on background threads. Thus all of these
> platforms provide ways to dispatch code back on the UI thread from the
> other threads. And because these mechanisms are so easy to invoke,
> many libraries that are provided callback on background threads and
> assume the user will take responsibility for dispatching back to the
> main UI thread if they need to. But since IUP abstracts away the
> native GUI parts, but doesn’t currently abstract the main UI dispatch
> mechanisms, we are left with a roadblock.
>
> Additionally, Windows and GTK are not guaranteed to be thread safe.
> While these platforms aren’t as explicit about advertising the
> threading rules as Apple and Android, the restrictions are effectively
> the same. Hence why Windows has a PostMessage() API and GTK offers
> g_idle_add, both of which are advertised as techniques for
> communicating back with the main UI thread.
>
> And it appears the web worker design also provides a postMessage()
> mechanism in this same spirt.
>
>
>
> So now. let me give a concrete example of the threading problem which
> I propose to solve.
>
>
> I have written a normal IUP program that works across all the
> different platforms I mentioned. But my program also uses an audio
> library, ALmixer, to play back sounds. ALmixer however has an
> audio-callback API for certain types events, which calls back on a
> background thread. For simplicity, let’s just focus on ALmixer’s
> “sound finished playing” callback notification.
>
> In my IUP program,
> - I have a button you press to start playing a sound.
> - When you press the button, I make the button inactive (ACTIVE=0) and
> play the sound.
> - When the audio finishes playing, I want to make the button active,
> so the user can play the sound again by pressing the button again.
>
> The problem is that the callback is on a background thread, so it is
> unsafe to call any IUP GUI APIs.
>
> So I need a mechanism where I can send a message to the main UI thread
> and start running a callback there, so I can make the button active
> again.
>
> My proposal is to introduce a new callback, in spirit of the Windows
> PostMessage(), maybe also web worker’s postMessage(). On GTK we can
> implement this with g_idle_add. On Apple, we can implement this easily
> with Apple’s libdispatch: dispatch_async/dispatch_get_main_queue.
> Android can be done with Handler.post() using Looper.getMainLooper()
> (assuming you designed the backend correctly, which I’ve done).
>
>
> The overall proposal idea for IUP is to introduce a new callback event,
> e.g.
> IupSetCallback(ih, “UITHREAD”, uicallback);
>
> and a API to trigger it from a background thread, e.g.
>
> IupPostMessage(Ihandle* ih, void* message_data);
>
> (I’ve suggest more proposal variations of this further below.)
>
>
>
> I’ve actually already implemented a working proof-of-concept for all
> the platforms I mentioned, except Windows. (I’ll explain why
> not-Windows in a moment.)
>
> My proof of concept example using ALmixer with IUP can be found here.
> https://bitbucket.org/ewing/iupalmixerthread/src
> (The bulk of the code is in the source directory, but the Android
> .java file resides in the CMakeModules subdirectory.)
>
>
> The proof-of-concept I wrote actually was implemented directly in my
> app, and outside (not-modifying) of IUP (mostly).  For Mac, iOS, and
> GTK, this is pretty trivial to do. Android on the other-hand was a
> nightmare (as usual with Android), but because I’ve already been
> keenly aware of this problem, I’ve already taken great care in
> designing IUPAndroid’s backend so this problem could be solved, so all
> the internal mechanisms I need are already inside my implementation.
>
>
> That leaves Windows. The reason I couldn’t implement this is because
> IUP abstracts the event loop for all the platforms in the API. For the
> Windows implementation, I will need to make a few additions to IUP’s
> Windows implementation in how it deals with the loop.
>
> But I believe I found a working solution. The solution actually from
> Raymond Chen’s blog:
> "Rescuing thread messages from modal loops via message filters”
> https://blogs.msdn.microsoft.com/oldnewthing/20050428-00/?p=35753
>
> The solution uses PostThreadMessage.
>
> Generally, this is not advised because it can lose messages during
> modal loops. PostMessage is usually the preferred solution. However,
> the problem with PostMessage is you must post a message to a explicit
> window handle. It is not always the case you know which window you
> want to deal with, if any. For example, in my audio example, maybe
> instead of updating the GUI, I just want to unload the audio file now
> that it is done playing. Additionally, none of the other platforms tie
> their thread messaging with specific GUI widgets, so this would create
> an impedance mismatch.
>
> But fortunately, Raymond Chen’s article demonstrates how to not lose
> messages when using PostThreadMessage. So even though it is not the
> usual recommended solution, in our case, I think it is the correct
> design. He warns that the downside to this implementation is that it
> requires “all modal loops need to remember to call CallMsgFilter as
> part of their dispatch loop”. But since IUP wraps/abstracts all the
> event loop stuff, this should be quite simple to do for us.
>
>
> Since this was an “end-run” prototype that didn’t modify IUP (except
> for Android), this is not using the API I am proposing for IUP. But it
> similar and should capture the heart of what needs to be done and how
> it works.
>
>
> So I’d like to work with you to bring this into IUP. But I wanted to
> ask before doing, and maybe find out if you have better ideas on both
> the API and the Windows implementation approach.
>
> I think needs to be in IUP because:
>
> - IUP abstracts/wrap the event loop, so IUP is the appropriate place
> to solve it.
>         - (And as I mentioned, the Windows implementation seems to require
> that it be in IUP.)
>
> - Additionally, IUP also somewhat hides the backend implementation
> (e.g GTK2, GTK3, Motif, etc.) So trying to do this outside IUP (aka
> “end-run”) is fragile because it depends on me implementing and using
> the exact same backend. So in my linked ALmixer example, I wrote a GTK
> backend and forced me to write GTK code and link to it, on top of also
> using IUP. And if IUP used a different backend (e.g. Motif) my end-run
> would break. As an IUP user, I shouldn’t have to write native code for
> this type of thing. And of course, my example cross-platform code is
> now littered with Mac, iOS, and Android implementations on top of all
> this.
>
> - The thread safety rules and issues are real in all the backends, and
> each native platform offers APIs to deal with thread messaging. IUP
> wrapping native GUIs should wrap this small piece too.
>
> - New libraries continue to push hard into callbacks on background
> threads. For example, but Apple and Android’s in-app purchase (which
> talk over the network) modules may callback on background threads.
>
>
>
>
>
> So below are 3 variations of my API proposal for consideration:
>
>
> /////////////////////
> Idea 1: Global callback
>
> // Should the term be UITHREAD or THREAD_DISPATCH or MAIN_DISPATCH?
>
> IupSetGlobal(“UITHREAD”, uicallback);
>
> void uicallback(int type, void* message_data)
> {
>
> }
>
> // Should the term be Message or Thread or Dispatch?
> IupPostMessage(int user_type, void* message_data);
>
>
> ///////////////////// Example:
> void Init()
> {
>         IupSetGlobal(“UITHREAD”, main_thread_callback);
>
>         AudioSetFinishedCallback(audio_background_thread_
> finished_playing);
>         struct AudioData* audio_data = LoadAudioData();
>         void* audio_user_data = NULL;
>         AudioPlaySound(audio_data, audio_user_data);
> }
>
> // This happens on a background thread
> void audio_background_thread_finished_playing(struct Audio*
> audio_data, void* audio_user_data)
> {
>         // I give audio types an arbitrary type of 42
>         IupPostMessage(42, audio_data); // does not block
> }
>
>
> // calls back on the main thread
> void main_thread_callback(int user_type, void* audio_data)
> {
>         switch(user_type)
>         {
>                 case 42: // got audio type
>                         UpdateUI();
>                         break;
>                 default:
>                         // unhandled
>         }
> }
> /////////////////////
>
>
> Notes:
> Since there is only one global callback, there may be multiple systems
> that want to use it. So to allow this, the int user_type can be used
> to distinguish different use cases.
>
> Requires Icallback with signature: viv
>
>
> Downsides:
>         - int user_types will need to be coordinated by end users so they
> don’t trample over each other.
>
>         - There is only one global callback, so library developers who need
> this but want to hide implementation details about their threads,
> cannot do so because the user may steal the global callback out from
> under them.
>
> Upsides:
>         - Does not require any explicit widget (Ihandle*) to work.
>         - Reuses existing IupSetGlobal() API.
>
> Additional thoughts:
>         - Maybe the user should be able to set one more void* user_data
> parameter they can get back in the callback? (That way, an Ihandle*
> can be passed through which can be used to UpdateUI() on the other
> side.
>         IupSetAttribute(NULL, “UITHREAD”, some_user_data_pointer);
>         // Then get it back when inside the uicallback?
>         void* some_user_data_pointer = IupGetAttrbitue(NULL, “UITHREAD”);
>
>
>
> /////////////////////
> /////////////////////
> Idea 2: Per Ihandle*
>
>
>
> IupSetCallback(ih, “UITHREAD”, uicallback);
>
> void uicallback(Ihandle* ih, int type, void* message_data)
> {
>
> }
>
> // Should the term be Message or Thread or Dispatch?
> IupPostMessage(Ihandle* ih, int user_type, void* message_data);
>
>
> ///////////////////// Example:
> void Init()
> {
>         Ihandle* ih = IupButton(NULL, NULL);
>         // (omitted) set up button stuff
>         IupSetCallback(ih, “UITHREAD”, main_thread_callback);
>
>         AudioSetFinishedCallback(audio_background_thread_
> finished_playing);
>         struct AudioData* audio_data = LoadAudioData();
>         void* audio_user_data = ih;
>         AudioPlaySound(audio_data, audio_user_data);
> }
>
> // This happens on a background thread
> void audio_background_thread_finished_playing(struct Audio*
> audio_data, void* audio_user_data)
> {
>         Ihandle* ih = (Ihandle*) audio_user_data;
>         // I give audio types an arbitrary type of 42
>         IupPostMessage(ih, 42, audio_data); // does not block
> }
>
>
> // calls back on the main thread
> void main_thread_callback(Ihandle* ih, int user_type, void* audio_data)
> {
>         switch(user_type)
>         {
>                 case 42: // got audio type
>                         UpdateUI(ih);
>                         break;
>                 default:
>                         // unhandled
>         }
> }
> /////////////////////
>
>
>
>
> Notes:
> Since we do this on a per-Ihandle basis, we might be able to do-away
> with the int user_type. I still think it could be handy though.
>
> Requires Icallback with signature: viv
>
>
> Downsides:
>         - Now requires an explicit, designated Ihandle* to work. This could
> create impedance for GUIs that are very fluid and tear down widgets a
> lot. In those, dialogs and other widgets go away a lot, so if you
> expect a callback to fire in some distant future, you have to keep
> those widgets alive. This may lead to the creation of dummy widgets
> (and may tempt people to create unmapped widgets…I’m not sure if this
> is a bad thing or not.)
>
>         - Also notice that the widget itself is not necessarily that
> important to the thread dispatch stuff. It is just a proxy object.
> Instead of a button, I could have used a label or dialog. And the
> widget itself does not need to be involved in anything else. Perhaps I
> just wanted to delete the AudioData in the callback instead of doing
> GUI stuff, yet I still brought along the Ihandle*. Hence, the
> association between Ihandle* and main thread dispatch is kind of weak
> and mostly just serves bookkeeping.
>
>
>
> Upsides:
>         - We now get one callback per Ihandle*, so library writers can use
> this to hide implementation details without being trampled by end
> users.
>
>         - Reuses existing IupSetCallback() API.
>
> Additional thoughts:
>         - Maybe the user should be able to set one more void* user_data
> parameter they can get back in the callback?
>         IupSetAttribute(NULL, “UITHREAD”, some_user_data_pointer);
>         // Then get it back when inside the uicallback?
>         void* some_user_data_pointer = IupGetAttrbitue(NULL, “UITHREAD”);
>
>
> /////////////////////
> /////////////////////
>
> Idea 3: New API to split the difference between Idea 1 and Idea 2?
> Allow separate callbacks for each different type of user type.
>
>
>
> IupSetUiThreadCallback(const char* user_type, “UITHREAD”, uicallback);
>
> void uicallback(const char* user_type, void* message_data)
> {
>
> }
>
> // Message or Thread or Dispatch?
> IupPostMessage(const char* user_type, void* message_data);
>
>
> ///////////////////// Example:
> void Init()
> {
>         Ihandle* ih = IupButton(NULL, NULL);
>         // (omitted) set up button stuff
>         IupSetUiThreadCallback(“audio”, “UITHREAD”,
> main_thread_audio_callback);
>
>         AudioSetFinishedCallback(audio_background_thread_
> finished_playing);
>         struct AudioData* audio_data = LoadAudioData();
>         void* audio_user_data = ih;
>         AudioPlaySound(audio_data, audio_user_data);
>
>         void* network_user_data = ih;
>         NetworkSetFinishedCallback(network_thread_finished_download);
>         NetworkFetchData(ih);
>         IupSetUiThreadCallback(“network”, “UITHREAD”,
> main_thread_network_callback);
>
>
> }
>
> // This happens on a background thread
> void audio_background_thread_finished_playing(struct Audio*
> audio_data, void* audio_user_data)
> {
>         Ihandle* ih = (Ihandle*) audio_user_data;
>         IupPostMessage(“audio”, audio_data, audio_user_data); // does not
> block
> }
> // This happens on a background thread
> void network_thread_finished_download(struct NetworkData*
> download_data, void* network_user_data)
> {
>         Ihandle* ih = (Ihandle*) network_user_data;
>         IupPostMessage(“network”, download_data, network_user_data); //
> does not block
> }
>
> // calls back on the main thread for just “audio” events
> void main_thread_callback(const char* user_type, void* audio_data,
> void* user_data)
> {
>         // assert: user_type == “audio”
>         Ihandle* ih = (Ihandle*)user_data;
>         UpdateUI(ih);
> }
>
> // calls back on the main thread for just “network” events
> void main_thread_callback(const char* user_type, void* network_data,
> void* user_data)
> {
>         // assert: user_type == “network”
>         Ihandle* ih = (Ihandle*)user_data;
>         UpdateUI(ih);
> }
>
> /////////////////////
>
> Notes: This idea decouples the thread dispatch callbacks from actual
> Ihandle* widgets (like Idea 1), but we still can get multiple
> callbacks so people don’t trample each other fighting over a single
> callback (like Idea 2).
>
> Additionally, this idea filters callbacks so that each user_type can
> potentially get a separate function pointer, if the user desires. The
> idea is somewhat similar to IupButton callbacks and how you can either
> have separate callbacks for each Ihandle*, or share the same one and
> distinguish on const char* action).
>
> My thinking is the const char* user_type can be very similar to the
> IupSetAttribute and IupSetStrAttribute in that maybe the user can use
> it as either a string or arbitrary pointer.
>
>
> Downsides:
>         - One more new API function
>         - Probably requires more implementation code for us to save and
> invoke the different callbacks.
>
> Upsides:
>         - Thread dispatch and Ihandle* widgets are not artificially
> coupled.
>         - Callbacks can be separated based on type.
>
>
>
> Anyway, since my proof-of-concept works (sans Windows), I expect this
> to be a relatively easy thing to implement. (Fingers crossed that
> Chen’s solution just works.) The main thing I believe is to decide on
> the API design so we can implement it.
>
>
>
> Thanks,
> Eric
>
> ------------------------------------------------------------
> ------------------
> Check out the vibrant tech community on one of the world's most
> engaging tech sites, Slashdot.org! http://sdm.link/slashdot
> _______________________________________________
> Iup-users mailing list
> [email protected]
> https://lists.sourceforge.net/lists/listinfo/iup-users
>
------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
Iup-users mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/iup-users

Reply via email to