X-Git-Url: http://git.megacz.com/?a=blobdiff_plain;f=GHC%2FIOBase.lhs;h=233148b0eaffffa011ccb91673cacb6e5820d0c8;hb=e9e2a5412bb7cda8d13a063ac403d9f18ac97380;hp=7b8fef53b61f805307a079d04e2dcfad85a8caa0;hpb=93fb89b406b738b64d652d17c7fb509344ff5d33;p=ghc-base.git diff --git a/GHC/IOBase.lhs b/GHC/IOBase.lhs index 7b8fef5..233148b 100644 --- a/GHC/IOBase.lhs +++ b/GHC/IOBase.lhs @@ -1,5 +1,5 @@ \begin{code} -{-# OPTIONS -fno-implicit-prelude #-} +{-# OPTIONS_GHC -fno-implicit-prelude #-} ----------------------------------------------------------------------------- -- | -- Module : GHC.IOBase @@ -112,13 +112,22 @@ returnIO x = IO (\ s -> (# s, x #)) -- --------------------------------------------------------------------------- -- Coercions between IO and ST ---stToIO :: (forall s. ST s a) -> IO a +-- | A monad transformer embedding strict state transformers in the 'IO' +-- monad. The 'RealWorld' parameter indicates that the internal state +-- used by the 'ST' computation is a special one supplied by the 'IO' +-- monad, and thus distinct from those used by invocations of 'runST'. stToIO :: ST RealWorld a -> IO a stToIO (ST m) = IO m ioToST :: IO a -> ST RealWorld a ioToST (IO m) = (ST m) +-- This relies on IO and ST having the same representation modulo the +-- constraint on the type of the state +-- +unsafeIOToST :: IO a -> ST s a +unsafeIOToST (IO io) = ST $ \ s -> (unsafeCoerce# io) s + -- --------------------------------------------------------------------------- -- Unsafe IO operations @@ -164,7 +173,7 @@ It is less well known that > > main = do > writeIORef test [42] -> bang \<- readIORef test +> bang <- readIORef test > print (bang :: [Char]) This program will core dump. This problem with polymorphic references @@ -178,13 +187,19 @@ help of 'unsafePerformIO'. So be careful! unsafePerformIO :: IO a -> a unsafePerformIO (IO m) = case m realWorld# of (# _, r #) -> r +-- Why do we NOINLINE unsafePerformIO? See the comment with +-- GHC.ST.runST. Essentially the issue is that the IO computation +-- inside unsafePerformIO must be atomic: it must either all run, or +-- not at all. If we let the compiler see the application of the IO +-- to realWorld#, it might float out part of the IO. + {-| 'unsafeInterleaveIO' allows 'IO' computation to be deferred lazily. When passed a value of type @IO a@, the 'IO' will only be performed when the value of the @a@ is demanded. This is used to implement lazy file reading, see 'System.IO.hGetContents'. -} -{-# NOINLINE unsafeInterleaveIO #-} +{-# INLINE unsafeInterleaveIO #-} unsafeInterleaveIO :: IO a -> IO a unsafeInterleaveIO (IO m) = IO ( \ s -> let @@ -192,6 +207,10 @@ unsafeInterleaveIO (IO m) in (# s, r #)) +-- We believe that INLINE on unsafeInterleaveIO is safe, because the +-- state from this IO thread is passed explicitly to the interleaved +-- IO, so it cannot be floated out and shared. + -- --------------------------------------------------------------------------- -- Handle type @@ -220,11 +239,51 @@ instance Eq (MVar a) where -- Note: when a Handle is garbage collected, we want to flush its buffer -- and close the OS file handle, so as to free up a (precious) resource. +-- | Haskell defines operations to read and write characters from and to files, +-- represented by values of type @Handle@. Each value of this type is a +-- /handle/: a record used by the Haskell run-time system to /manage/ I\/O +-- with file system objects. A handle has at least the following properties: +-- +-- * whether it manages input or output or both; +-- +-- * whether it is /open/, /closed/ or /semi-closed/; +-- +-- * whether the object is seekable; +-- +-- * whether buffering is disabled, or enabled on a line or block basis; +-- +-- * a buffer (whose length may be zero). +-- +-- Most handles will also have a current I\/O position indicating where the next +-- input or output operation will occur. A handle is /readable/ if it +-- manages only input or both input and output; likewise, it is /writable/ if +-- it manages only output or both input and output. A handle is /open/ when +-- first allocated. +-- Once it is closed it can no longer be used for either input or output, +-- though an implementation cannot re-use its storage while references +-- remain to it. Handles are in the 'Show' and 'Eq' classes. The string +-- produced by showing a handle is system dependent; it should include +-- enough information to identify the handle for debugging. A handle is +-- equal according to '==' only to itself; no attempt +-- is made to compare the internal state of different handles for equality. +-- +-- GHC note: a 'Handle' will be automatically closed when the garbage +-- collector detects that it has become unreferenced by the program. +-- However, relying on this behaviour is not generally recommended: +-- the garbage collector is unpredictable. If possible, use explicit +-- an explicit 'hClose' to close 'Handle's when they are no longer +-- required. GHC does not currently attempt to free up file +-- descriptors when they have run out, it is your responsibility to +-- ensure that this doesn't happen. + data Handle = FileHandle -- A normal handle to a file + FilePath -- the file (invariant) !(MVar Handle__) | DuplexHandle -- A handle to a read/write stream + FilePath -- file for a FIFO, otherwise some + -- descriptive string. !(MVar Handle__) -- The read side !(MVar Handle__) -- The write side @@ -233,8 +292,8 @@ data Handle -- seekable. instance Eq Handle where - (FileHandle h1) == (FileHandle h2) = h1 == h2 - (DuplexHandle h1 _) == (DuplexHandle h2 _) = h1 == h2 + (FileHandle _ h1) == (FileHandle _ h2) = h1 == h2 + (DuplexHandle _ h1 _) == (DuplexHandle _ h2 _) = h1 == h2 _ == _ = False type FD = Int -- XXX ToDo: should be CInt @@ -246,7 +305,6 @@ data Handle__ haIsBin :: Bool, -- binary mode? haIsStream :: Bool, -- is this a stream handle? haBufferMode :: BufferMode, -- buffer contains read/write data? - haFilePath :: FilePath, -- file name, possibly haBuffer :: !(IORef Buffer), -- the current buffer haBuffers :: !(IORef BufferList), -- spare buffers haOtherSide :: Maybe (MVar Handle__) -- ptr to the write side of a @@ -338,53 +396,63 @@ isWritableHandleType WriteHandle = True isWritableHandleType ReadWriteHandle = True isWritableHandleType _ = False --- File names are specified using @FilePath@, a OS-dependent --- string that (hopefully, I guess) maps to an accessible file/object. +-- | File and directory names are values of type 'String', whose precise +-- meaning is operating system dependent. Files can be opened, yielding a +-- handle which can then be used to operate on the contents of that file. type FilePath = String -- --------------------------------------------------------------------------- -- Buffering modes --- Three kinds of buffering are supported: line-buffering, +-- | Three kinds of buffering are supported: line-buffering, -- block-buffering or no-buffering. These modes have the following --- effects. For output, items are written out from the internal --- buffer according to the buffer mode: +-- effects. For output, items are written out, or /flushed/, +-- from the internal buffer according to the buffer mode: -- --- o line-buffering the entire output buffer is written --- out whenever a newline is output, the output buffer overflows, --- a flush is issued, or the handle is closed. +-- * /line-buffering/: the entire output buffer is flushed +-- whenever a newline is output, the buffer overflows, +-- a 'System.IO.hFlush' is issued, or the handle is closed. -- --- o block-buffering the entire output buffer is written out whenever --- it overflows, a flush is issued, or the handle --- is closed. +-- * /block-buffering/: the entire buffer is written out whenever it +-- overflows, a 'System.IO.hFlush' is issued, or the handle is closed. -- --- o no-buffering output is written immediately, and never stored --- in the output buffer. +-- * /no-buffering/: output is written immediately, and never stored +-- in the buffer. -- +-- An implementation is free to flush the buffer more frequently, +-- but not less frequently, than specified above. -- The output buffer is emptied as soon as it has been written out. - --- Similarly, input occurs according to the buffer mode for handle {\em hdl}. - --- o line-buffering when the input buffer for the handle is not empty, --- the next item is obtained from the buffer; --- otherwise, when the input buffer is empty, --- characters up to and including the next newline --- character are read into the buffer. No characters --- are available until the newline character is --- available. -- --- o block-buffering when the input buffer for the handle becomes empty, --- the next block of data is read into this buffer. +-- Similarly, input occurs according to the buffer mode for the handle: -- --- o no-buffering the next input item is read and returned. - +-- * /line-buffering/: when the buffer for the handle is not empty, +-- the next item is obtained from the buffer; otherwise, when the +-- buffer is empty, characters up to and including the next newline +-- character are read into the buffer. No characters are available +-- until the newline character is available or the buffer is full. +-- +-- * /block-buffering/: when the buffer for the handle becomes empty, +-- the next block of data is read into the buffer. +-- +-- * /no-buffering/: the next input item is read and returned. +-- The 'System.IO.hLookAhead' operation implies that even a no-buffered +-- handle may require a one-character buffer. +-- +-- The default buffering mode when a handle is opened is +-- implementation-dependent and may depend on the file system object +-- which is attached to that handle. -- For most implementations, physical files will normally be block-buffered --- and terminals will normally be line-buffered. (the IO interface provides --- operations for changing the default buffering of a handle tho.) +-- and terminals will normally be line-buffered. data BufferMode - = NoBuffering | LineBuffering | BlockBuffering (Maybe Int) + = NoBuffering -- ^ buffering is disabled if possible. + | LineBuffering + -- ^ line-buffering should be enabled if possible. + | BlockBuffering (Maybe Int) + -- ^ block-buffering should be enabled if possible. + -- The size of the buffer is @n@ items if the argument + -- is 'Just' @n@ and is otherwise implementation-dependent. deriving (Eq, Ord, Read, Show) -- --------------------------------------------------------------------------- @@ -467,46 +535,10 @@ instance Show HandleType where ReadWriteHandle -> showString "read-writable" instance Show Handle where - showsPrec p (FileHandle h) = showHandle p h False - showsPrec p (DuplexHandle _ h) = showHandle p h True - -showHandle p h duplex = - let - -- (Big) SIGH: unfolded defn of takeMVar to avoid - -- an (oh-so) unfortunate module loop with GHC.Conc. - hdl_ = unsafePerformIO (IO $ \ s# -> - case h of { MVar h# -> - case takeMVar# h# s# of { (# s2# , r #) -> - case putMVar# h# r s2# of { s3# -> - (# s3#, r #) }}}) - - showType | duplex = showString "duplex (read-write)" - | otherwise = showsPrec p (haType hdl_) - in - showChar '{' . - showHdl (haType hdl_) - (showString "loc=" . showString (haFilePath hdl_) . showChar ',' . - showString "type=" . showType . showChar ',' . - showString "binary=" . showsPrec p (haIsBin hdl_) . showChar ',' . - showString "buffering=" . showBufMode (unsafePerformIO (readIORef (haBuffer hdl_))) (haBufferMode hdl_) . showString "}" ) - where - - showHdl :: HandleType -> ShowS -> ShowS - showHdl ht cont = - case ht of - ClosedHandle -> showsPrec p ht . showString "}" - _ -> cont - - showBufMode :: Buffer -> BufferMode -> ShowS - showBufMode buf bmo = - case bmo of - NoBuffering -> showString "none" - LineBuffering -> showString "line" - BlockBuffering (Just n) -> showString "block " . showParen True (showsPrec p n) - BlockBuffering Nothing -> showString "block " . showParen True (showsPrec p def) - where - def :: Int - def = bufSize buf + showsPrec p (FileHandle file _) = showHandle file + showsPrec p (DuplexHandle file _ _) = showHandle file + +showHandle file = showString "{handle: " . showString file . showString "}" -- ------------------------------------------------------------------------ -- Exception datatype and operations @@ -514,7 +546,8 @@ showHandle p h duplex = -- |The type of exceptions. Every kind of system-generated exception -- has a constructor in the 'Exception' type, and values of other -- types may be injected into 'Exception' by coercing them to --- 'Dynamic' (see the section on Dynamic Exceptions: "Control.Exception\#DynamicExceptions"). +-- 'Data.Dynamic.Dynamic' (see the section on Dynamic Exceptions: +-- "Control.Exception\#DynamicExceptions"). data Exception = ArithException ArithException -- ^Exceptions raised by arithmetic @@ -533,8 +566,12 @@ data Exception -- ^Asynchronous exceptions (see section on Asynchronous Exceptions: "Control.Exception\#AsynchronousExceptions"). | BlockedOnDeadMVar -- ^The current thread was executing a call to - -- 'takeMVar' that could never return, because there are no other - -- references to this 'MVar'. + -- 'Control.Concurrent.MVar.takeMVar' that could never return, + -- because there are no other references to this 'MVar'. + | BlockedIndefinitely + -- ^The current thread was waiting to retry an atomic memory transaction + -- that could never become possible to complete because there are no other + -- threads referring to any of teh TVars involved. | Deadlock -- ^There are no runnable threads, so the program is -- deadlocked. The 'Deadlock' exception is @@ -546,9 +583,9 @@ data Exception -- argument of 'ErrorCall' is the string passed to 'error' when it was -- called. | ExitException ExitCode - -- ^The 'ExitException' exception is thrown by 'System.exitWith' (and - -- 'System.exitFailure'). The 'ExitCode' argument is the value passed - -- to 'System.exitWith'. An unhandled 'ExitException' exception in the + -- ^The 'ExitException' exception is thrown by 'System.Exit.exitWith' (and + -- 'System.Exit.exitFailure'). The 'ExitCode' argument is the value passed + -- to 'System.Exit.exitWith'. An unhandled 'ExitException' exception in the -- main thread will cause the program to be terminated with the given -- exit code. | IOException IOException @@ -616,7 +653,7 @@ data AsyncException -- * GHC currently does not throw 'HeapOverflow' exceptions. | ThreadKilled -- ^This exception is raised by another thread - -- calling 'killThread', or by the system + -- calling 'Control.Concurrent.killThread', or by the system -- if it needs to terminate the thread for some -- reason. deriving (Eq, Ord) @@ -672,6 +709,7 @@ instance Show Exception where showsPrec _ (DynException _err) = showString "unknown exception" showsPrec _ (AsyncException e) = shows e showsPrec _ (BlockedOnDeadMVar) = showString "thread blocked indefinitely" + showsPrec _ (BlockedIndefinitely) = showString "thread blocked indefinitely" showsPrec _ (NonTermination) = showString "<>" showsPrec _ (Deadlock) = showString "<>" @@ -697,18 +735,17 @@ instance Eq Exception where -- ----------------------------------------------------------------------------- -- The ExitCode type --- The `ExitCode' type defines the exit codes that a program --- can return. `ExitSuccess' indicates successful termination; --- and `ExitFailure code' indicates program failure --- with value `code'. The exact interpretation of `code' --- is operating-system dependent. In particular, some values of --- `code' may be prohibited (e.g. 0 on a POSIX-compliant system). - -- We need it here because it is used in ExitException in the -- Exception datatype (above). -data ExitCode = ExitSuccess | ExitFailure Int - deriving (Eq, Ord, Read, Show) +data ExitCode + = ExitSuccess -- ^ indicates successful termination; + | ExitFailure Int + -- ^ indicates program failure with an exit code. + -- The exact interpretation of the code is + -- operating-system dependent. In particular, some values + -- may be prohibited (e.g. 0 on a POSIX-compliant system). + deriving (Eq, Ord, Read, Show) -- -------------------------------------------------------------------------- -- Primitive throw @@ -739,13 +776,18 @@ throwIO err = IO $ raiseIO# err ioException :: IOException -> IO a ioException err = IO $ raiseIO# (IOException err) +-- | Raise an 'IOError' in the 'IO' monad. ioError :: IOError -> IO a ioError = ioException -- --------------------------------------------------------------------------- -- IOError type --- | The Haskell 98 type for exceptions in the @IO@ monad. +-- | The Haskell 98 type for exceptions in the 'IO' monad. +-- Any I\/O operation may raise an 'IOError' instead of returning a result. +-- For a more general type of exception, including also those that arise +-- in pure code, see 'Control.Exception.Exception'. +-- -- In Haskell 98, this is an opaque type. type IOError = IOException @@ -767,6 +809,7 @@ instance Eq IOException where (IOError h1 e1 loc1 str1 fn1) == (IOError h2 e2 loc2 str2 fn2) = e1==e2 && str1==str2 && h1==h2 && loc1==loc2 && fn1==fn2 +-- | An abstract type that contains a value for each variant of 'IOError'. data IOErrorType -- Haskell 98: = AlreadyExists @@ -822,6 +865,14 @@ instance Show IOErrorType where UnsupportedOperation -> "unsupported operation" DynIOError{} -> "unknown IO error" +-- | Construct an 'IOError' value with a string describing the error. +-- The 'fail' method of the 'IO' instance of the 'Monad' class raises a +-- 'userError', thus: +-- +-- > instance Monad IO where +-- > ... +-- > fail s = ioError (userError s) +-- userError :: String -> IOError userError str = IOError Nothing UserError "" str Nothing @@ -830,19 +881,18 @@ userError str = IOError Nothing UserError "" str Nothing instance Show IOException where showsPrec p (IOError hdl iot loc s fn) = - showsPrec p iot . + (case fn of + Nothing -> case hdl of + Nothing -> id + Just h -> showsPrec p h . showString ": " + Just name -> showString name . showString ": ") . (case loc of "" -> id - _ -> showString "\nAction: " . showString loc) . - (case hdl of - Nothing -> id - Just h -> showString "\nHandle: " . showsPrec p h) . + _ -> showString loc . showString ": ") . + showsPrec p iot . (case s of "" -> id - _ -> showString "\nReason: " . showString s) . - (case fn of - Nothing -> id - Just name -> showString "\nFile: " . showString name) + _ -> showString " (" . showString s . showString ")") -- ----------------------------------------------------------------------------- -- IOMode type