Discussion:
[Gtk2hs-users] Separate signal emitter and handler in distinct threads
koral
2014-11-02 18:33:11 UTC
Permalink
Hello,

I'm attaching to this mail a commented piece of code that exposes a design issue I'm having.

Basically, I'm trying to separate the code that emits a GUI signal, from the code that handles it.
The issue is that some gtk2hs signals expect a result in return (e.g. Graphics.UI.Gtk.WebKit.WebView.mimeTypePolicyDecisionRequested).
I implemented this through MVar-s, but this creates a deadlock as detailed in the example code.

I'm now wondering:
* was separating emitter and handler in distinct threads a good idea in the first place ? It looks to me that such design would provide a great isolation (e.g. running emitter and handler in separate monads)
* am I right to conclude that it is impossible to implement such design, as suggested by the attached example ?

Thank you in advance for your insights.

--
koral
Brandon Allbery
2014-11-02 18:53:10 UTC
Permalink
Post by koral
* was separating emitter and handler in distinct threads a good idea in
the first place ? It looks to me that such design would provide a great
isolation (e.g. running emitter and handler in separate monads)
* am I right to conclude that it is impossible to implement such design,
as suggested by the attached example ?
It would be nice, but the design of the underlying gtk library pretty much
prohibits it; it plays very badly with threads in any language.
--
brandon s allbery kf8nh sine nomine associates
***@gmail.com ***@sinenomine.net
unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net
Axel Simon
2014-11-03 08:04:31 UTC
Permalink
Hi Koral,
Post by koral
Hello,
I'm attaching to this mail a commented piece of code that exposes a design issue I'm having.
Basically, I'm trying to separate the code that emits a GUI signal, from the code that handles it.
The issue is that some gtk2hs signals expect a result in return (e.g. Graphics.UI.Gtk.WebKit.WebView.mimeTypePolicyDecisionRequested).
I implemented this through MVar-s, but this creates a deadlock as detailed in the example code.
* was separating emitter and handler in distinct threads a good idea in the first place ? It looks to me that such design would provide a great isolation (e.g. running emitter and handler in separate monads)
* am I right to conclude that it is impossible to implement such design, as suggested by the attached example ?
Thank you in advance for your insights.
Brandon said that the underlying Gtk library “pretty much prohibits this” which is true but it is a bit unfair since there are hardly any GUI libraries out there that are truly concurrent. I believe that the reason for this is the high conceptual complexity that full concurrency would entail and that this would lead to little gain:

So it seems that you want to lookup the type of a file in parallel. If Gtk+ would allow you to arbitrarily delay a signal handler (so that you wouldn’t even have to spawn a new thread during the signal handler) then the GUI would be unresponsive until all these threads have returned a result. If determining the mime type means opening the file and this file sits on a network that is down then you need a timeout and your GUI would be unresponsive until this timeout has run out. How would one then make the GUI more responsive? By using an inaccurate mime type to start off with and then slowly fill in the actual mime types as the various files have been identified. This idea, however, can readily be implemented by the even-queue nonpreemptive multitasking that Gtk (and most other) toolkits implement: upon the initial signal, spawn a thread to determine the mime type and return immediately with the unknown type. Upon the thread’s completion, it uses postGUI to update the specific mime type of a file.

In a nutshell: a good (responsive) GUI is built by always returning immediately form an event; a true parallel event implementation would not make it any easier to fulfilling this goal.

Cheers,
Axel
Post by koral
--
koral
<example.hs>------------------------------------------------------------------------------
_______________________________________________
Gtk2hs-users mailing list
https://lists.sourceforge.net/lists/listinfo/gtk2hs-users
koral
2014-11-03 12:40:07 UTC
Permalink
So it seems that you want to lookup the type of a file in parallel. If Gtk+ would allow you to arbitrarily delay a signal handler (so that you wouldn’t even have to spawn a new thread during the signal handler) then the GUI would be unresponsive until all these threads have returned a result. If determining the mime type means opening the file and this file sits on a network that is down then you need a timeout and your GUI would be unresponsive until this timeout has run out.
Regarding the example of mimeTypePolicyDecisionRequested, the final decision is essentially whether to render or download a file. Why should the main GUI loop wait for this decision ? In other words, using my code example, why should thread A wait for thread B to end ? It kind of defeats the purpose of having thread B != thread A. It seems to me that this constraint imposed by gtk is unnecessarily too strong.
In a nutshell: a good (responsive) GUI is built by always returning immediately form an event; a true parallel event implementation would not make it any easier to fulfilling this goal.
My concern is more about code separation/isolation rather than concurrency, notably I'd like to run the UI thread and the signal handlers in distinct monad stacks, and concurrency seemed a good way to achieve that. If you have an idea on how to do it differently, I'm all ears.

--
koral

------------------------------------------------------------------------------
Axel Simon
2014-11-03 14:24:30 UTC
Permalink
Hi,
Post by koral
Post by Axel Simon
So it seems that you want to lookup the type of a file in parallel. If Gtk+ would allow you to arbitrarily delay a signal handler (so that you wouldn’t even have to spawn a new thread during the signal handler) then the GUI would be unresponsive until all these threads have returned a result. If determining the mime type means opening the file and this file sits on a network that is down then you need a timeout and your GUI would be unresponsive until this timeout has run out.
Regarding the example of mimeTypePolicyDecisionRequested, the final decision is essentially whether to render or download a file. Why should the main GUI loop wait for this decision ? In other words, using my code example, why should thread A wait for thread B to end ? It kind of defeats the purpose of having thread B != thread A. It seems to me that this constraint imposed by gtk is unnecessarily too strong.
I’d be the last to defend the Gtk+ API design, but: what is the GUI supposed to do until your application logic decides whether the document should be opened or rendered? It can only wait. So there is little reason to raise the event and have a different API call deliver the answer (which would be the for, e.g. creating a preview of a file based on it’s content of mime-type, since here a generic icon could be displayed). So for querying this kind of information a two-thread solution doesn’t seem to be a win.
Post by koral
Post by Axel Simon
In a nutshell: a good (responsive) GUI is built by always returning immediately form an event; a true parallel event implementation would not make it any easier to fulfilling this goal.
My concern is more about code separation/isolation rather than concurrency, notably I'd like to run the UI thread and the signal handlers in distinct monad stacks, and concurrency seemed a good way to achieve that. If you have an idea on how to do it differently, I'm all ears.
Hm, I always thought having the same Monad for the main loop and the event handlers is the difficult but desirable goal which can only be achieved by wrapping each code that serves an even handler with a lifting to the desired application monad. Aren't multithreading and lifting into a different monad two orthogonal things?

Cheers,
Axel
koral
2014-11-03 21:42:16 UTC
Permalink
Post by Axel Simon
what is the GUI supposed to do until your application logic decides whether the document should be opened or rendered? It can only wait.
It doesn't make sense to me: thread A could very well keep processing pending GUI events, while thread B is busy taking a decision.
The mimeTypePolicyDecisionRequested signal is notably emitted when a link is clicked. I see no reason why GTK should block the GUI between the moment the link is clicked, and the moment the decision to render/download it is made.
If the decision takes some time, it makes perfect sense to be able to scroll the currently displayed webpage in the meantime.
Post by Axel Simon
Hm, I always thought having the same Monad for the main loop and the event handlers is the difficult but desirable goal which can only be achieved by wrapping each code that serves an even handler with a lifting to the desired application monad. Aren't multithreading and lifting into a different monad two orthogonal things?
Sure, I simply meant that separating signal emission/handling in distinct threads naturally leads to separate monad stacks, which is what I'm striving for.

--
koral

------------------------------------------------------------------------------
Axel Simon
2014-11-04 06:45:18 UTC
Permalink
Hi Koral,
Post by koral
Post by Axel Simon
what is the GUI supposed to do until your application logic decides whether the document should be opened or rendered? It can only wait.
It doesn't make sense to me: thread A could very well keep processing pending GUI events, while thread B is busy taking a decision.
The mimeTypePolicyDecisionRequested signal is notably emitted when a link is clicked. I see no reason why GTK should block the GUI between the moment the link is clicked, and the moment the decision to render/download it is made.
If the decision takes some time, it makes perfect sense to be able to scroll the currently displayed webpage in the meantime.
Ok, fair enough. I guess the Gtk+ developers thought that this decision can and should be taken instantaneously. Implementing this asynchronously is harder, possibly acceptably harder in Haskell, probably too hard in C.
Post by koral
Post by Axel Simon
Hm, I always thought having the same Monad for the main loop and the event handlers is the difficult but desirable goal which can only be achieved by wrapping each code that serves an even handler with a lifting to the desired application monad. Aren't multithreading and lifting into a different monad two orthogonal things?
Sure, I simply meant that separating signal emission/handling in distinct threads naturally leads to separate monad stacks, which is what I'm striving for.
I can see that using a different thread forces you to pick a new monad stack.

Cheers,
Axel

Loading...