
On 05 April 2006 16:46, Marcin 'Qrczak' Kowalczyk wrote:
"Simon Marlow"
writes: 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.
I think it's unnecessary to treat signals in the way you do - you're assuming that a signal interrupts the current thread and runs a new computation (the signal handler) on the same stack, completely blocking the interrupted thread until the signal handler completes. This is the wrong way to view signal handlers, IMO: they should run in completely separate threads (perhaps a higher priority thread, if possible). + you don't have to block signals just because you happen to be holding a mutex. Synchronisation with a signal handler is just synchronisation with another thread. + protecting the invariants of your mutable state from signal handlers is done using existing mechanisms for sharing mutable state with other threads + you can't just raise an exception from a signal handler, it has to be thrown to another thread. This raises the need for asynchronous exception handling and block/unblock, but that's simpler than signal handling. Basically, the whole situation is much simpler if signals don't *interrupt* threads. Of course I'm talking here about signals that are truly asynchronous. Synchronous signals should ideally just turn into exceptions directly (synchronous exceptions, of course). I agree with your assessment of the problems with interruptible operations in GHC: that it is impossible to completely block async exceptions across a computation. We could certainly add a way to do this. Is that the substance of your objection to GHC's async exception mechanism? I couldn't completely follow all the arguments from your paper (some example code would help!). Suppose we had block/unblock count scopes, as you suggested in your paper. We considered this at the time of the design, but rejected it because we wanted the property that unblock always unblocks exceptions. I must admit I can't see why this is an important property, now that I think about it; for modularity it seems you want the opposite. What about interruptible operations? We certainly want operations that are interruptible inside a block - that's the whole point. But we might also want to perform these same operations in a non-interruptible way, as you argued. So I propose also adding blockAll :: IO () -> IO () which is just like block, but it doesn't allow interruptible operations either. This gives us the full range of synchronous/asynchronous/blocked that pthreads has. Cheers, Simon

"Simon Marlow"
I think it's unnecessary to treat signals in the way you do - you're assuming that a signal interrupts the current thread and runs a new computation (the signal handler) on the same stack, completely blocking the interrupted thread until the signal handler completes. This is the wrong way to view signal handlers, IMO: they should run in completely separate threads (perhaps a higher priority thread, if possible).
This can be emulated in my model: by designating a thread for system signals, possibly even letting it spawn a new thread for each signal. Most Unix signals are supposed to abort the process however, and thus a mechanism for aborting one thread from another is needed anyway. I think async exceptions are not that much easier than async signals. Async signals include the ability to pause threads in safe points, which is needed for SIGSTOP / SIGTSTP and for my fork() wrapper. This is not archievable with signals spawning threads + async exceptions.
+ you don't have to block signals just because you happen to be holding a mutex. Synchronisation with a signal handler is just synchronisation with another thread.
It's still very probable that taking a mutex coincides with the need to block async exceptions: an async exception in the middle of a critical section implies a danger of leaving data in an inconsistent state. Reasons for automatic blocking of async signals carry over to async exceptions.
I agree with your assessment of the problems with interruptible operations in GHC: that it is impossible to completely block async exceptions across a computation. We could certainly add a way to do this. Is that the substance of your objection to GHC's async exception mechanism?
Regarding interruptible operations, this and one more thing that I haven't written there: The fact that some function uses an interruptible operation internally is a visible aspect of its behavior, both in GHC design and in mine. This means that choosing which operations are interruptible should be done carefully: even if some operation blocks the thread, it might be a bad choice for an interruption point, because usage of some blocking operations should better not have to be exposed. In my case such blocking but uninterruptible operations include waiting for a mutex, and waiting for a lazy variable, among others. But Concurrent Haskell uses a single construct of MVars as mutexes, semaphores, or communication channels. The runtime can't recognize the pattern of usage to distinguish these cases. -- __("< Marcin Kowalczyk \__/ qrczak@knm.org.pl ^^ http://qrnik.knm.org.pl/~qrczak/
participants (2)
-
Marcin 'Qrczak' Kowalczyk
-
Simon Marlow