|
From: Duncan C. <dun...@wo...> - 2004-05-11 00:51:15
|
All, I gather that wxHaskell does not yet work nicely with multiple Haskell threads (eg the long computation in a separate thread in order to show a progress bar). I'm working on the same issue with gtk2hs and was hoping we could coordinate in designing a solution and in talking to the ghc developers if necessary. Current solutions are not very satisfactory, one can either add an idle handler that periodically yields to the ghc rts, or sprinkle your long running action with calls to yield to the gui main loop. The former is not good because it is basically polling which costs cpu time, even when your app should be idle. The latter is either difficult and annoying or impossible; the long running process may involve code from a different library that knows nothing of GUIs (which you would have to modify) or may even be pure code in which case you can't yield to the gui main loop at all. While using multiple threads may well not be a good technique for GUIs written in traditional languages (since the locking issues with OS threads are considerable), using lightweight Haskell threads should be a nice way of structuring GUI programs in Haskell. There are a couple research papers on this theme. Also, if we can make sure that all the Haskell threads accessing the GUI are running in the context of the same OS thread then there are no locking issues. So here's some of my ideas for what we would want: I would see as the goal, a solution that allows multiple Haskell threads to be bound to the single OS thread (the GUI thread), so that all calls that query or modify the GUI occur in the same single OS thread. If we had such a mechanism, then we still have to solve the problem that the GUI event loop blocks until it gets an event; while we would like to get control to return to the ghc rts occasionally so that it can service any active Haskell threads that want to make gui calls. We also want to be able to do this without polling. This may require some mechanism to get the appropriate information from the ghc rts so that the gui event loop can block, and return control to the ghc rts for one timeslice whenever there are events that ghc's rts is interested in (timers, io activity etc) I'm interested in your thoughts on this issue, (especially alternative proposals) and what priority you feel it deserves. Duncan Coutts |
|
From: Wolfgang T. <wol...@gm...> - 2004-05-11 08:41:50
|
On 11.05.2004, at 02:51, Duncan Coutts wrote:
> I would see as the goal, a solution that allows multiple Haskell
> threads
> to be bound to the single OS thread (the GUI thread), so that all calls
> that query or modify the GUI occur in the same single OS thread.
I would recommend not to go down that route...
*) It places a high burden on the Haskell implementation - GHC's is the
only RTS where this is even remotely implementable, whereas the current
"bound threads" scheme can be implemented in a variety of different
ways. I like to consider lightweight threads as an implementation
detail (used to achieve top performance in GHC) and not as a language
feature that has to be duplicated by all future implementations.
*) Having multiple threads trigger actions in the GUI thread can be
done without any special support by the RTS (see below).
*) Executing two Haskell threads in one OS thread leads to a lot of
problems when interacting
with foreign code. Nobody seems to have succeeded in even specifying
exactly what should happen in all the strange situations that arise
here. For example, let's assume we have two Haskell threads, A and B,
running in one OS thread.
Now A calls a foreign function "foo" that takes a long time to execute.
During this time, B doesn't execute at all, right?
OK, so now after "foo" is finished, A calls another foreign function,
"bar", which immediately calls back into Haskell. So as soon as the RTS
is re-entered, B should continue to run?
But what if B calls another foreign function, which in turn calls back
to Haskell? Now if the A-callback wants to return to bar, will that be
possible? No, it'll have to wait for that other foreign call in another
thread.
It's a can of worms, we _don't_ want to go there.
*) Executing two Haskell threads in one OS thread does not solve the
problem.
Sometimes we want to make library calls A, B and C in order, and not
have another thread call D in-between; for example because the library
has some global or thread-local state which we don't want to be messed
up, or perhaps because we want A, B and C to be done right after each
other (e.g. updating different parts of one window), and D might take a
long time (show a dialog box).
> I'm interested in your thoughts on this issue, (especially alternative
> proposals) and what priority you feel it deserves.
Well...
let's assume we've imported a few things from some "foreign" GUI
library (sorry, I don't know much about either gtk or wx, so I'll try
to stay general):
> runMainLoop :: IO () -- returns only when the program is over, calls
> lots of callbacks.
> -- send a user event to the main loop (wrap the IO action in a stable
> pointer)
> postUserEvent :: IO () -> IO ()
>
> {-# NOINLINE uiThread #-}
> uiThread = unsafePerformIO $ newIORef (undefined :: ThreadId)
>
> onClick :: ... -> IO () -- just an example
> onClick blah blah blah = do tid <- myThreadId
> writeIORef uiThread tid
> handleTheEvent
>
> -- handle a user event
> onUserEvent :: IO () -> IO ()
>
> onUserEvent action = do tid <- myThreadId
> writeIORef uiThread tid
> action
>
> doInUIThread action = do tid <- myThreadId
> uiTid <- readIORef uiThread
> if tid == uiTid
> then action
> else postUserEvent action
And then we can either wrap doInUIThread around every function we
import from the UI library (slow), or we can use it manually to do
things in bigger chunks.
I think we do _not_ want the handling of one event to be preempted by
UI actions from other threads; we might be in the progress of building
a dialog box in response to a user event, we don't want a background
thread to display e.g. an error message when we have a half-finished
dialog box...
Cheers,
Wolfgang
|