
I'm not sure whether asynchronous exceptions should be in Haskell'. I don't feel entirely comfortable about the "interruptible operations" facet of the design, and I'm hoping that STM can clean things up: after all, STM already gives you a much nicer way to program in an exception-safe way, as long as you aren't doing any real I/O. The fact that throwTo can interrupt a takeMVar, but can't interrupt a foreign call, even a concurrent one, is a bit strange. We have this odd situation in GHC right now where throwTo can interrupt threadDelay on Unix, but not on Windows because threadDelay maps directly to a foreign call to Sleep() on Windows. To fix this I have to implement the I/O manager thread on Windows (I should do this anyway, though). On 03 April 2006 07:38, John Meacham wrote:
however, in a preemptive one, it is less clear whether they may be delivered right away or not and what the implementation costs are, as threads arn't necessarily blocked at a point where they can happily deal with an exception. Perhaps the yhc guys have thought about the problem?
In fact, GHC's SMP mode still doesn't implement throwTo, mainly because it's rather difficult and I need to put some serious thought into how to do it.
there are also a few questions we would want answered for the spec
* do we require the thrower to 'block' until the signal is recieved? (only relevant to pre-emptive implementations)
GHC's throwTo does block until the exception is delivered. The main argument for doing it this way is that you only need an extra forkIO to get the other version. Another argument is that it gives you more guarantees: if two threads throwTo each other, only one will succeed.
* what happens if mutilple thrown exceptions "pile up" before the catcher gets to them?
They get delivered in some order (fairness probably applies in the same way to threads blocked on MVars).
* what happns to exceptions that fall off the end of threads, or the main thread? (should be answered anyway)
throwTo a thread that has completed is a no-op.
* promtness? how much work is the target allowed to do before it "sees" the exception? pthreads allows an implementation to delay processing an exception to a "cancellation point" do we want the same thing in haskell? if not, how will this affect OS threaded implementations?
The only guarantee you can give is "the exception isn't delayed indefinitely, unless the target thread remains inside a block". Just like the fairness property for MVars. Cheers, Simon

On Tue, Apr 04, 2006 at 01:33:39PM +0100, Simon Marlow wrote:
I'm not sure whether asynchronous exceptions should be in Haskell'. I don't feel entirely comfortable about the "interruptible operations" facet of the design, and I'm hoping that STM can clean things up: after all, STM already gives you a much nicer way to program in an exception-safe way, as long as you aren't doing any real I/O.
For me, asynchronous exceptions are the primary reason to use concurrent Haskell. They're the only way I'm aware of to write a program that handles signals in Haskell, and it's be a real shame to leave Haskell' programs unable to handle signals--it means that any real-world programs that deal with locking or the like will need to use non-standard extensions. Unless you can come up with some other way to deal with signals. Having no chance to clean up when control-C is hit isn't an acceptable alternative, and neither is simply ignoring control-C and forcing users to run kill (and then once again get no chance to clean up!). Another option would be to hard-code signals into some sort of ordinary exceptions, but that's not very good, since you'd then still want to split exceptions into two camps, so that you could run a catch that only catches signals generated *by* the IO action that you're running (i.e. you often want to "remove a file, and ignore any sort of failure", but don't want to accidentally ignore a sigTERM that arrives during this process). I suppose you could do this with the complicated catchJust, but that's a pain. It's nice having a small (and documented) set of exceptions that most IO operations can throw.
The fact that throwTo can interrupt a takeMVar, but can't interrupt a foreign call, even a concurrent one, is a bit strange. We have this odd situation in GHC right now where throwTo can interrupt threadDelay on Unix, but not on Windows because threadDelay maps directly to a foreign call to Sleep() on Windows. To fix this I have to implement the I/O manager thread on Windows (I should do this anyway, though). [...] The only guarantee you can give is "the exception isn't delayed indefinitely, unless the target thread remains inside a block". Just like the fairness property for MVars.
I think this is fine. There's no need for strong guarantees that asynchronous exceptions are delivered soon or interrrupt any particular external function calls, or even interrupt particular standard library functions. At least to me, that's what makes them asynchronous. In other words, I wouldn't mind cooperative asynchronous function calls. Which is to say, that I don't see any reason the standard IO library shouldn't be allowed (by the standard) to use block in all its calls. This does limit the power of their application to signal-handling, but if you really want signal-catching in ffi functions, those functions could install their own signal-catchers. My main concern is that as far as I can see, without asynchronous exceptions there's no way to implement this sort of functionality in pure Haskell. Actually, I suppose you could do this with a (cooperative) implementation of asynchronous exceptions using just MVars and concurrency by rewriting all the IO calls you use to first check whether an asynchronous exception has been thrown, but rewriting all the std library functions seems like a rather crude way of doing this. On the other hand, I suppose that this could also provide a reference implementation of asynchronous exceptions for any Haskell' that supports concurrency... -- David Roundy http://www.darcs.net

On Wed, Apr 05, 2006 at 07:47:08AM -0400, David Roundy wrote:
For me, asynchronous exceptions are the primary reason to use concurrent Haskell. They're the only way I'm aware of to write a program that handles signals in Haskell, and it's be a real shame to leave Haskell' programs unable to handle signals--it means that any real-world programs that deal with locking or the like will need to use non-standard extensions. Unless you can come up with some other way to deal with signals. Having no chance to clean up when control-C is hit isn't an acceptable alternative, and neither is simply ignoring control-C and forcing users to run kill (and then once again get no chance to clean up!).
I have been giving signals some thought, and resarching what other languages do, and have a semi-proposal-maybe. signals tend to be used for one of a couple purposes (some can fall into multiple categories): signal a synchronous exceptional event - SIGFPE, SIGPIPE, SIGILL, SIGSEGV signal an asynchronous exceptional event - SIGINT, SIGHUP (interactive) inform the app of an event it might want to take note of - SIGALRM, SIGCHLD, SIGWINCH, SIGHUP (daemon) I think it would make sense to have 3 mechanisms to cover these cases. signal a synchronous exceptional event - raise a (possibly imprecise) exception on the thread that produced the signal. signal an asynchronous exceptional event - the user should be able to choose the threads on which they wish to catch these, those that need to clean up after themselves. inform the app of an event it might want to take note of - these should run on their own thread, concurrently to all other threads data Signal = ... data SigInfo = SigInfo { ... } -- | declare which signals should throw a synchronous exception always (because they are thread specific) signalException :: [Signal] -> IO () signalExceution = ... -- | declare that a thread should catch the given asynchronous signals during the processing of the argument signalCatch :: [Signal] -- ^ the signals I care about -> IO a -- ^ while running this, signals will be delivered as an exception to this one. (not necessarily exclusively) -> IO a signalCatch = ... -- | declare a signal should perform a task in its own thread, exceptions thrown will be discarded. signalAction :: [Signal] -> (SigInfo -> IO ()) -> IO () signalAction = ... these roughly corespond to the Core,Term,and Ign type signals respectivly. ideally, we should specify some default behaior, ILL,FPE,SEGV raise synchronous exceptions SIGPIPE ignored John -- John Meacham - ⑆repetae.net⑆john⑈

"Simon Marlow"
I'm not sure whether asynchronous exceptions should be in Haskell'. I don't feel entirely comfortable about the "interruptible operations" facet of the design,
I designed that differently for my language. There is a distinct "synchronous mode" where asynchronous exceptions are handled by certain operations only, similarly to POSIX deferred mode of thread cancellation. This allows to use blocking operations without being interrupted. http://www.cs.ioc.ee/tfp-icfp-gpce05/tfp-proc/06num.pdf Actually I support asynchronous signals, not just exceptions: the reaction to a signal can be something other than throwing an exception. For example Linux SIGWINCH should be handled by resizing and repainting the screen at an appropriate moment, not by aborting any computation in progress.
The fact that throwTo can interrupt a takeMVar, but can't interrupt a foreign call, even a concurrent one, is a bit strange.
When entering "foreign mode" in C code embedded in Kogut (which
corresponds to concurrent foreign imports in Haskell but is less
convenient to use), it's possible to specify how that thread wishes
to be interrupted in case someone else sends it an asynchronous signal
during the foreign trip.
The only implemented application of this mechanism is sending a Unix
signal. This is enough to interrupt blocking syscalls like waitpid.
If waitpid fails and errno == EINTR, pending signals for this thread
are processed and waiting continues (unless some signal handler has
thrown an exception).
Implementing this without race conditions requires a sigsafe library
or something equivalent.
John Meacham
* do we require the thrower to 'block' until the signal is recieved? (only relevant to pre-emptive implementations)
My language doesn't do it, and I'm not convinced that Haskell should block. It's more efficient to make this non-blocking, and I think usually this is what is needed.
* what happens if mutilple thrown exceptions "pile up" before the catcher gets to them?
In my language each thread has a queue of pending asynchronous signals, and they are processed in order. Handling an asynchronous signal, or throwing an exception until it is handled, blocks further signals automatically, so more signals are processed only after the previous signal was handled. An exception handler is not in a tail position wrt. the catching construct, for two reasons: the state of asynchronous signals is restored after handling the exception, and a stack trace shown when the exception is propagated to the toplevel without being handled includes code in unfinished exception handlers. There is a separate exception handling syntax when the exception should be considered already handled, for cases when the exception handler should be in a tail context.
* what happns to exceptions that fall off the end of threads, or the main thread? (should be answered anyway)
In my case a thread body ends with a value or with an exception, and this can be examined when joining a thread, or by default the exception is propagated in the joiner. This has a disadvantage that errors in threads nobody waits for might be left undetected, unless they use an explicit wrapper. For the main thread there is a settable handler of exceptions reaching the toplevel, which by default handles some exceptions specially (Unix signals, and a request of program termination), and others are printed along with a stack trace.
* promtness? how much work is the target allowed to do before it "sees" the exception? pthreads allows an implementation to delay processing an exception to a "cancellation point" do we want the same thing in haskell?
Perhaps. My design includes that.
David Roundy
It would also be nice to address signal behavior, and by default state that signals should be converted to asynchronous exceptions.
This is not enough for SIGWINCH, or for SIGHUP used to trigger reloading configuration files. OTOH purity of Haskell's functional subsystem has some nice consequences for asynchronous exceptions which don't have to carry over to asynchronous signals which don't necessarily abort the computation. If the signal is guaranteed to abort some part of IO code, then it makes sense to revert thunks under evaluation. If the signal only causes to execute some handler, then reverting them might be wasteful, as they will soon be needed again.
The only downside I can see of this as default behavior would be that in cooperative systems the response to a sigTERM might be very slow.
Right, it's a pity, and I agree that benefits outweigh this problem. In my implementation the thread which handles system signals (settable, defaults to the main thread) needs to be chosen by the scheduler in order to process the signal. It might take some time if there is a lot of threads. -- __("< Marcin Kowalczyk \__/ qrczak@knm.org.pl ^^ http://qrnik.knm.org.pl/~qrczak/
participants (4)
-
David Roundy
-
John Meacham
-
Marcin 'Qrczak' Kowalczyk
-
Simon Marlow