[reactive] Bug fixes in progress
Svein Ove Aas
svein.ove at aas.no
Wed Jun 3 10:54:42 EDT 2009
I can agree to this phrasing, but I don't think it's perfect yet.
Specifically, it doesn't explicitly explain how unsafePerformIO code
is paused; it only mentions pausing in respect to pure code, while it
is arguably reasonable to expect unsafePerformIO to behave
Fix that, and I'd sign off on the patch if I were in charge. :)
On Wed, Jun 3, 2009 at 1:34 AM, Bertram Felgenhauer <int-e at gmx.de> wrote:
> Conal Elliott wrote:
>> I've pushed Svein's patch, plus some small tweaks of my own, to the darcs
>> repo at http://code.haskell.org/unamb . Comments, please. I have a pretty
>> tenuous grip on this block/unblock/retry stuff.
> I think the code is fine.
> | -- Exception handling in unsafePerformIO does not happen like you're
> | -- used to in normal code. Specifically:
> This is not really true. Exception handling is the same whether
> unsafePerformIO is involved or not. The real point, I think, is that
> normal IO code will never be executed more than once with the same
> RealWorld# token, and never be called from pure code, so that you do
> not have to think about the difference between asynchronous and
> synchronous exceptions at all.
> I've tried to rephrase the comment below. (I can submit a patch if
> we agree that this is an improvement.)
> restartingUnsafePerformIO :: IO a -> a
> restartingUnsafePerformIO = unsafePerformIO . retry
> -- We rely heavily on implementation details of GHC's exception handling
> -- in the code below. Specificially:
> -- * If a thread catches an asynchronous exception, the stack is unwound
> -- until the first exception handler is found. All pending updates
> -- are turned into thunks that recreate the stack and continue the
> -- computation where it is aborted. This is done so that pure
> -- computations can be interrupted and later resumed when the
> -- corresponding value is demanded another time. In other words,
> -- the currently running computation is paused. This also works
> -- for unsafePerformIO, and that's what we exploit below.
> -- * If a thread throws a normal (synchronous) exception, e.g. throw,
> -- throwIO, error (including pattern match failures), etc., then
> -- all pending updates will instead be turned into thunks that
> -- re-throw the same exception again. This is done for performance
> -- reasons, but it would make our unsafePerformIO unrestartable.
> -- So we have to avoid using throw or throwIO below.
> -- To make a whole IO action restartable, we catch any exceptions
> -- thrown by it (which typically indicate that the thread was killed
> -- in the case of 'unamb'), and use an asynchronous exception to set
> -- up a paused computation that runs the IO action again.
> -- Incidentally, all exception handlers run inside an implicit block, and
> -- blocking operations contain an implicit unblock. This ensures that any
> -- further pending exceptions won't mess this scheme up, as they can't be
> -- delivered until after throwTo has been called.
> retry :: IO a -> IO a
> retry act =
> act `catch` \ (SomeException e) -> do
> myThreadId >>= flip throwTo e
> unblock $ retry act
Svein Ove Aas
More information about the Reactive