module Main where

import Control.Concurrent
import qualified Control.Exception as E

trapHandler :: MVar Int -> MVar () -> IO ()
trapHandler inVar caughtVar =
  (do E.mask_ $ do
          trapMsg <- takeMVar inVar
          putStrLn ("Handler got: " ++ show trapMsg)
      trapHandler inVar caughtVar
  )
  `E.catch`
  (trapExc inVar caughtVar)

trapExc :: MVar Int -> MVar () -> E.SomeException -> IO ()
-- If we have been killed then we are done
trapExc inVar caughtVar  e
  | Just E.ThreadKilled <- E.fromException e  = return ()
-- Otherwise...
trapExc inVar caughtVar e =
  do putStrLn ("Exception: " ++ show e)
     putMVar caughtVar ()
     trapHandler inVar caughtVar

main :: IO ()
main = do
  inVar <- newEmptyMVar
  caughtVar <- newEmptyMVar
  tid <- forkIO (trapHandler inVar caughtVar)
  yield
  putMVar inVar 1
  threadDelay 1000
  throwTo tid (E.ErrorCall "1st")
  takeMVar caughtVar
  putMVar inVar 2
  threadDelay 1000
  throwTo tid (E.ErrorCall "2nd")
        -- the second time around, exceptions will be blocked, because
        -- the trapHandler is effectively "still in the handler" from the
        -- first exception.  I'm not sure if this is by design or by
        -- accident.  Anyway, the trapHandler will at some point block
        -- in takeMVar, and thereby become interruptible, at which point
        -- it will receive the second exception.
  takeMVar caughtVar
  -- Running the GHCi way complains that tid is blocked indefinitely if
  -- it still exists, so kill it.
  killThread tid
  putStrLn "All done"
