Concurrency (was: Re: [GUI] Re: events & callbacks)
Wolfgang Thaller
wolfgang.thaller@gmx.net
Thu, 13 Mar 2003 18:17:47 +0100
Sorry in advance for the long post...
Daan Leijen wrote:
> Please think a little bit longer before voting a "big NO". If we want
> to make
> progress we shouldn't rule out a solid and simple solution based on
> shaky arguments.
My vote remains a big NO for now because I don't consider my arguments
shaky. Of course, I'll keep thinking about it, and as soon as I
discover the error of my ways, I'll change my vote. If I remain too
stubborn, a single "NO" vote isn't a majority for no, so don't worry...
;-)
> 2e) All haskell call backs are run in a single OS thread (the "GUI
> thread"). Calls to the CGA
> can be made at any time from any Haskell thread that run within the
> GUI thread.
My reservation remains that there is no way for a standard Concurrent
Haskell program to control which Haskell threads run in which OS
thread. The current proposal for "bound threads" (see ffi@haskell.org)
will allow some control, but for the sake of simplicity it doesn't
support explicitly binding multiple Haskell threads to one OS thread.
> 1) What if Haskell automatically decides to run Haskell threads in
> some other OS thread?
> First, I believe that an automatic system would be a mistake,
... which, if you think that it is a mistake, this mistake has already
been made. Threadsafe already exists.
> but secondly, such system
> will surely provide a forkIO variant that runs the Haskell thread in
> the same OS thread.
... which would defeat the purpose of "threadsafe", which was to
prevent the FFI from blocking other Haskell threads unpredictably.
> However, I don't really want to discuss OS threads/Haskell threads
> here, it is
> enough to know that this formulation does *not* rule out future GHC's
> or future
> OS multi threaded systems. )
Do we know this? GHC does not guarantee that multiple Haskell threads
are run in a certain OS thread. It most definitely rules out the
current version of GHC (with a special configure switch) and everything
that was planned for the next version.
As I said, there is no defined mapping between Haskell threads and OS
threads. So we can't say "... from the same OS thread", because a
Haskell programmer currently has no way to say "fork a new Haskell
thread in the same OS thread as the current Haskell thread".
That leaves us with two options that I can think of:
a) Restrict everything to one *Haskell* thread
b) Don't restrict anything - require implementations to do proper
synchronization or, if the backend requires it, even to channel
everything to the right thread.
b) is of course more work, and people have had doubts that it is
implementable. I'm convinced that it can be implemented, and that it's
not too difficult, but of course we can't go ahead with this until all
backend implementors are convinced that it's possible.
a) probably won't be popular with people who want to experiment with
concurrency for GUIs.
> You are right that different Haskell threads can interact with each
> other in devious
> ways by modifying global state... this is called the IO monad :-)
> There is no way you can solve this problem in general, and for the GUI
> it may involve a lot of overhead.
>
> *This is the responsibility of the user when using concurrency*. In
> your example,
> it is a classic multiple writers, single resource pattern.
I was thinking of situations where the state is not visible to the user
of the CGA.
Suppose there was a function like messageBox :: String -> IO () in the
CGA.
On Win32, this could be implemented by a call to the Win32 MessageBox
function.
On MacOS, there is no predefined function for that, so the
implementation would consist of the following steps:
1) Create a dialog window with the message
2) Show the window & bring it to the front
3) Run the modal event loop
4) Close the window
If two Haskell threads (possibly even running in the same OS thread)
attempt to call messageBox, then there'll be a race condition here in
the Mac version, but not on the Windows version. If we say that that is
the application's responsibility, then the application developer would
need to know the implementation of the Mac OS backend in order to write
a correct program. An average programmer would not forget about
locking, test it on Windows, and it would work. On Mac OS, we'd get
hard-to-reproduce bugs.
> Well, you introduced heavy weight synchronisation that has to be
> wrapped around
> *every* foreign call to the GUI.
No. I'm talking about normal Haskell synchronization primitives. As
there's still only one Haskell thread running at one time, they are as
lightweight as ever.
> Furthermore, you imply that a worker thread should
> be run in a different OS thread to keep everything reactive.
That will happen automatically in future versions of GHC. If you want a
foreign call and a Haskell thread to run in parallel, you need at least
two OS threads. Idle callbacks are more inefficient.
> However, OS threads are
> rather heavy weight and it may take longer to start one than the
> processing takes.
> In contrast, Haskell threads are extremely light-weight and fast.
The RTS pools OS threads. In GHC, I don't expect a complex concurrent
GUI program to spawn more than about three OS threads.
> I appreciate the work you are doing on supporting OS threads in GHC --
> it is interesting
> to see how it will work out. However, for the CGA it is enough to run
> in a single OS
> thread. The specification should just allow room for use of the CGA in
> the context
> of multiple OS threads -- but it doesn't have to support all this
> automatically as
> I think that the issues are more subtle than initially appreciated.
> Evolution, not revolution!
Of course. I don't insist on requiring CGA implementations to do proper
synchronizing, but I think that
a) it's not any more difficult than any other problem involving
concurrency
b) Prohibiting Concurrency and limiting everything to one Haskell
thread would be very restrictive
c) The middle ground that you proposed, that is to allow multiple
Haskell threads to call the CGA as long as they all run in one OS
thread, doesn't exist, as a Haskell thread is never guaranteed to run
in a certain OS thread.
d) Achieving the amount of synchronization & serialization that is
automatically provided by a single-threaded RTS is _really simple_, so
the number of OS threads should be a non-issue. The question is, do we
want to support concurrency or not?
Cheers,
Wolfgang