
On Tue, 2006-11-14 at 10:40 +0000, Simon Peyton-Jones wrote:
I wonder whether it'd be possible to make the gtk2hs stuff emit warnings if you make calls from two different threads? Then an application would complain constructively rather than "becoming unstable".
I have three plans: Plan 1: prevent gtk2hs initialising when using the threaded RTS. This is what the dev version does at the moment to prevent people shooting themselves in the foot. Obviously this is not satisfactory as long term the threaded rts is going to be the only option available. The funny thing is that we can actually use Haskell threads with Gtk2Hs perfectly well with the single threaded rts (we currently use a polling scheme to to cooperative scheduling between gtk+ and ghc rts but there are some non-polling possibilities that could be implemented.) Indeed, we used to be able to have GUI stuff work fine in GHCi, but now that GHCi uses the threaded rts we're in trouble. Plan 2: Make the threaded rts do what we want. This is hard and involves begging Simon M to do lots of work. The constraints we have are that only the OS thread that initialised the GUI can make calls to the GUI lib. So any Haskell threads doing GUI stuff have to be running on that OS thread. Note that this really means one OS thread, not one capability which can run on a pool of threads (with X11, the pool might be ok as only one would be doing GUI stuff at once but on Win32 the restrictions are even tighter). I really don't want to make the Haskell coders have to deal with passing actions between threads to get it right, because people will get it wrong too easily. So any thread cunning needs to be hidden in the library so that people cn just use threads willy nilly without there being dangerous unchecked conditions. So one idea is to start the UI in a bound OS thread, but then we also want to make sure that any Haskell thread that makes a GUI call only do it in that same OS thread. Currently we cannot bind several Haskell threads to a single OS thread. From discussions with Simon M it is clear that making this possible is non-trivial. Then even once we have that, to make it transparent we'd need to be able to migrate a Haskell thread to the right OS thread if it makes a GUI call. The GUI wrapper lib would need to arrange this and arrange for cooperative scheduling on the one OS thread. Lots of work. The reason we can't just post actions to the right thread all the time is that GUI calls are typically very short lived. Many are just reading fields out of C struts. If every one of those needed an OS thread switch then it'd be pretty bad. So thats why we'd need to migrate the thread since that's a one off and after that GUI calls would be quick. Plan 3: perhaps the IO monad isn't the right monad It's really quite convenient to have complete access to the IO monad when doing GUI stuff and that seems to be the design that most recent libs have chosen. So suppose we had a GUI monad and the boundaries between the IO and GUI monad would take care of making sure we were using the right OS thread. So we would initialise the GUI and then work inside the GUI monad. It'd fork a bound thread to run the GUI lib's event loop. It'd also have to arrange for cooperative scheduling in that OS thread. Then the GUI monad could have a forkGUI that uses Haskell level scheduling to schedule several GUI 'threads' in the single bound Haskell thread. Then to do IO, we could provide an escape hatch to do IO, for blocking stuff we could forkIO and let it run in an unbound thread. Similarly for long running pure computations we could provide something (or just par?) to let those run outside the GUI thread so that it doesn't block the UI. So you'd be able to lift IO stuff into the GUI monad. As for the other way around, I guess that could arrange for the GUI action to be added to the GUI scheduler run queue. Tricky bits: callbacks get run in their own unbound Haskell thread which would therefore not run in the bound OS GUI thread. That'd be annoying as it'd involve 2 pointless context switches per callback event. Is there any way we could be a bit less strict. All we need is that the GUI Haskell thread runs in the GUI OS thread, we don't mind if other Haskell threads run in that OS thread too. So a could a callback that comes in on that OS thread spark a Haskell thread in that same OS thread? Then we could add the callback action to the Haskell GUI thread's runqueue and yield to that thread. So we might still need some RTS changes to make it work nicely. Sigh. I'd welcome other suggestions. If we can make it work nicely it should be great. Using light weight threads is a nice approach to GUIs compared to the contortions that people have to do in other languages to do everything in an event driven style to share a single thread while avoiding ever blocking. Duncan