[Haskell-cafe] FFI and callbacks

Duncan Coutts duncan.coutts at worcester.oxford.ac.uk
Mon Aug 22 10:15:52 EDT 2005


On Mon, 2005-08-22 at 15:45 +0200, Sebastian Sylvan wrote:
> On 7/23/05, Duncan Coutts <duncan.coutts at worcester.oxford.ac.uk> 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



More information about the Haskell-Cafe mailing list