Skip to content

re-throwing an asynchronous exception throws it synchronously

If you catch and re-throw an asynchronous exception, the second time it will be thrown synchronously. This only makes a difference when using unsafePerformIO or unsafeInterleaveIO, because otherwise the catch will not be inside any thunks.

This could have the unfortunate effect of updating an unsafePerformIO thunk with Timeout, or KillThread, or some other asychronous exception that should not be recorded as the value of the thunk.

We believe that exceptions thrown by an exception handler should be thrown in the same mode (synchronous or asynchronous) as the original exception. This means that we need to

  • keep track of the current exception-throwing mode

  • push a frame when in an exception handler (we already do this, because the

    exception handler is implicitly wrapped in block)

  • respect this mode when throwing exceptions.

This raises another issue: catching an exception and recovering by tail-calling out of the exception handler is not a good thing to do. Currently it has the undesirable effect that the thread remains in block mode, and an explicit unblock is required. With the changes above, the thread may also remain in "asynchronous exception" mode. The right way to solve this is just "don't do that". Here's how to use the exception API:

  • For cleanup, use finally, bracket, and onException.
  • To modify the exception, use mapException.
  • For recovery, use try.

Perhaps we should be deprecating catch? Or perhaps catch should be implemented in terms of try, so it doesn't have the implicit block / re-throw async exceptions behaviour?

Trac metadata
Trac field Value
Version 6.8.3
Type Bug
TypeOfFailure OtherFailure
Priority normal
Resolution Unresolved
Component Compiler
Test case
Differential revisions
BlockedBy
Related
Blocking
CC
Operating system Unknown
Architecture Unknown
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information