
On 7/23/05, Duncan Coutts
On Wed, 2005-07-20 at 16:10 +0100, Simon Marlow wrote:
On 20 July 2005 14:35, John Goerzen wrote:
I'm looking at packaging an event-driven console widget set (CDK) for Haskell using FFI. I know that other event-driven widget sets have Haskell bindings, but I'm not quite sure how to make everything play nice with forkIO.
These systems generally have some sort of an opaque main loop, implemented in C. This loop would usually never return, or perhaps only return once the UI is destroyed.
Is the library thread-safe or not? I mean OS-thread safe. If it is, then you're home and dry: just compile your program with GHC's -threaded option, and any foreign calls that need to run concurrently with Haskell threads must be declared "safe" (which is the default in fact, so instead I should really advise you to mark all calls that don't need to run concurrently as "unsafe", because that's good for performance).
If the library isn't thread-safe, then you're in the same boat as Gtk and wxHaskell (I believe) where there are known problems with having a multithreaded Haskell GUI app.
Yep.
You can only have one OS thread (and hence only one Haskell thread) doing GUI operations, and that is the thread in the main loop.
To be more precise, they can only be acessed from one OS thread. You can use multiple Haskell threads so long as they only make GUI calls from within the main OS thread.
The problem then as John noted is that the main loop of these toolkits block and so the other Haskell threads would not get a chance to schedule. So the challenge is to give the Haskell threads a chance to schedule.
Most toolkits with a main loop system allow you to setup timers. In the Gtk2Hs bindings we can use this trick:
-- 50ms timeout, so GHC will get a chance to scheule about 20 times a second -- which gives reasonable latency without the polling generating too much -- cpu load. timeoutAddFull (yield >> return True) priorityDefaultIdle 50
This causes the Gtk main loop to yeild to the ghc rts and give it a chance to run any runnable threads for a while. It is unfortunately a polling solution which has it's obvious disadvantages. However in practice it seems to generate negligable load when idle. The GHC rts yield function seems to be quite good about not doing much work when there is no work to be done!
Does anyone know how wxHaskell deals with this? I mean is it safe to call wxHaskell GUI stuff from several threads at once? Initial experimentation seems to suggest it is. I wrote a simple chat client / server, and to abstract away the networking I implemented a function "talk" which just takes a handle and then maps input and output to that handle using two Chans. So I basically have two threads, one which waits for stuff to be sent out (readChan, blocking when there's nothing in it) and the other which waits for stuff coming in (hWaitForInput h (-1), and then writeChan to the "in" Chan). In addition to that I, in the client GUI, spawn of a thread which just reads the "in" channel over and over and appends whatever's coming in to the chat area. So that's three threads right there, plus the main thread. So to experiment I implemented this in wxHaskell using a timer to unblock the main loop every now and then. To make sure the performance was visible I set it at 1000 ms. Naturally it took several seconds for all the different threads to get a chance to do anything so sending messages was very slow. Then I simply recompiled with "-threaded" and everything is super fast (the timer hack isn't even necessary). I haven't found any issues with wxHaskell misbehaving when it's called from different threads, but I'd like to know for sure that it's actually honest-to-goodness thread safe. So does anyone know? /S -- Sebastian Sylvan +46(0)736-818655 UIN: 44640862