Re: events & callbacks

Daan wrote (about whether callbacks are concurrent)
Just for the record: this is *not* true for most (all?) event based implementations as they are *not* concurrent (at least not pre-emptively concurrent). Somewhere down, there is an event loop -- a loop that waits for an event, executes a callback, and loops again. Even though more events can happen while executing the callback, it will only propegate into Haskell once the callback returns and the event loop pops it off the OS event queue. [note: You can achieve more concurrency in Haskell by using Haskell threads but even than one has to wait till the callbacks are done or otherwise the runtime system sits idle while waiting for the next event (as this is a C call).] Well, if the callback model requires you to work in a gaol in which concurrency is banned, it is useless. I rest my case, m'lud.
But really, are you serious? Imagine I click button A, which is supposed to start another dialog in which I click button B, and so on. Since it is apparently illegal in GIOs world for button A's callback to include a wait on button B's callback, the only way to do this kind of thing is turn my whole program inside out, rewrite it in continuation-passing-style, and then make button A put the whole continuation on button B's callback. I remember having to do this kind of thing once, back in the days when GHC only allowed continuations to be given to interrupt handlers, and believe me I don't want to do it again. As far as I'm concerned you can forget about any GUI which doesn't allow you to write a function confirm :: String -> IO Bool which puts up a window containing the String and "OK" and "Cancel" buttons, instead making the user put up with the CPS style. confirm :: String -> (Bool -> IO ()) -> IO ()

On Tue, 11 Mar 2003 13:52:46 +0100, George Russell
Daan wrote (about whether callbacks are concurrent)
Just for the record: this is *not* true for most (all?) event based implementations as they are *not* concurrent (at least not pre-emptively concurrent). Somewhere down, there is an event loop -- a loop that waits for an event, executes a callback, and loops again. Even though more events can happen while executing the callback, it will only propegate into Haskell once the callback returns and the event loop pops it off the OS event queue. [note: You can achieve more concurrency in Haskell by using Haskell threads but even than one has to wait till the callbacks are done or otherwise the runtime system sits idle while waiting for the next event (as this is a C call).]
Well, if the callback model requires you to work in a gaol in which concurrency is banned, it is useless. I rest my case, m'lud.
It has nothing to do with the Haskell "callback" model. It is how most GUI libraries just work on the OS level: win32, X, GTK, etc. This doesn't mean you can't do all kinds of tricks to implement a more appropiate model in Haskell -- I just wanted to make clear that the argument/example/supposed bug was false.
As far as I'm concerned you can forget about any GUI which doesn't allow you to write a function confirm :: String -> IO Bool which puts up a window containing the String and "OK" and "Cancel" buttons, instead making the user put up with the CPS style. confirm :: String -> (Bool -> IO ()) -> IO ()
Again, it has nothing to do with Haskell but depends on the OS. Fortunately all platforms indeed allow you to run a window "modally". (In this case, you call C world again from a callback, but that C call will go into its own event loop and call Haskell callbacks from there again.). In short, the eventloop model is what each platform provides and it is up to us to implement something nice in Haskell. Since each platform has "modal" windows, there is no need for CPS style programming. Even better, using concurrency, I have even found an implementation of "sync" on top of the eventloop model. All the best, Daan.
instead making the user put up with the CPS style. confirm :: String -> (Bool -> IO ()) -> IO ()
_______________________________________________ GUI mailing list GUI@haskell.org http://www.haskell.org/mailman/listinfo/gui

Daan Leijen wrote: (snip)
Well, if the callback model requires you to work in a gaol in which concurrency is banned, it is useless. I rest my case, m'lud.
It has nothing to do with the Haskell "callback" model. It is how most GUI libraries just work on the OS level: win32, X, GTK, etc.
This doesn't mean you can't do all kinds of tricks to implement a more appropiate model in Haskell -- I just wanted to make clear that the argument/example/supposed bug was false.
I just don't understand this at all. Either IO actions provoked by callbacks run concurrently with other Haskell actions or they do not. If they run concurrently, the "argument/example/supposed bug" is going to occur. Furthermore, I think there is actually very little alternative to allowing other Haskell actions to run concurrently, if only because of the infamous blackholing problem (see FFI archives, passim; the basic problem is what you do when the callback needs some value which some other bit of Haskell is half-way through evaluating). You can't bring the whole of the rest of the Haskell world to a stop and run just the callback thread. (snip)
Again, it has nothing to do with Haskell but depends on the OS. Fortunately all platforms indeed allow you to run a window "modally". (In this case, you call C world again from a callback, but that C call will go into its own event loop and call Haskell callbacks from there again.).
In short, the eventloop model is what each platform provides and it is up to us to implement something nice in Haskell. Since each platform has "modal" windows, there is no need for CPS style programming. Even better, using concurrency, I have even found an implementation of "sync" on top of the eventloop model.
If the GUI is to be built in terms of modal event loops, then we should at least know how these work, so we can see how they will fit in with concurrency, laziness and so on. It does seem to me that my plain old events are likely to be a lot simpler ... A simple question: what happens when two separate Haskell threads both try and open dialogues?

On Tue, 11 Mar 2003 16:07:59 +0100, George Russell
Daan Leijen wrote:
It has nothing to do with the Haskell "callback" model. It is how most GUI libraries just work on the OS level: win32, X, GTK, etc.
I just don't understand this at all. Either IO actions provoked by callbacks run concurrently with other Haskell actions or they do not. If they run concurrently, the "argument/example/supposed bug" is going to occur.
On the primitive level, nothing runs concurrent. This means that you can register other handlers etc. since no other event will be handled while you are doing haskell work. On the primitive level there is an eventloop that gets an event from the (OS maintained) event queue, calls haskell with the event as argument, and when haskell returns it waits again. One can implement all other models on top of this: - you just keep this model and use callbacks. This is what GIO, HsGTK and TkGofer do. - you make the callbacks behave concurrently (this would lead to the bug in the example though). How could you do that? well, when in Haskell, you fork a thread to handle an event and return directly. Would it work? No! because the eventloop will immediately wait for the next event. As this is a C call, the haskell runtime system will not run any haskell threads. Are there solutions? yes! You can for example run haskell threads in idle time or wait until all haskell threads are in a waiting state, before returning to the event loop. Another solution is to run the event loop and Haskell as OS co-threads. Different solutions are probably appropiate in different situations. Bottom line though is that all the concurrent stuff can (and must!) be implemented on top of simple non-concurrent callbacks. In the end, whether events are concurrent or not, becomes a property of a specific library. A library should therefore be clear about the semantics of events and whether they can be concurrent of not. (Examples of concurrent libraries are Haggis and Fran.) -- Daan.

Daan Leijen wrote:
On the primitive level, nothing runs concurrent. This means that you can register other handlers etc. since no other event will be handled while you are doing haskell work. On the primitive level there is an eventloop that gets an event from the (OS maintained) event queue, calls haskell with the event as argument, and when haskell returns it waits again. [...] well, when in Haskell, you fork a thread to handle an event and return directly. Would it work? No! because the eventloop will immediately wait for the next event. As this is a C call, the haskell runtime system will not run any haskell threads.
That is not guaranteed, it is not part of the FFI specification. Future Haskell implementations will support concurrently executing Haskell and foreign code, and people (for example myself) will want to take advantage of this. The next major version of GHC will probably continue to execute Haskell threads while foreign calls are in progress ("threadsafe" foreign calls). Also, I have to note that some platform-specific (C-language) GUI APIs require that some operations are executed from a certain OS thread (Apple's libraries, but perhaps also some parts of Win32, I haven't investigated exactly). There's a discussion on ffi@haskell.org about how to extend the FFI to handle this. Therefore I think that we should either a) specify that the CGA has to provide all the necessary synchronization to ensure proper operation in the presence of concurrency (this might be done using mvars). b) specify that the CGA may only be used when proper serialization is ensured by the user. I'm strongly in favour of a), as b) will cause a lot of trouble for the library user in the presence of "threadsafe" foreign calls, and even more trouble when the backend library wants to be called only from a certain OS thread (mostly the "main" thread). I do agree that we should not require concurrency for CGA, i.e. that all callbacks should run synchronously and map as directly as possible to the callbacks supplied by the underlying back end libraries. When we have "threadsafe" foreign calls and the CGA is properly protected against concurrent invocation, then concurrent callbacks, events, whatever can easily be implemented on top of that. Cheers, Wolfgang

On Wed, 12 Mar 2003 00:14:54 +0100, Wolfgang Thaller
Daan Leijen wrote:
the eventloop will immediately wait for the next event. As this is a C call, the haskell runtime system will not run any haskell threads.
That is not guaranteed, it is not part of the FFI specification.
I know. Just wanted to explain why the "byedemo" examples are not buggy, which eventually lead to this lengthy explanation of how most implementations work.
Therefore I think that we should either a) specify that the CGA has to provide all the necessary synchronization to ensure proper operation in the presence of concurrency (this might be done using mvars). b) specify that the CGA may only be used when proper serialization is ensured by the user.
I'm strongly in favour of a),
I am too. Cheers, Daan.

On Wed, Mar 12, 2003 at 12:14:54AM +0100, Wolfgang Thaller wrote:
I do agree that we should not require concurrency for CGA, i.e. that all callbacks should run synchronously and map as directly as possible to the callbacks supplied by the underlying back end libraries. Yes, please. You got my vote for that. No preemptive concurrency with GUIs.
Axel.

On Wed, 12 Mar 2003 08:22:46 +0000
Axel Simon
Yes, please. You got my vote for that. No preemptive concurrency with GUIs.
Can you explain a bit more what this is going to mean? I see many advantages in multithreading, and there's people wich works with multithreading in C and succeeds. Now, please make your choiches, but when you decide something important, in one direction or in another, please think and discuss it twice, playing the devil's advocate if necessary, and document your reasons; you are doing an important design work and with the results of this work people in the haskell community are going to present haskell as a real-world, general purpose programming language to potential customers. --- how to play the devil's advocate with the os threads matter :) --- Will we in the 2005, with hardware thread support in baby monitors and coffee machines, go to software engineers and tell them "you will do everything in one OS thread, but this functional language is really cool" ? Can we *really* be sure that the only FFI need of a real project is the GUI? So, can we really be sure that when the "threadsafe" attribute will be enabled by default in GHC, we are not going to make use of that feature?--- Vincenzo

On Wed, Mar 12, 2003 at 10:02:35AM +0100, Nick Name wrote:
On Wed, 12 Mar 2003 08:22:46 +0000 Axel Simon
wrote: Yes, please. You got my vote for that. No preemptive concurrency with GUIs.
Can you explain a bit more what this is going to mean? I see many advantages in multithreading, and there's people wich works with multithreading in C and succeeds. Don't get me wrong. OS thread support in GHC is good and CGA should cope with that. But I would like to have just one thread talking to the GUI.
--- how to play the devil's advocate with the os threads matter :) ---
Will we in the 2005, with hardware thread support in baby monitors and coffee machines, go to software engineers and tell them "you will do everything in one OS thread, but this functional language is really cool" ? ...everytime I faced real preemptive concurrency so far, I tried to restrict this behaviour to a small code fraction and abstract away from all the synchronization issues. I always fear that I miss something and
Concurrency is surely need in GUI applications. I think we talk about non-preemptive and preemptive. I know that Gtk is (and will stay) non-preemptive. As such there is no way of writing a preemptive CGA backend based on Gtk. My vote is therefore mostly Gtk-centric and not very objective, but... the whole system eventually falls over. So yes, one OS thread for the GUI, one for the database, one for each socket,... . I think this is not a restriction but the natural approach. Once we have OS threads, attaching something to the idle handler of (Gtk's) event loop is not necessary anymore since the Haskell RTS will continue to run. But every call to Gtk (or whatever library) will be channeled through this one thread. Is this a restriction? Axel.

On Wed, 12 Mar 2003 10:00:43 +0000
Axel Simon
I always fear that I miss something and the whole system eventually falls over. So yes, one OS thread for the GUI, one for the database, one for each socket,... . I think this is not a restriction but the natural approach.
This sounds very reasonable to my unexpert eyes. It's important to allow this scheme, since there is a simple and I guess frequent case of use; I leave it for a new thread to avoid confusion.
Once we have OS threads, attaching something to the idle handler of (Gtk's) event loop is not necessary anymore since the Haskell RTS will continue to run. But every call to Gtk (or whatever library) will be channeled through this one thread. Is this a restriction?
I hope not. Communication primitives are to be used sometimes :) But I am btw no expert on the subject, it's just an impression. Maybe the CGA could do this channeling itself? Would it be an headache in your opinion? Tests can already be done, since it's possible to compile GHC with -threaded-RTS or something like that. Vincenzo

Once we have OS threads, attaching something to the idle handler of (Gtk's) event loop is not necessary anymore since the Haskell RTS will continue to run. But every call to Gtk (or whatever library) will be channeled through this one thread. Is this a restriction?
I hope not. Communication primitives are to be used sometimes :) But I am btw no expert on the subject, it's just an impression.
Maybe the CGA could do this channeling itself? Would it be an headache in your opinion? Tests can already be done, since it's possible to compile GHC with -threaded-RTS or something like that. I just replied to Wolfgang asking what threadsafe means in C calls. As far as the rumors in my head are concerned the "channelling" you mention is
On Wed, Mar 12, 2003 at 07:02:41PM +0100, Nick Name wrote: then done by the FFI. Axel.

I hereby propose that: 1) Callbacks will be executed synchronously. No other events will be handled until a callback returns. Rationale: *) This maps directly to the execution model of all backend toolkits that I know *) You can easily get concurrency by calling forkIO, but going the other way is difficult. 2) Calls to the CGA can be made at any time, from any thread. The implementation is responsible for assuring that the serialization requirements of the backend toolkit are met. Number 1) is easy. Number 2) is more work for implementations, but it is necessary so that concurrent GUIs can easily be built on top of the CGA. Point 2) probably requires wrapping each and every foreign call in a CGA backend with some synchronization code. And I have one question: How many of the toolkits we are targeting support posting a user-defined event from a different OS thread than the event loop? AFAIK, Win32 and Mac OS Cocoa/Carbon do. We will have to choose between 2a) and 2b): 2a) If the CGA is currently waiting for an (OS-toolkit-level) event, it is possible that a call to the CGA from another concurrent Haskell thread will be blocked until an event arrives. 2b) If the CGA is currently waiting for an (OS-toolkit-level) event and another concurrent Haskell thread calls into the CGA, the CGA backend implementation has to make sure that the call can be handled in a reasonable amount of time (e.g. by posting an application-defined event or by registering a regular timer event). Can we have a vote on this, and, if we agree, put this down in the specification? We have to make some progress here sooner or later... My vote is for 1), 2) and 2b), unless somebody convinces me of something else... :-) Cheers, Wolfgang

On Wed, Mar 12, 2003 at 11:35:58AM +0100, Wolfgang Thaller wrote:
I hereby propose that:
1) Callbacks will be executed synchronously. No other events will be handled until a callback returns. Minor detail: There are these Win 3.1 style peekEvent functions in Gtk which let the event loop run a little bit to keep the GUI responsive (which means that other callbacks can be triggered). If this functionality is useful and supported, then your definition needs to be refined.
2a) If the CGA is currently waiting for an (OS-toolkit-level) event, it is possible that a call to the CGA from another concurrent Haskell thread will be blocked until an event arrives.
2b) If the CGA is currently waiting for an (OS-toolkit-level) event and another concurrent Haskell thread calls into the CGA, the CGA backend implementation has to make sure that the call can be handled in a reasonable amount of time (e.g. by posting an application-defined event or by registering a regular timer event).
Can we have a vote on this, and, if we agree, put this down in the specification? We have to make some progress here sooner or later...
My vote is for 1), 2) and 2b), unless somebody convinces me of something else... :-) Yes, I agree. Actually, when we have OS-threads in ghc, then 2a) seems to be more difficult to implement than 2b). Any program which does anything else than serving GUIs needs 2b), so it is really the only choice.
Axel.

I have not been following this discussion particularly closely, so can't really comment on the specifics. However, I think it is worth noting that Swing, Java's state-of-the-art cross-platform GUI toolkit, is NOT thread-safe, despite the fact that the Java language *and* libraries have had robust support for threads and multi-threaded programming from the start, and there was no shortage of developer resources or concurrent programming expertise on the Swing team. The decision not to make Swing thread-safe was deliberate, and was based in part on brutal experience by previous attempts to build thread-safe GUI toolkits (Trestle, SubArctic, etc.). I strongly encourage anyone who is thinking seriously about these issues to at least read the following short article: http://java.sun.com/products/jfc/tsc/articles/threads/threads1.html particularly the final section ("Why did we implement Swing This Way?") which discusses the rationale for their design. Remember: Worse Is Better. -antony -- Antony Courtney Grad. Student, Dept. of Computer Science, Yale University antony@apocalypse.org http://www.apocalypse.org/pub/u/antony

Antony Courtney
I strongly encourage anyone who is thinking seriously about these issues to at least read the following short article:
http://java.sun.com/products/jfc/tsc/articles/threads/threads1.html
particularly the final section ("Why did we implement Swing This Way?") which discusses the rationale for their design.
Very interesting.
Remember: Worse Is Better.
Indeed! Manuel

Hi Wolfgang, Thanks for your concrete proposal!
1) Callbacks will be executed synchronously. No other events will be handled until a callback returns. Rationale: *) This maps directly to the execution model of all backend toolkits that I know *) You can easily get concurrency by calling forkIO, but going the other way is difficult.
Good plan. Just to make things more clear, this means that all callbacks are executed sequentially.
2) Calls to the CGA can be made at any time, from any thread. The implementation is responsible for assuring that the serialization requirements of the backend toolkit are met.
Unfortunately, you are not clear about what kind of thread you mean: OS thread or Haskell thread? I therefore propose another rule: 2d) All haskell code is run in a single OS thread (the "GUI thread"). Calls to the CGA can be made at any time from any Haskell thread (within the GUI thread). Note that: - We can run many haskell threads within one OS thread. Furthermore, the interaction between Haskell threads is well understood and light weight. - The serialization requirements of the backend toolkit are automatically met since all these haskell threads run in the same OS thread. - All current Haskell systems can easily support this model -- in contrast, no haskell system (except the LVM in Helium :-) supports multiple OS threads to my knowledge. - We can implement all advanced event models on top of this model using concurrency. - We most surely do *not* want foreign C calls to be run in a different OS thread and don't want the "threadsafe" keyword here. (I am opposed to such extension -- complexity without reason - run your OS threads from C yourself!) About implementing concurrency: As you explained, a callback can use forkIO to achieve concurrent callbacks for example. However a more "real world" reason for having concurrency would be that a callback needs to do a lot of processing. As callbacks are run sequentially, the entire application will not react to events while the callback does its work. Therefore, a callback that needs to do a lot of processing should spawn a Haskell thread to do the processing (a worker thread) and return as soon as possible in order to stay reactive. Now, the big issue here is how to keep those Haskell threads running! At the primitive level, there is an eventloop that waits for events to happen, pops them of the queue, calls a Haskell callback, waits for its result, and loops again. When a callback returns that has just forked another Haskell thread, the eventloop will make an OS call to wait for the next event. Since we are running in a single OS thread, this will "disable" the Haskell scheduler and the worker thread will not run at all until another event happens! The question is how to solve this. Alastair Reid used the "yield" primitive to wait until all Haskell threads were done (or called yield themselves), but that wouldn't solve our particular problem as we want to stay reactive *and* have a lot of processing to do. Now, I think that we should use "idle time" events to solve this. It would stay nicely in our simple concurrency model and also keep the whole GUI desktop reactive (do we remember the old Winhugs bug :-). Basically, when an "idle" time event happens we call a callback that just sleeps for, say, 0.1 seconds, giving the Haskell scheduler time to run other haskell threads. It would be good of course when we have some hooks to determine whether there are any haskell threads waiting to be scheduled so we can avoid calling that handler when not necessary. We don't necessarily have to use real "idle" events: if there are Haskell treads waiting to be scheduled, we can also peek at the event queue, and when no new events have arrived, call the idle callback. When all haskell threads are done, we wait again. Note that this schedule also give more priority to new events, keeping the whole application highly reactive. Cheers, -- Daan.

2) Calls to the CGA can be made at any time, from any thread. The implementation is responsible for assuring that the serialization requirements of the backend toolkit are met.
Unfortunately, you are not clear about what kind of thread you mean: OS thread or Haskell thread?
The CGA will be called from Haskell code, so I'm talking of Haskell threads. However, an implementation may choose to use any OS thread it likes to execute the Haskell code, so I'm also talking of OS threads.
I therefore propose another rule:
2d) All haskell code is run in a single OS thread (the "GUI thread"). Calls to the CGA can be made at any time from any Haskell thread (within the GUI thread).
My vote is a big NO: This means restricting what a Haskell runtime system is allowed to do.
Note that: - We can run many haskell threads within one OS thread. Furthermore, the interaction between Haskell threads is well understood and light weight.
But there is no standard way to manage the correspondence between haskell threads and OS threads. Relying on the fact that most current implementations use one OS thread for all haskell threads is just asking for trouble.
- The serialization requirements of the backend toolkit are automatically met since all these haskell threads run in the same OS thread.
... unless the toolkit uses some global state variable (the "current graphics port" in carbon; drawing to one window from multiple _haskell_ threads simultanously would probably be a problem for Win32, too, as Drawing Context state may be modified in the process).
- All current Haskell systems can easily support this model -- in contrast, no haskell system (except the LVM in Helium :-) supports multiple OS threads to my knowledge.
The "threaded RTS" is disabled by default in GHC 5.04, because it wasn't well tested and contained several bugs. I fixed a whole lot of bugs for the GHC 5.05 CVS version, and therefore I hope htat GHC 5.06 will fully support using multiple OS threads.
- We can implement all advanced event models on top of this model using concurrency.
- We most surely do *not* want foreign C calls to be run in a different OS thread and don't want the "threadsafe" keyword here. (I am opposed to such extension -- complexity without reason - run your OS threads from C yourself!)
We (I have to admit, that means: I) most surely *do* want the "threadsafe" keyword here. After all, it is a feature that I have been taking for granted before I switched from C++ to Hasell. Having to think about an implementation detail of Haskell where things work "just right" when you use C++ is scary and definitely the wrong way to go. That's why threadsafe calls are the future. (On the other hand,the behaviour of "safe" calls has never been specified accurately, is difficult to implement in implementations that properly support multiple OS threads, and will probably have to go, unless somebody comes up with a meaningful and implementable specification on ffi@haskell.org).
As you explained, a callback can use forkIO to achieve concurrent callbacks for example. However a more "real world" reason for having concurrency would be that a callback needs to do a lot of processing. As callbacks are run sequentially, the entire application will not react to events while the callback does its work. Therefore, a callback that needs to do a lot of processing should spawn a Haskell thread to do the processing (a worker thread) and return as soon as possible in order to stay reactive.
Agreed.
Now, the big issue here is how to keep those Haskell threads running! [...]
With threadsafe calls, that problem disappears. There'll just be normal synchronization issues like in every other language.
When a callback returns that has just forked another Haskell thread, the eventloop will make an OS call to wait for the next event. Since we are running in a single OS thread, this will "disable" the Haskell scheduler and the worker thread will not run at all until another event happens!
The question is how to solve this. Alastair Reid used the "yield" primitive to wait until all Haskell threads were done (or called yield themselves), but that wouldn't solve our particular problem as we want to stay reactive *and* have a lot of processing to do.
Now, I think that we should use "idle time" events to solve this. It would stay nicely in our simple concurrency model and also keep the whole GUI desktop reactive (do we remember the old Winhugs bug :-). Basically, when an "idle" time event happens we call a callback that just sleeps for, say, 0.1 seconds, giving the Haskell scheduler time to run other haskell threads. It would be good of course when we have some hooks to determine whether there are any haskell threads waiting to be scheduled so we can avoid calling that handler when not necessary. We don't necessarily have to use real "idle" events: if there are Haskell treads waiting to be scheduled, we can also peek at the event queue, and when no new events have arrived, call the idle callback. When all haskell threads are done, we wait again. Note that this schedule also give more priority to new events, keeping the whole application highly reactive.
I don't like that. Having those "hooks" into the runtime system sounds like much more unnecessary (and implementation-specific) complexity than the "threadsafe" extension. Not having those hooks would waste some CPU time. It should of course be possible to implement the CGA on single-OS-thread haskell implementations (using the "idle time" scheme you proposed above). But we should write the specification in a way that allows taking advantage of "threadsafe". It would be a big mistake to write the specification in a way that is incompatible with a planned feature of the next version of GHC. Cheers, Wolfgang

On Thu, Mar 13, 2003 at 12:42:04AM +0100, Wolfgang Thaller wrote: > >- We most surely do *not* want foreign C calls to be run in a > >different OS thread > > and don't want the "threadsafe" keyword here. (I am opposed to such > >extension -- > > complexity without reason - run your OS threads from C yourself!) > > We (I have to admit, that means: I) most surely *do* want the > "threadsafe" keyword here. I am not on the FFI list. Could you explain what threadsafe means in a call to C? I though you would specify a thread name each time you call C and the Haskell RTS would make sure that you always call C with the same OS thread. > >Now, I think that we should use "idle time" events to solve this. We just had this discussion on gtk2hs-users. I propose: 3) if the underlying Haskell RTS is single threaded, the library should automatically call "yield" whenever it is idle to ensure that forked Haskell processes continue to run. Then, no matter if the RTS is multithreaded or not, the user would always experience the same behaviour. > It would be a big mistake to write the specification in a way that is > incompatible with a planned feature of the next version of GHC. We don't want to break with GHC, do we? :-) Axel.

Axel Simon wrote:
I thought this would come for free with the FFI if we wrap all GUI calls with
foreign ccall thread "GUI" blah...
which means all calls are done from a single OS thread called "GUI". I suspect this didn't wind up in the FFI, or did it?
No, there's no such thing.
I am not on the FFI list. Could you explain what threadsafe means in a call to C? I though you would specify a thread name each time you call C and the Haskell RTS would make sure that you always call C with the same OS thread.
No. Threadsafe (which is part of the FFI addendum) means that the RTS should do everything that's necessary so that other threads won't block during a lengthy foreign call. For example: foreign import ccall threadsafe "DoALotOfWorkInC" doALotOfWorkInC :: IO () foo = forkIO doALotOfWorkInHaskell >> doALotOfWorkInC Without the "threadsafe" attribute, doALotOfWorkInHaskell would get hardly any time to run. With "threadsafe", both would continue to run. One of the most efficient ways to implement this feature is to switch the Haskell runtime from one thread to another. This has been implemented in GHC (as an optional feature, disabled by default) since version 5.02 or even earlier. It still is substantially faster than using OS threads directly, but Haskell programmers should no longer need to be aware of the fact that Haskell threads are not OS threads. Axel Simon wrote:
3) if the underlying Haskell RTS is single threaded, the library should automatically call "yield" whenever it is idle to ensure that forked Haskell processes continue to run.
Agreed. The idle callback will waste a little time, but I think that's worth it. Cheers, Wolfgang

I therefore propose another rule:
2d) All haskell code is run in a single OS thread (the "GUI thread"). Calls to the CGA can be made at any time from any Haskell thread (within the GUI thread).
My vote is a big NO: This means restricting what a Haskell runtime system is allowed to do.
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. I agree that my formulation was a bit too vague, here is a better version: 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. (In particular, this formulation doesn't restrict the Haskell runtime system at all. I think that you worry about: 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, but secondly, such system will surely provide a forkIO variant that runs the Haskell thread in the same OS thread. 2) What if I want to call the CGA from some other OS thread? Well, you should just use a co-thread to do it: schedule the call from a haskell thread running in the OS GUI thread and communicate the result via MVar's. 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. )
- The serialization requirements of the backend toolkit are automatically met since all these haskell threads run in the same OS thread.
... unless the toolkit uses some global state variable (the "current graphics port" in carbon; drawing to one window from multiple _haskell_ threads simultanously would probably be a problem for Win32, too, as Drawing Context state may be modified in the process).
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.
- We can implement all advanced event models on top of this model using concurrency.
- We most surely do *not* want foreign C calls to be run in a different OS thread and don't want the "threadsafe" keyword here. (I am opposed to such extension -- complexity without reason - run your OS threads from C yourself!)
We (I have to admit, that means: I) most surely *do* want the "threadsafe" keyword here. After all, it is a feature that I have been taking for granted before I switched from C++ to Hasell. Having to think about an implementation detail of Haskell where things work "just right" when you use C++ is scary and definitely the wrong way to go. That's why threadsafe calls are the future.
You may think so, but I believe that it is much better to leave the user in control. The "threadsafe" will spawn implicit OS threads and I consider this a bad thing. If I have the ability to spawn OS threads from with Haskell (and when all C calls are made within the same OS thread), I can get the "threadsafe" behaviour without using some implicit mechanism -- spawn a new OS thread, run a Haskell thread in there that makes the C call and wait for its result via an MVar. I really think that OS thread resources are too complex to be managed automagically and that you should just provide some primitive calls to manage OS threads. Usage will maybe show interesting patterns that can be captured in combinators. (Note that no extensions to the FFI are needed and OS threads can be selectively supported by implementations by implementing a single function to spawn an OS thread: forkOS :: IO () -> IO OSThreadId -- new OS thread forkIOIn :: IO () -> OSThreadId -> IO ThreadId -- new Haskel thread in specific OS thread getOSThreadId :: IO OSThreadId -- get current OS thread id But again, this really belongs to the FFI mailing list and I don't really want to discuss this in length.)
Now, the big issue here is how to keep those Haskell threads running! [...]
With threadsafe calls, that problem disappears. There'll just be normal synchronization issues like in every other language.
Well, you introduced heavy weight synchronisation that has to be wrapped around *every* foreign call to the GUI. Furthermore, you imply that a worker thread should be run in a different OS thread to keep everything reactive. 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. Only heavy processing or should/could use OS threads -- not simple GUI components. OS thread support is beyond the scope of this library, we just have to make sure that it can work with future extensions -- and my proposal does so.
I don't like that. Having those "hooks" into the runtime system sounds like much more unnecessary (and implementation-specific) complexity than the "threadsafe" extension. Not having those hooks would waste some CPU time.
Those "hooks" are just haskell calls: "threadsRunning :: IO Bool", nothing special here, should have been in the API anyway.
It should of course be possible to implement the CGA on single-OS-thread haskell implementations (using the "idle time" scheme you proposed above) . But we should write the specification in a way that allows taking advantage of "threadsafe".
The specification shouldn't talk about this at all. If someone wants multiple OS threads, it should be possible to use them with the specification, that is all.
It would be a big mistake to write the specification in a way that is incompatible with a planned feature of the next version of GHC.
I don't think that the proposed rule is incompatible with a "planned feature of the next version of GHC". 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! All the best, Daan.

Daan Leijen
I therefore propose another rule:
2d) All haskell code is run in a single OS thread (the "GUI thread"). Calls to the CGA can be made at any time from any Haskell thread (within the GUI thread).
I don't think the GUI design needs to talk about this because which OS thread is executing Haskell code is not observable*. The only way that a particular OS thread is observable is which OS thread executes foreign code. This issue is being addressed by the FFI group and I think we're converging on a solution. -- Alastair Reid alastair@reid-consulting-uk.ltd.uk Reid Consulting (UK) Limited http://www.reid-consulting-uk.ltd.uk/alastair/ * This is a slight overstatement. It is possible to observe which OS thread is doing what using things akin to 'system "/bin/ps aux"' but such "observations" don't seem to be relevant here.

Daan Leijen
One can implement all other models on top of this: - you just keep this model and use callbacks. This is what GIO, HsGTK and TkGofer do. - you make the callbacks behave concurrently (this would lead to the bug in the example though). How could you do that? well, when in Haskell, you fork a thread to handle an event and return directly. Would it work? No! because the eventloop will immediately wait for the next event. As this is a C call, the haskell runtime system will not run any haskell threads.
Note that HGL (which works on both Hugs and GHC) does something a lot like what you describe. The reason it works on HGL is that the event loop thread calls 'yield' before executing the blocking call to get the next event (on X11) or returning from the event callback (on Win32). Yield pushes the current thread down to the lowest priority so all the other threads get to run until they terminate, block or yield before the event loop thread gets to continue. [Caveat: The semantics of yield is the Hugs semantics, the GHC semantics are a little different and should, perhaps be strengthened.] In short, it's perfectly possible to have the callbacks run concurrently. Whether it's a good way to structure systems I leave to the usual contributors to this list. -- Alastair Reid alastair@reid-consulting-uk.ltd.uk Reid Consulting (UK) Limited http://www.reid-consulting-uk.ltd.uk/alastair/
participants (8)
-
Alastair Reid
-
Antony Courtney
-
Axel Simon
-
Daan Leijen
-
George Russell
-
Manuel M T Chakravarty
-
Nick Name
-
Wolfgang Thaller