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 |