--
-- Maintainer : libraries@haskell.org
-- Stability : experimental
--- Portability : non-portable
+-- Portability : non-portable (extended exceptions)
--
-- This module provides support for raising and catching both built-in
-- and user-defined exceptions.
--
+-- In addition to exceptions thrown by 'IO' operations, exceptions may
+-- be thrown by pure code (imprecise exceptions) or by external events
+-- (asynchronous exceptions), but may only be caught in the 'IO' monad.
+-- For more details, see:
+--
+-- * /A semantics for imprecise exceptions/, by Simon Peyton Jones,
+-- Alastair Reid, Tony Hoare, Simon Marlow, Fergus Henderson,
+-- in /PLDI'99/.
+--
+-- * /Asynchronous exceptions in Haskell/, by Simon Marlow, Simon Peyton
+-- Jones, Andy Moran and John Reppy, in /PLDI'01/.
+--
-----------------------------------------------------------------------------
module Control.Exception (
bracket, -- :: IO a -> (a -> IO b) -> (a -> IO c) -> IO ()
bracket_, -- :: IO a -> IO b -> IO c -> IO ()
+ bracketOnError,
finally, -- :: IO a -> IO b -> IO a
#ifdef __GLASGOW_HASKELL__
- setUncatchedExceptionHandler, -- :: (Exception -> IO ()) -> IO ()
- getUncatchedExceptionHandler -- :: IO (Exception -> IO ())
+ setUncaughtExceptionHandler, -- :: (Exception -> IO ()) -> IO ()
+ getUncaughtExceptionHandler -- :: IO (Exception -> IO ())
#endif
) where
import GHC.Base ( assert )
import GHC.Exception as ExceptionBase hiding (catch)
import GHC.Conc ( throwTo, ThreadId )
-import GHC.IOBase ( IO(..), IORef(..), newIORef, readIORef, writeIORef )
-import GHC.Handle ( stdout, hFlush )
+import Data.IORef ( IORef, newIORef, readIORef, writeIORef )
+import Foreign.C.String ( CString, withCString )
+import System.IO ( stdout, hFlush )
#endif
#ifdef __HUGS__
import Hugs.Exception as ExceptionBase
#endif
-import Foreign.C.String ( CString, withCStringLen )
-
import Prelude hiding ( catch )
import System.IO.Error hiding ( catch, try )
import System.IO.Unsafe (unsafePerformIO)
import Data.Dynamic
-#include "Typeable.h"
-INSTANCE_TYPEABLE0(Exception,exceptionTc,"Exception")
-INSTANCE_TYPEABLE0(IOException,ioExceptionTc,"IOException")
-INSTANCE_TYPEABLE0(ArithException,arithExceptionTc,"ArithException")
-INSTANCE_TYPEABLE0(ArrayException,arrayExceptionTc,"ArrayException")
-INSTANCE_TYPEABLE0(AsyncException,asyncExceptionTc,"AsyncException")
-
-----------------------------------------------------------------------------
-- Catching exceptions
-- argument. Otherwise, the result is returned as normal. For example:
--
-- > catch (openFile f ReadMode)
--- > (\e -> hPutStr stderr (\"Couldn\'t open \"++f++\": \" ++ show e))
+-- > (\e -> hPutStr stderr ("Couldn't open "++f++": " ++ show e))
--
-- For catching exceptions in pure (non-'IO') expressions, see the
-- function 'evaluate'.
-- might be a 'ThreadKilled', for example). In this case it is usually better
-- to use 'catchJust' and select the kinds of exceptions to catch.
--
--- Also note that The "Prelude" also exports a
--- function called 'catch' which has the same type as
--- 'Control.Exception.catch', the difference being that the
--- "Prelude" version only catches the IO and user
--- families of exceptions (as required by Haskell 98). We recommend
--- either hiding the "Prelude" version of
--- 'catch' when importing
--- "Control.Exception", or importing
--- "Control.Exception" qualified, to avoid name-clashes.
+-- Also note that the "Prelude" also exports a function called
+-- 'Prelude.catch' with a similar type to 'Control.Exception.catch',
+-- except that the "Prelude" version only catches the IO and user
+-- families of exceptions (as required by Haskell 98).
+--
+-- We recommend either hiding the "Prelude" version of 'Prelude.catch'
+-- when importing "Control.Exception":
+--
+-- > import Prelude hiding (catch)
+--
+-- or importing "Control.Exception" qualified, to avoid name-clashes:
+--
+-- > import qualified Control.Exception as C
+--
+-- and then using @C.catch@
+--
catch :: IO a -- ^ The computation to run
-> (Exception -> IO a) -- ^ Handler to invoke if an exception is raised
handleJust p = flip (catchJust p)
-----------------------------------------------------------------------------
--- evaluate
-
--- | Forces its argument to be evaluated, and returns the result in
--- the 'IO' monad. It can be used to order evaluation with respect to
--- other 'IO' operations; its semantics are given by
---
--- > evaluate undefined `seq` return () ==> return ()
--- > catch (evaluate undefined) (\e -> return ()) ==> return ()
---
--- NOTE: @(evaluate a)@ is /not/ the same as @(a \`seq\` return a)@.
-#ifdef __GLASGOW_HASKELL__
-evaluate :: a -> IO a
-evaluate a = IO $ \s -> case a `seq` () of () -> (# s, a #)
- -- NB. can't write
- -- a `seq` (# s, a #)
- -- because we can't have an unboxed tuple as a function argument
-#endif
-
------------------------------------------------------------------------------
-- 'mapException'
-- | This function maps one exception into another as proposed in the
-- 'try' and variations.
-- | Similar to 'catch', but returns an 'Either' result which is
--- @(Right a)@ if no exception was raised, or @(Left e)@ if an
+-- @('Right' a)@ if no exception was raised, or @('Left' e)@ if an
-- exception was raised and its value is @e@.
--
--- > try a = catch (Right \`liftM\` a) (return . Left)
+-- > try a = catch (Right `liftM` a) (return . Left)
--
-- Note: as with 'catch', it is only polite to use this variant if you intend
-- to re-throw the exception after performing whatever cleanup is needed.
-- Otherwise, 'tryJust' is generally considered to be better.
--
+-- Also note that "System.IO.Error" also exports a function called
+-- 'System.IO.Error.try' with a similar type to 'Control.Exception.try',
+-- except that it catches only the IO and user families of exceptions
+-- (as required by the Haskell 98 @IO@ module).
+
try :: IO a -> IO (Either Exception a)
try a = catch (a >>= \ v -> return (Right v)) (\e -> return (Left e))
-- The arguments to 'bracket' are in this order so that we can partially apply
-- it, e.g.:
--
--- > withFile name = bracket (openFile name) hClose
+-- > withFile name mode = bracket (openFile name mode) hClose
--
bracket
:: IO a -- ^ computation to run first (\"acquire resource\")
bracket_ :: IO a -> IO b -> IO c -> IO c
bracket_ before after thing = bracket before (const after) (const thing)
+-- | Like bracket, but only performs the final action if there was an
+-- exception raised by the in-between computation.
+bracketOnError
+ :: IO a -- ^ computation to run first (\"acquire resource\")
+ -> (a -> IO b) -- ^ computation to run last (\"release resource\")
+ -> (a -> IO c) -- ^ computation to run in-between
+ -> IO c -- returns the value from the in-between computation
+bracketOnError before after thing =
+ block (do
+ a <- before
+ catch
+ (unblock (thing a))
+ (\e -> do { after a; throw e })
+ )
+
-- -----------------------------------------------------------------------------
-- Asynchronous exceptions
'System.IO.openFile'.
-}
--- -----------------------------------------------------------------------------
--- Assert
-
-#ifdef __HADDOCK__
--- | If the first argument evaluates to 'True', then the result is the
--- second argument. Otherwise an 'AssertionFailed' exception is raised,
--- containing a 'String' with the source file and line number of the
--- call to assert.
---
--- Assertions can normally be turned on or off with a compiler flag
--- (for GHC, assertions are normally on unless the @-fignore-asserts@
--- option is give). When assertions are turned off, the first
--- argument to 'assert' is ignored, and the second argument is
--- returned as the result.
-assert :: Bool -> a -> a
-#endif
-
#ifndef __GLASGOW_HASKELL__
assert :: Bool -> a -> a
assert True x = x
#ifdef __GLASGOW_HASKELL__
-{-# NOINLINE uncatchedExceptionHandler #-}
-uncatchedExceptionHandler :: IORef (Exception -> IO ())
-uncatchedExceptionHandler = unsafePerformIO (newIORef defaultHandler)
+{-# NOINLINE uncaughtExceptionHandler #-}
+uncaughtExceptionHandler :: IORef (Exception -> IO ())
+uncaughtExceptionHandler = unsafePerformIO (newIORef defaultHandler)
where
defaultHandler :: Exception -> IO ()
defaultHandler ex = do
Deadlock -> "no threads to run: infinite loop or deadlock?"
ErrorCall s -> s
other -> showsPrec 0 other "\n"
- withCStringLen ("Fail: "++msg) $ \(cstr,len) -> writeErrString cstr len
-
-foreign import ccall unsafe "writeErrString__"
- writeErrString :: CString -> Int -> IO ()
+ withCString "%s" $ \cfmt ->
+ withCString msg $ \cmsg ->
+ errorBelch cfmt cmsg
-setUncatchedExceptionHandler :: (Exception -> IO ()) -> IO ()
-setUncatchedExceptionHandler = writeIORef uncatchedExceptionHandler
+foreign import ccall unsafe "RtsMessages.h errorBelch"
+ errorBelch :: CString -> CString -> IO ()
-getUncatchedExceptionHandler :: IO (Exception -> IO ())
-getUncatchedExceptionHandler = readIORef uncatchedExceptionHandler
-#endif
\ No newline at end of file
+setUncaughtExceptionHandler :: (Exception -> IO ()) -> IO ()
+setUncaughtExceptionHandler = writeIORef uncaughtExceptionHandler
+
+getUncaughtExceptionHandler :: IO (Exception -> IO ())
+getUncaughtExceptionHandler = readIORef uncaughtExceptionHandler
+#endif