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 |