
On Mon, 2005-08-22 at 15:45 +0200, Sebastian Sylvan wrote:
On 7/23/05, Duncan Coutts
wrote: 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.
It may appear so at first and then mysteriously fail. I don't think wx is thread safe (since the underlying gui libs are not thread safe either). I don't believe that wx does it's own per-widget locking, but I may be wrong. Here's a bit from the wx manual: http://www.wxwidgets.org/manuals/2.4.2/wx494.htm "If you do decide to use threads in your application, it is strongly recommended that no more than one thread calls GUI functions. The thread sample shows that it is possible for many different threads to call GUI functions at once (all the threads created in the sample access GUI), but it is a very poor design choice for anything except an example. The design which uses one GUI thread and several worker threads which communicate with the main one using events is much more robust and will undoubtedly save you countless problems (example: under Win32 a thread can only access GDI objects such as pens, brushes, &c created by itself and not by the other threads)."
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?
So you can do this stuff when not using "-threaded" however when using "-threaded" you are using multiple OS threads and you should heed the advice from the wx manual above (ie don't do it). Actually as noted above, some wx backend (win32) have even stricter requirements for threads than others (Gtk+). Win32 does not even allow you to access drawing objects from threads which did not create them rather than simple not allowing concurrent access (as in Gtk+). Duncan