Faster timeout but is it correct?

Bas van Dijk v.dijk.bas at gmail.com
Wed Feb 16 15:03:14 CET 2011


I made a slight modification and now it runs 16 times faster than the original:

timeout :: Int -> IO a -> IO (Maybe a)
timeout n f
    | n <  0    = fmap Just f
    | n == 0    = return Nothing
    | otherwise = do
        myTid <- myThreadId
        timeoutEx  <- fmap Timeout newUnique
        uninterruptibleMask $ \restore -> do
          tid <- restore $ forkIO $ threadDelay n >> throwTo myTid timeoutEx
          (restore (fmap Just f) >>= \mb -> killThread tid >> return mb)
            `catch` \e ->
                case fromException e of
                  Just timeoutEx' | timeoutEx' == timeoutEx -> return Nothing
                  _ -> killThread tid >> throwIO e

However I may have noticed a deadlock in the previous version (maybe
this version has it also). The deadlock occurred when running the
externalException benchmark:

externalException = do
  (tid, wait) <- fork $ timeout oneSec (threadDelay oneSec)
  threadDelay 500
  throwTo tid MyException
  r <- wait
  case r of
    Left e | Just MyException <- fromException e -> return ()
    _ -> error "MyException should have been thrown!"

data MyException = MyException deriving (Show, Typeable)
instance Exception MyException

-- Fork a thread and return a computation that waits for its result.
-- Equivalent to forkIO from the threads package.
fork :: IO a -> IO (ThreadId, IO (Either SomeException a))
fork a = do
  res <- newEmptyMVar
  tid <- mask $ \restore -> forkIO $ try (restore a) >>= putMVar res
  return (tid, readMVar res)

So please review this carefully.

Bas



More information about the Libraries mailing list