+{-# OPTIONS_GHC -XNoImplicitPrelude #-}
+
-----------------------------------------------------------------------------
---
+-- |
-- Module : Control.Exception
-- Copyright : (c) The University of Glasgow 2001
--- License : BSD-style (see the file libraries/core/LICENSE)
+-- License : BSD-style (see the file libraries/base/LICENSE)
--
-- 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.
--
--- $Id: Exception.hs,v 1.1 2001/06/28 14:15:01 simonmar Exp $
+-- 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:
--
--- The External API for exceptions. The functions provided in this
--- module allow catching of exceptions in the IO monad.
+-- * /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 (
- Exception(..), -- instance Eq, Ord, Show, Typeable
- IOException, -- instance Eq, Ord, Show, Typeable
- ArithException(..), -- instance Eq, Ord, Show, Typeable
- ArrayException(..), -- instance Eq, Ord, Show, Typeable
- AsyncException(..), -- instance Eq, Ord, Show, Typeable
-
- try, -- :: IO a -> IO (Either Exception a)
- tryJust, -- :: (Exception -> Maybe b) -> a -> IO (Either b a)
-
- catch, -- :: IO a -> (Exception -> IO a) -> IO a
- catchJust, -- :: (Exception -> Maybe b) -> IO a -> (b -> IO a) -> IO a
-
- evaluate, -- :: a -> IO a
-
- -- Exception predicates (for catchJust, tryJust)
-
- ioErrors, -- :: Exception -> Maybe IOError
- arithExceptions, -- :: Exception -> Maybe ArithException
- errorCalls, -- :: Exception -> Maybe String
- dynExceptions, -- :: Exception -> Maybe Dynamic
- assertions, -- :: Exception -> Maybe String
- asyncExceptions, -- :: Exception -> Maybe AsyncException
- userErrors, -- :: Exception -> Maybe String
-
- -- Throwing exceptions
-
- throw, -- :: Exception -> a
-#ifndef __STGHUGS__
- -- for now
- throwTo, -- :: ThreadId -> Exception -> a
+ -- * The Exception type
+#ifdef __HUGS__
+ SomeException,
+#else
+ SomeException(..),
#endif
-
- -- Dynamic exceptions
-
- throwDyn, -- :: Typeable ex => ex -> b
- throwDynTo, -- :: Typeable ex => ThreadId -> ex -> b
- catchDyn, -- :: Typeable ex => IO a -> (ex -> IO a) -> IO a
-
- -- Async exception control
-
- block, -- :: IO a -> IO a
- unblock, -- :: IO a -> IO a
-
- -- Assertions
-
- -- for now
- assert, -- :: Bool -> a -> a
-
- -- Utilities
-
- finally, -- :: IO a -> IO b -> IO b
-
- bracket, -- :: IO a -> (a -> IO b) -> (a -> IO c) -> IO ()
- bracket_, -- :: IO a -> IO b -> IO c -> IO ()
-
- ) where
-
-#ifdef __GLASGOW_HASKELL__
-import Prelude hiding (catch)
-import GHC.Prim ( assert )
-import GHC.Exception hiding (try, catch, bracket, bracket_)
-import GHC.Conc ( throwTo, ThreadId )
-import GHC.IOBase ( IO(..) )
+ Exception(..), -- class
+ IOException, -- instance Eq, Ord, Show, Typeable, Exception
+ ArithException(..), -- instance Eq, Ord, Show, Typeable, Exception
+ ArrayException(..), -- instance Eq, Ord, Show, Typeable, Exception
+ AssertionFailed(..),
+ AsyncException(..), -- instance Eq, Ord, Show, Typeable, Exception
+
+#if __GLASGOW_HASKELL__ || __HUGS__
+ NonTermination(..),
+ NestedAtomically(..),
+#endif
+#ifdef __NHC__
+ System.ExitCode(), -- instance Exception
#endif
-#ifdef __HUGS__
-import Prelude hiding ( catch )
-import PrelPrim ( catchException
- , Exception(..)
- , throw
- , ArithException(..)
- , AsyncException(..)
- , assert
- )
+ BlockedOnDeadMVar(..),
+ BlockedIndefinitely(..),
+ Deadlock(..),
+ NoMethodError(..),
+ PatternMatchFail(..),
+ RecConError(..),
+ RecSelError(..),
+ RecUpdError(..),
+ ErrorCall(..),
+
+ -- * Throwing exceptions
+ throwIO, -- :: Exception -> IO a
+ throw, -- :: Exception -> a
+ ioError, -- :: IOError -> IO a
+#ifdef __GLASGOW_HASKELL__
+ throwTo, -- :: ThreadId -> Exception -> a
#endif
-import Data.Dynamic
+ -- * Catching Exceptions
-#include "Dynamic.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")
+ -- |There are several functions for catching and examining
+ -- exceptions; all of them may only be used from within the
+ -- 'IO' monad.
------------------------------------------------------------------------------
--- Catching exceptions
+ -- ** The @catch@ functions
+ catch, -- :: IO a -> (Exception -> IO a) -> IO a
+ catches, Handler(..),
+ catchJust, -- :: (Exception -> Maybe b) -> IO a -> (b -> IO a) -> IO a
--- PrelException defines 'catchException' for us.
+ -- ** The @handle@ functions
+ handle, -- :: (Exception -> IO a) -> IO a -> IO a
+ handleJust,-- :: (Exception -> Maybe b) -> (b -> IO a) -> IO a -> IO a
-catch :: IO a -> (Exception -> IO a) -> IO a
-catch = catchException
+ -- ** The @try@ functions
+ try, -- :: IO a -> IO (Either Exception a)
+ tryJust, -- :: (Exception -> Maybe b) -> a -> IO (Either b a)
+ onException,
-catchJust :: (Exception -> Maybe b) -> IO a -> (b -> IO a) -> IO a
-catchJust p a handler = catch a handler'
- where handler' e = case p e of
- Nothing -> throw e
- Just b -> handler b
+ -- ** The @evaluate@ function
+ evaluate, -- :: a -> IO a
------------------------------------------------------------------------------
--- evaluate
+ -- ** The @mapException@ function
+ mapException, -- :: (Exception -> Exception) -> a -> a
-evaluate :: a -> IO a
-evaluate a = a `seq` return a
+ -- * Asynchronous Exceptions
------------------------------------------------------------------------------
--- 'try' and variations.
+ -- $async
-try :: IO a -> IO (Either Exception a)
-try a = catch (a >>= \ v -> return (Right v)) (\e -> return (Left e))
+ -- ** Asynchronous exception control
-tryJust :: (Exception -> Maybe b) -> IO a -> IO (Either b a)
-tryJust p a = do
- r <- try a
- case r of
- Right v -> return (Right v)
- Left e -> case p e of
- Nothing -> throw e
- Just b -> return (Left b)
+ -- |The following two functions allow a thread to control delivery of
+ -- asynchronous exceptions during a critical region.
------------------------------------------------------------------------------
--- Dynamic exception types. Since one of the possible kinds of exception
--- is a dynamically typed value, we can effectively have polymorphic
--- exceptions.
-
--- throwDyn will raise any value as an exception, provided it is in the
--- Typeable class (see Dynamic.lhs).
-
--- catchDyn will catch any exception of a given type (determined by the
--- handler function). Any raised exceptions that don't match are
--- re-raised.
+ block, -- :: IO a -> IO a
+ unblock, -- :: IO a -> IO a
+ blocked, -- :: IO Bool
-throwDyn :: Typeable exception => exception -> b
-throwDyn exception = throw (DynException (toDyn exception))
+ -- *** Applying @block@ to an exception handler
-throwDynTo :: Typeable exception => ThreadId -> exception -> IO ()
-throwDynTo t exception = throwTo t (DynException (toDyn exception))
+ -- $block_handler
-catchDyn :: Typeable exception => IO a -> (exception -> IO a) -> IO a
-catchDyn m k = catchException m handle
- where handle ex = case ex of
- (DynException dyn) ->
- case fromDynamic dyn of
- Just exception -> k exception
- Nothing -> throw ex
- _ -> throw ex
+ -- *** Interruptible operations
------------------------------------------------------------------------------
--- Exception Predicates
+ -- $interruptible
-ioErrors :: Exception -> Maybe IOError
-arithExceptions :: Exception -> Maybe ArithException
-errorCalls :: Exception -> Maybe String
-dynExceptions :: Exception -> Maybe Dynamic
-assertions :: Exception -> Maybe String
-asyncExceptions :: Exception -> Maybe AsyncException
-userErrors :: Exception -> Maybe String
+ -- * Assertions
-ioErrors e@(IOException _) = Just e
-ioErrors _ = Nothing
+ assert, -- :: Bool -> a -> a
-arithExceptions (ArithException e) = Just e
-arithExceptions _ = Nothing
+ -- * Utilities
-errorCalls (ErrorCall e) = Just e
-errorCalls _ = Nothing
+ bracket, -- :: IO a -> (a -> IO b) -> (a -> IO c) -> IO ()
+ bracket_, -- :: IO a -> IO b -> IO c -> IO ()
+ bracketOnError,
-assertions (AssertionFailed e) = Just e
-assertions _ = Nothing
+ finally, -- :: IO a -> IO b -> IO a
+ ) where
-dynExceptions (DynException e) = Just e
-dynExceptions _ = Nothing
+import Control.Exception.Base
-asyncExceptions (AsyncException e) = Just e
-asyncExceptions _ = Nothing
+#ifdef __GLASGOW_HASKELL__
+import GHC.Base
+import GHC.IOBase
+import Data.Maybe
+#else
+import Prelude hiding (catch)
+#endif
-userErrors (UserError e) = Just e
-userErrors _ = Nothing
+#ifdef __NHC__
+import System (ExitCode())
+#endif
------------------------------------------------------------------------------
--- Some Useful Functions
-
-bracket :: IO a -> (a -> IO b) -> (a -> IO c) -> IO c
-bracket before after thing =
- block (do
- a <- before
- r <- catch
- (unblock (thing a))
- (\e -> do { after a; throw e })
- after a
- return r
- )
-
--- finally is an instance of bracket, but it's quite common
--- so we give the specialised version for efficiency.
-finally :: IO a -> IO b -> IO a
-a `finally` sequel =
- block (do
- r <- catch
- (unblock a)
- (\e -> do { sequel; throw e })
- sequel
- return r
- )
-
-bracket_ :: IO a -> IO b -> IO c -> IO c
-bracket_ before after thing = bracket before (const after) (const thing)
+data Handler a = forall e . Exception e => Handler (e -> IO a)
+
+catches :: IO a -> [Handler a] -> IO a
+catches io handlers = io `catch` catchesHandler handlers
+
+catchesHandler :: [Handler a] -> SomeException -> IO a
+catchesHandler handlers e = foldr tryHandler (throw e) handlers
+ where tryHandler (Handler handler) res
+ = case fromException e of
+ Just e' -> handler e'
+ Nothing -> res
+
+-- -----------------------------------------------------------------------------
+-- Asynchronous exceptions
+
+{- $async
+
+ #AsynchronousExceptions# Asynchronous exceptions are so-called because they arise due to
+external influences, and can be raised at any point during execution.
+'StackOverflow' and 'HeapOverflow' are two examples of
+system-generated asynchronous exceptions.
+
+The primary source of asynchronous exceptions, however, is
+'throwTo':
+
+> throwTo :: ThreadId -> Exception -> IO ()
+
+'throwTo' (also 'throwDynTo' and 'Control.Concurrent.killThread') allows one
+running thread to raise an arbitrary exception in another thread. The
+exception is therefore asynchronous with respect to the target thread,
+which could be doing anything at the time it receives the exception.
+Great care should be taken with asynchronous exceptions; it is all too
+easy to introduce race conditions by the over zealous use of
+'throwTo'.
+-}
+
+{- $block_handler
+There\'s an implied 'block' around every exception handler in a call
+to one of the 'catch' family of functions. This is because that is
+what you want most of the time - it eliminates a common race condition
+in starting an exception handler, because there may be no exception
+handler on the stack to handle another exception if one arrives
+immediately. If asynchronous exceptions are blocked on entering the
+handler, though, we have time to install a new exception handler
+before being interrupted. If this weren\'t the default, one would have
+to write something like
+
+> block (
+> catch (unblock (...))
+> (\e -> handler)
+> )
+
+If you need to unblock asynchronous exceptions again in the exception
+handler, just use 'unblock' as normal.
+
+Note that 'try' and friends /do not/ have a similar default, because
+there is no exception handler in this case. If you want to use 'try'
+in an asynchronous-exception-safe way, you will need to use
+'block'.
+-}
+
+{- $interruptible
+
+Some operations are /interruptible/, which means that they can receive
+asynchronous exceptions even in the scope of a 'block'. Any function
+which may itself block is defined as interruptible; this includes
+'Control.Concurrent.MVar.takeMVar'
+(but not 'Control.Concurrent.MVar.tryTakeMVar'),
+and most operations which perform
+some I\/O with the outside world. The reason for having
+interruptible operations is so that we can write things like
+
+> block (
+> a <- takeMVar m
+> catch (unblock (...))
+> (\e -> ...)
+> )
+
+if the 'Control.Concurrent.MVar.takeMVar' was not interruptible,
+then this particular
+combination could lead to deadlock, because the thread itself would be
+blocked in a state where it can\'t receive any asynchronous exceptions.
+With 'Control.Concurrent.MVar.takeMVar' interruptible, however, we can be
+safe in the knowledge that the thread can receive exceptions right up
+until the point when the 'Control.Concurrent.MVar.takeMVar' succeeds.
+Similar arguments apply for other interruptible operations like
+'System.IO.openFile'.
+-}