[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
differently.

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
>  where
>   -- 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
> <<<
>
> regards,
>
> Bertram
>



-- 
Svein Ove Aas


More information about the Reactive mailing list