
Oh gosh I'm glad we've got a decent debate going on this ... Wolfgang Thaller wrote (snipped)
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.
I am strongly opposed to this proposal. As a matter of fact this does *not* map directly to the execution model of HTk, in the sense that if I wanted to add such callbacks to HTk I would have to add an extra lock to prevent callbacks running synchronously. However there is a more fundamental objection. I think in general this does not scale. For example initialisation code (1) might occasionally need to put up a window (eg one that says "Can't find configuration file, please enter new path") (2) might, thanks to the common paradigm of doing initialisation using unsafePerformIO, need to be run almost anywhere in the program, including during a callback. The only way of avoiding that is to force the user to adopt a paradigm where no callback may make use of a service which might require an initialisation (or anything else) which itself requires a GUI event. This is highly unpleasant. Also this way of programming is highly unpleasant. For example, if I have a button "Send an insult to Simon.Peyton-Jones@microsoft.com" and it wants to put up a window saying "Do you really want to insult Simon Peyton-Jones?" then you need a chain of callbacks. The callback attached to the first button is not allowed to insult Simon PJ itself, instead it has to attach a continuation (containing the insult) to the confirmation button. In general this means continuation-passing-style is going to infect this part of the program. For programs where almost everything is initiated by user action, an awful lot of them is going to have to be written in CPS.

George Russell wrote:
Oh gosh I'm glad we've got a decent debate going on this ...
Wolfgang Thaller wrote (snipped)
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.
I am strongly opposed to this proposal. As a matter of fact this does *not* map directly to the execution model of HTk, in the sense that if I wanted to add such callbacks to HTk I would have to add an extra lock to prevent callbacks running synchronously. However there is a more fundamental objection.
What do you mean by "callbacks running synchronously"? Let's assume that, in a button callback, I do some calculation that takes about one or two seconds, and then show a dialog box. To me, "asynchronous callbacks" would mean that I could click other buttons and trigger other actions before the dialog shows. I don't know how to prevent that if I don't have a guarantee that callbacks are synchronous. Going the other way is easy - just wrap the callback handler in a call to forkIO.
I think in general this does not scale. For example initialisation code (1) might occasionally need to put up a window (eg one that says "Can't find configuration file, please enter new path") (2) might, thanks to the common paradigm of doing initialisation using unsafePerformIO, need to be run almost anywhere in the program, including during a callback. The only way of avoiding that is to force the user to adopt a paradigm where no callback may make use of a service which might require an initialisation (or anything else) which itself requires a GUI event. This is highly unpleasant.
I think invoking a dialog from unsafePerformIO is highly unpleasant. But I think you are misunderstanding my proposal. A callback may, of course, start a "nested" event loop for handling a modal dialog. You can use all the concurrency you want by wrapping your handlers in forkIO (if my proposal 2b, or a variant thereof, is accepted). I'll try to rephrase my proposal 1: 1) The event loop will not invoke a callback action until the previous callback action has returned, i.e. callbacks will be invoked sequentially, not concurrently. 1a) Event loops may be nested, i.e. a callback, or (if proposal 2 is also accepted), any other IO action, may call a CGA function that runs another event loop, e.g. for handling an application-modal dialog (the main event loop won't handle events during that time).
Also this way of programming is highly unpleasant. For example, if I have a button "Send an insult to Simon.Peyton-Jones@microsoft.com" and it wants to put up a window saying "Do you really want to insult Simon Peyton-Jones?" then you need a chain of callbacks. The callback attached to the first button is not allowed to insult Simon PJ itself, instead it has to attach a continuation (containing the insult) to the confirmation button. In general this means continuation-passing-style is going to infect this part of the program. For programs where almost everything is initiated by user action, an awful lot of them is going to have to be written in CPS.
I think Haskell's type system already makes it illegal to insult Simon PJ (at least in GHC) ;-). Seriously, as I said, I was not talking about forcing people to use CPS. The intent is to allow, but to NOT require concurrency. In other words, my proposal 1) refers to the same event handling model as used by GTK, Carbon, and Win32 (with the exception that Win32 uses an explicit event loop, while the others have RunApplicationEventLoop() and gtk_whatsitcalled). Proposal 2) (especially 2b) talks about extending this to support all concurrency you could possibly ever want, without forcing concurrency on everybody. Cheers, Wolfgang

On Wed, 12 Mar 2003 19:05:51 +0100
Wolfgang Thaller
1a) Event loops may be nested, i.e. a callback, or (if proposal 2 is also accepted), any other IO action, may call a CGA function that runs another event loop, e.g. for handling an application-modal dialog (the main event loop won't handle events during that time).
I am sorry to make you repeat yourself, but I wish to understand it well. Event loops may be nested, but: there will be a way to avoid that. Is this what you are saying? There needs to be a way to make both a callback and its parent window handle events simultaneously, even in non-preemptive environments, without forkIO. I hate when a dialog hangs a program (sometimes it happens to be popped up in a wm layer lower than the application, guess what happens then), seriously, so I need a way to code a program and be sure that, on any implementation, callbacks will be handled properly even when there are dialogs popped up. I am sure that you are previewing this case and handling gracefully, just want to understand how you would do it in a non preemptive environment. Vincenzo

Nick Name wrote:
On Wed, 12 Mar 2003 19:05:51 +0100 Wolfgang Thaller
wrote: 1a) Event loops may be nested, i.e. a callback, or (if proposal 2 is also accepted), any other IO action, may call a CGA function that runs another event loop, e.g. for handling an application-modal dialog (the main event loop won't handle events during that time).
I am sorry to make you repeat yourself, but I wish to understand it well. Event loops may be nested
... in order to allow application-modal dialogs to be created as easily as with Win32, Carbon, GTK, etc.
, but: there will be a way to avoid that. Is this what you are saying?
I haven't said that. But it's probably a good idea. However, I think this is a (partly) independent issue, so those details should be discussed separately (as point 3, for example). I was only saying that we should do without concurrency by default (unless somebody wants to use concurrency). It's obvious that all kinds of applications are possible that way (GTK, Win32, Carbon, Cocoa, Swing and many more all work without concurrency). The discussion about how to do it exactly, how to handle modal dialogs and other things like that should probably be the next discussion topic.
There needs to be a way to make both a callback and its parent window handle events simultaneously, even in non-preemptive environments, without forkIO.
Agreed (at least if I understood you correctly). Here's a first guess on how things could be done. This is not a real proposal yet, it's just a first guess, heavily influenced by Apple's APIs: *) non-modal windows: If a callback wants to open a non-modal window, it should just open the window, attach some callbacks to the window and return to the main event loop. No special issues here. *) application-modal dialogs: For application-modal windows, at least Win32 and Apple's APIs (don't know about GTK and others) provide a function that runs a nested event loop. For expose events and other events that still have to be handled when a modal dialog is active, this nested event loop invokes the appropriate callbacks. *) window-modal dialogs: We should also support window-modal dialogs for those platforms that support it --- I only know that they are supported on Mac OS X, I'm not sure if users of other platforms would even want them. If a backend doesn't support window-modal dialogs, it can treat them as application-modal instead. The above things are just first ideas. The main point is, who agrees/disagrees that (I'm just rephrasing my original two points again): 1) We should adopt a callback scheme that does not require concurrency 2) CGA implementations should automatically do all necessary synchronization so that the CGA fully supports concurrency even if the backend library does not. (2b) We should be careful to ensure that CGA functions can be invoked from a "background" thread while the "main" thread is waiting for events (means additional work, but it is probably necessary if we want to fully use concurrency). Cheers, Wolfgang

Wolfgang Thaller wrote:
2) CGA implementations should automatically do all necessary synchronization so that the CGA fully supports concurrency even if the backend library does not.
I suspect that this might be a bad idea, and I'm not totally sure that
it's even possible. I think that it would be better to defer this
decision until the feasibility can be assessed.
--
Glynn Clements

On Wed, 12 Mar 2003 23:46:57 +0100
Wolfgang Thaller
, but: there will be a way to avoid that. Is this what you are saying?
I haven't said that. But it's probably a good idea. However, I think this is a (partly) independent issue, so those details should be discussed separately (as point 3, for example).
I am by no means useful in a discussion about strategies in multithreading. I rephrase my wish, and leave it here for the mission statement. GUI libraries wich make the application hang when a dialog is created are a bad idea. We need to have a way, even in hugs, to control the events that are to be sent to the underlying parent window when a dialog is created. Vincenzo

Nick Name
GUI libraries wich make the application hang when a dialog is created are a bad idea. We need to have a way, even in hugs, to control the events that are to be sent to the underlying parent window when a dialog is created.
Note that the HGL successfully does this on Hugs so we know that this can be implemented. A demonstration would be something like: - One window running an animation serviced by its own Haskell thread. - One window running a dialog box asking if you want to quit. Obviosuly, we'd need to make any reactive widgets in the animation window unresponsive while the dialog box is open. This isn't quite the same as blocking all events though because, for example, the animation window should still respond to redraw events. [What can't be implemented on Hugs is to have a thread doing some big calculation (like factoring large numbers) in one thread while other threads continue to run. The difference is because threads can only be preempted when in the IO monad (and, at least for now, only at certain places in the IO monad).] -- Alastair Reid alastair@reid-consulting-uk.ltd.uk Reid Consulting (UK) Limited http://www.reid-consulting-uk.ltd.uk/alastair/

*) non-modal windows: If a callback wants to open a non-modal window, it should just open the window, attach some callbacks to the window and return to the main event loop. No special issues here.
*) application-modal dialogs: For application-modal windows, at least Win32 and Apple's APIs (don't know about GTK and others) provide a function that runs a nested event loop. For expose events and other events that still have to be handled when a modal dialog is active, this nested event loop invokes the appropriate callbacks. For information: yes, same thing in Gtk. You just call mainLoop again (or runDialog) which lets the global event loop run. The underlying application window would still emit redraw/expose events but no buttons could be pressed since it cannot get the input focus as long as the modal
On Wed, Mar 12, 2003 at 11:46:57PM +0100, Wolfgang Thaller wrote: dialog is open. I guess this is the same in all three backends. [..]
1) We should adopt a callback scheme that does not require concurrency 2) CGA implementations should automatically do all necessary synchronization so that the CGA fully supports concurrency even if the backend library does not. (2b) We should be careful to ensure that CGA functions can be invoked from a "background" thread while the "main" thread is waiting for events (means additional work, but it is probably necessary if we want to fully use concurrency). Glynn oposed to this, he said it could be very difficult or even impossible. 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? Axel.
participants (6)
-
Alastair Reid
-
Axel Simon
-
George Russell
-
Glynn Clements
-
Nick Name
-
Wolfgang Thaller