[project @ 2002-05-10 13:17:27 by simonmar]
authorsimonmar <unknown>
Fri, 10 May 2002 13:17:29 +0000 (13:17 +0000)
committersimonmar <unknown>
Fri, 10 May 2002 13:17:29 +0000 (13:17 +0000)
- Add documentation to Control.Concurrent and friends
- Other documentation tweaks

13 files changed:
Control/Concurrent.hs
Control/Concurrent/CVar.hs [deleted file]
Control/Concurrent/Chan.hs
Control/Concurrent/MVar.hs
Control/Concurrent/QSem.hs
Control/Concurrent/QSemN.hs
Control/Concurrent/SampleVar.hs
Data/Array/Base.hs
GHC/Base.lhs
GHC/Conc.lhs
GHC/IOBase.lhs
Text/ParserCombinators/ReadP.hs
Text/ParserCombinators/ReadPrec.hs

index 673bac3..5484fc1 100644 (file)
@@ -6,7 +6,7 @@
 -- 
 -- Maintainer  :  libraries@haskell.org
 -- Stability   :  experimental
--- Portability :  non-portable
+-- Portability :  non-portable (concurrency)
 --
 -- A common interface to a collection of useful concurrency
 -- abstractions.
 -----------------------------------------------------------------------------
 
 module Control.Concurrent (
-       module Control.Concurrent.Chan,
-       module Control.Concurrent.CVar,
-       module Control.Concurrent.MVar,
-       module Control.Concurrent.QSem,
-       module Control.Concurrent.QSemN,
-       module Control.Concurrent.SampleVar,
+       -- * Concurrent Haskell
 
-       forkIO,                 -- :: IO () -> IO ()
-       yield,                  -- :: IO ()
+       -- $conc_intro
+
+       -- * Basic concurrency operations
 
-#ifdef __GLASGOW_HASKELL__
         ThreadId,
+       myThreadId,
+
+       forkIO,
+       killThread,
+       throwTo,
+
+       -- * Scheduling
 
-       -- Forking and suchlike
-       myThreadId,             -- :: IO ThreadId
-       killThread,             -- :: ThreadId -> IO ()
-       throwTo,                -- :: ThreadId -> Exception -> IO ()
+       -- $conc_scheduling     
+       yield,                  -- :: IO ()
+
+       -- ** Blocking
+       
+       -- $blocking
 
+#ifdef __GLASGOW_HASKELL__
+       -- ** Waiting
        threadDelay,            -- :: Int -> IO ()
        threadWaitRead,         -- :: Int -> IO ()
        threadWaitWrite,        -- :: Int -> IO ()
 #endif
 
-        -- merging of streams
+       -- * Communication abstractions
+
+       module Control.Concurrent.MVar,
+       module Control.Concurrent.Chan,
+       module Control.Concurrent.QSem,
+       module Control.Concurrent.QSemN,
+       module Control.Concurrent.SampleVar,
+
+       -- * Merging of streams
        mergeIO,                -- :: [a]   -> [a] -> IO [a]
-       nmergeIO                -- :: [[a]] -> IO [a]
+       nmergeIO,               -- :: [[a]] -> IO [a]
+       -- $merge
+
+       -- * GHC\'s implementation of concurrency
+
+       -- |This section describes features specific to GHC\'s
+       -- implementation of Concurrent Haskell.
+       
+       -- ** Terminating the program
+
+       -- $termination
+
+       -- ** Pre-emption
+
+       -- $preemption
+
     ) where
 
 import Prelude
@@ -60,12 +89,57 @@ import ConcBase
 #endif
 
 import Control.Concurrent.MVar
-import Control.Concurrent.CVar
 import Control.Concurrent.Chan
 import Control.Concurrent.QSem
 import Control.Concurrent.QSemN
 import Control.Concurrent.SampleVar
 
+{- $conc_intro
+
+The concurrency extension for Haskell is described in the paper
+/Concurrent Haskell/
+<http://www.haskell.org/ghc/docs/papers/concurrent-haskell.ps.gz>.
+
+Concurrency is \"lightweight\", which means that both thread creation
+and context switching overheads are extremely low.  Scheduling of
+Haskell threads is done internally in the Haskell runtime system, and
+doesn\'t make use of any operating system-supplied thread packages.
+
+Haskell threads can communicate via 'MVar's, a kind of synchronised
+mutable variable (see "Control.Concurrent.MVar").  Several common
+concurrency abstractions can be built from 'MVar's, and these are
+provided by the "Concurrent" library.  Threads may also communicate
+via exceptions. 
+-}
+
+{- $conc_scheduling
+
+    Scheduling may be either pre-emptive or co-operative,
+    depending on the implementation of Concurrent Haskell (see below
+    for imformation related to specific compilers).  In a co-operative
+    system, context switches only occur when you use one of the
+    primitives defined in this module.  This means that programs such
+    as:
+
+
+>   main = forkIO (write \'a\') >> write \'b\'
+>     where write c = putChar c >> write c
+
+    will print either @aaaaaaaaaaaaaa...@ or @bbbbbbbbbbbb...@,
+    instead of some random interleaving of @a@s and @b@s.  In
+    practice, cooperative multitasking is sufficient for writing
+    simple graphical user interfaces.  
+-}
+
+{- $blocking
+Calling a foreign C procedure (such as @getchar@) that blocks waiting
+for input will block /all/ threads, unless the @threadsafe@ attribute
+is used on the foreign call (and your compiler \/ operating system
+supports it).  GHC\'s I\/O system uses non-blocking I\/O internally to
+implement thread-friendly I\/O, so calling standard Haskell I\/O
+functions blocks only the thead making the call.
+-}
+
 -- Thread Ids, specifically the instances of Eq and Ord for these things.
 -- The ThreadId type itself is defined in std/PrelConc.lhs.
 
@@ -99,6 +173,11 @@ instance Show ThreadId where
        showString "ThreadId " . 
         showsPrec d (getThreadId (unsafeCoerce# t))
 
+{- |
+This sparks off a new thread to run the 'IO' computation passed as the
+first argument, and returns the 'ThreadId' of the newly created
+thread.
+-}
 forkIO :: IO () -> IO ThreadId
 forkIO action = IO $ \ s -> 
    case (fork# action_plus s) of (# s1, id #) -> (# s1, ThreadId id #)
@@ -129,6 +208,14 @@ max_buff_size = 1
 mergeIO :: [a] -> [a] -> IO [a]
 nmergeIO :: [[a]] -> IO [a]
 
+-- $merge
+-- The 'mergeIO' and 'nmergeIO' functions fork one thread for each
+-- input list that concurrently evaluates that list; the results are
+-- merged into a single output list.  
+--
+-- Note: Hugs does not provide these functions, since they require
+-- preemptive multitasking.
+
 mergeIO ls rs
  = newEmptyMVar                       >>= \ tail_node ->
    newMVar tail_node          >>= \ tail_list ->
@@ -186,3 +273,96 @@ nmergeIO lss
     return val
   where
     mapIO f xs = sequence (map f xs)
+
+-- ---------------------------------------------------------------------------
+-- More docs
+
+{- $termination
+
+      In a standalone GHC program, only the main thread is
+      required to terminate in order for the process to terminate.
+      Thus all other forked threads will simply terminate at the same
+      time as the main thread (the terminology for this kind of
+      behaviour is \"daemonic threads\").
+
+      If you want the program to wait for child threads to
+      finish before exiting, you need to program this yourself.  A
+      simple mechanism is to have each child thread write to an
+      'MVar' when it completes, and have the main
+      thread wait on all the 'MVar's before
+      exiting:
+
+>   myForkIO :: IO () -> IO (MVar ())
+>   myForkIO io = do
+>     mvar \<- newEmptyMVar
+>     forkIO (io \`finally\` putMVar mvar ())
+>     return mvar
+
+      Note that we use 'finally' from the
+      "Exception" module to make sure that the
+      'MVar' is written to even if the thread dies or
+      is killed for some reason.
+
+      A better method is to keep a global list of all child
+      threads which we should wait for at the end of the program:
+
+>     children :: MVar [MVar ()]
+>     children = unsafePerformIO (newMVar [])
+>     
+>     waitForChildren :: IO ()
+>     waitForChildren = do
+>      (mvar:mvars) \<- takeMVar children
+>      putMVar children mvars
+>      takeMVar mvar
+>      waitForChildren
+>     
+>     forkChild :: IO () -> IO ()
+>     forkChild io = do
+>       mvar \<- newEmptyMVar
+>       forkIO (p \`finally\` putMVar mvar ())
+>       childs \<- takeMVar children
+>       putMVar children (mvar:childs)
+>     
+>     later = flip finally
+>     
+>     main =
+>      later waitForChildren $
+>      ...
+
+      The main thread principle also applies to calls to Haskell from
+      outside, using @foreign export@.  When the @foreign export@ed
+      function is invoked, it starts a new main thread, and it returns
+      when this main thread terminates.  If the call causes new
+      threads to be forked, they may remain in the system after the
+      @foreign export@ed function has returned.
+-}
+
+{- $preemption
+
+      GHC implements pre-emptive multitasking: the execution of
+      threads are interleaved in a random fashion.  More specifically,
+      a thread may be pre-empted whenever it allocates some memory,
+      which unfortunately means that tight loops which do no
+      allocation tend to lock out other threads (this only seems to
+      happen with pathalogical benchmark-style code, however).
+
+      The rescheduling timer runs on a 20ms granularity by
+      default, but this may be altered using the
+      @-i<n>@ RTS option.  After a rescheduling
+      \"tick\" the running thread is pre-empted as soon as
+      possible.
+
+      One final note: the
+      @aaaa@ @bbbb@ example may not
+      work too well on GHC (see Scheduling, above), due
+      to the locking on a 'Handle'.  Only one thread
+      may hold the lock on a 'Handle' at any one
+      time, so if a reschedule happens while a thread is holding the
+      lock, the other thread won\'t be able to run.  The upshot is that
+      the switch from @aaaa@ to
+      @bbbbb@ happens infrequently.  It can be
+      improved by lowering the reschedule tick period.  We also have a
+      patch that causes a reschedule whenever a thread waiting on a
+      lock is woken up, but haven\'t found it to be useful for anything
+      other than this example :-)
+-}
diff --git a/Control/Concurrent/CVar.hs b/Control/Concurrent/CVar.hs
deleted file mode 100644 (file)
index 0d7c7b8..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
------------------------------------------------------------------------------
--- |
--- Module      :  Control.Concurrent.CVar
--- Copyright   :  (c) The University of Glasgow 2001
--- License     :  BSD-style (see the file libraries/base/LICENSE)
--- 
--- Maintainer  :  libraries@haskell.org
--- Stability   :  experimental
--- Portability :  non-portable
---
--- Channel variables are one-element channels.
---
------------------------------------------------------------------------------
-
-module Control.Concurrent.CVar
-       ( -- abstract
-         CVar
-       , newCVar       -- :: IO (CVar a)
-       , writeCVar     -- :: CVar a -> a -> IO ()
-       , readCVar      -- :: CVar a -> IO a
-       ) where
-
-import Prelude
-
-import Control.Concurrent.MVar
-
--- @MVars@ provide the basic mechanisms for synchronising access to a
--- shared resource. @CVars@, or channel variables, provide an abstraction
--- that guarantee that the producer is not allowed to run riot, but
--- enforces the interleaved access to the channel variable,i.e., a
--- producer is forced to wait up for a consumer to remove the previous
--- value before it can deposit a new one in the @CVar@.
-
-data CVar a
- = CVar (MVar a)     -- prod -> cons
-        (MVar ())    -- cons -> prod
-
-newCVar :: IO (CVar a)
-newCVar 
- = newEmptyMVar >>= \ datum ->
-   newMVar ()   >>= \ ack ->
-   return (CVar datum ack)
-
-writeCVar :: CVar a -> a -> IO ()
-
-writeCVar (CVar datum ack) val
- = takeMVar ack      >> 
-   putMVar datum val >>
-   return ()
-
-readCVar :: CVar a -> IO a
-readCVar (CVar datum ack)
- = takeMVar datum >>= \ val ->
-   putMVar ack () >> 
-   return val
index e756d1a..09afdb5 100644 (file)
@@ -6,31 +6,29 @@
 -- 
 -- Maintainer  :  libraries@haskell.org
 -- Stability   :  experimental
--- Portability :  non-portable
+-- Portability :  non-portable (concurrency).
 --
--- Standard, unbounded channel abstraction.
+-- Unbounded channels.
 --
 -----------------------------------------------------------------------------
 
 module Control.Concurrent.Chan
-       ( Chan                  -- abstract
-
-         -- creator
-       , newChan               -- :: IO (Chan a)
-
-         -- operators
-       , writeChan             -- :: Chan a -> a -> IO ()
-       , readChan              -- :: Chan a -> IO a
-       , dupChan               -- :: Chan a -> IO (Chan a)
-       , unGetChan             -- :: Chan a -> a -> IO ()
-
-       , isEmptyChan           -- :: Chan a -> IO Bool
-
-         -- stream interface
-       , getChanContents       -- :: Chan a -> IO [a]
-       , writeList2Chan        -- :: Chan a -> [a] -> IO ()
-
-       ) where
+  ( 
+         -- * The 'Chan' type
+       Chan,                   -- abstract
+
+         -- * Operations
+       newChan,                -- :: IO (Chan a)
+       writeChan,              -- :: Chan a -> a -> IO ()
+       readChan,               -- :: Chan a -> IO a
+       dupChan,                -- :: Chan a -> IO (Chan a)
+       unGetChan,              -- :: Chan a -> a -> IO ()
+       isEmptyChan,            -- :: Chan a -> IO Bool
+
+         -- * Stream interface
+       getChanContents,        -- :: Chan a -> IO [a]
+       writeList2Chan,         -- :: Chan a -> [a] -> IO ()
+   ) where
 
 import Prelude
 
@@ -41,6 +39,7 @@ import Control.Concurrent.MVar
 -- of the channel contents,i.e.,  the read- and write ends. Empty @MVar@s
 -- are used to handle consumers trying to read from an empty channel.
 
+-- |'Chan' is an abstract type representing an unbounded FIFO channel.
 data Chan a
  = Chan (MVar (Stream a))
         (MVar (Stream a))
@@ -55,6 +54,7 @@ data ChItem a = ChItem a (Stream a)
 -- @newChan@ sets up the read and write end of a channel by initialising
 -- these two @MVar@s with an empty @MVar@.
 
+-- |Build and returns a new instance of 'Chan'.
 newChan :: IO (Chan a)
 newChan = do
    hole  <- newEmptyMVar
@@ -67,6 +67,7 @@ newChan = do
 -- filled in with a new stream element holding the entered value and the
 -- new hole.
 
+-- |Write a value to a 'Chan'.
 writeChan :: Chan a -> a -> IO ()
 writeChan (Chan _read write) val = do
   new_hole <- newEmptyMVar
@@ -74,6 +75,7 @@ writeChan (Chan _read write) val = do
     putMVar old_hole (ChItem val new_hole)
     return new_hole
 
+-- |Read the next value from the 'Chan'.
 readChan :: Chan a -> IO a
 readChan (Chan read _write) = do
   modifyMVar read $ \read_end -> do
@@ -82,12 +84,17 @@ readChan (Chan read _write) = do
        -- else dupChan doesn't work
     return (new_read_end, val)
 
+-- |Duplicate a 'Chan': the duplicate channel begins empty, but data written to
+-- either channel from then on will be available from both.  Hence this creates
+-- a kind of broadcast channel, where data written by anyone is seen by
+-- everyone else.
 dupChan :: Chan a -> IO (Chan a)
 dupChan (Chan _read write) = do
    hole     <- readMVar write
    new_read <- newMVar hole
    return (Chan new_read write)
 
+-- |Put a data item back onto a channel, where it will be the next item read.
 unGetChan :: Chan a -> a -> IO ()
 unGetChan (Chan read _write) val = do
    new_read_end <- newEmptyMVar
@@ -95,6 +102,7 @@ unGetChan (Chan read _write) val = do
      putMVar new_read_end (ChItem val read_end)
      return new_read_end
 
+-- |Returns 'True' if the supplied 'Chan' is empty.
 isEmptyChan :: Chan a -> IO Bool
 isEmptyChan (Chan read write) = do
    withMVar read $ \r -> do
@@ -104,6 +112,8 @@ isEmptyChan (Chan read write) = do
 
 -- Operators for interfacing with functional streams.
 
+-- |Return a lazy list representing the contents of the supplied
+-- 'Chan', much like 'IO.hGetContents'.
 getChanContents :: Chan a -> IO [a]
 getChanContents ch
   = unsafeInterleaveIO (do
@@ -112,6 +122,6 @@ getChanContents ch
        return (x:xs)
     )
 
--------------
+-- |Write an entire list of items to a 'Chan'.
 writeList2Chan :: Chan a -> [a] -> IO ()
 writeList2Chan ch ls = sequence_ (map (writeChan ch) ls)
index eb1f03f..c56750f 100644 (file)
@@ -6,14 +6,16 @@
 -- 
 -- Maintainer  :  libraries@haskell.org
 -- Stability   :  experimental
--- Portability :  non-portable
+-- Portability :  non-portable (concurrency)
 --
--- MVars: Synchronising variables
+-- Synchronising variables
 --
 -----------------------------------------------------------------------------
 
 module Control.Concurrent.MVar
-       ( MVar          -- abstract
+       ( 
+         -- * @MVar@s
+         MVar          -- abstract
        , newEmptyMVar  -- :: IO (MVar a)
        , newMVar       -- :: a -> IO (MVar a)
        , takeMVar      -- :: MVar a -> IO a
@@ -52,6 +54,10 @@ throw = throwIO
 #endif
 
 #ifdef __GLASGOW_HASKELL__
+{-|
+  This is a combination of 'takeMVar' and 'putMVar'; ie. it takes the value
+  from the 'MVar', puts it back, and also returns it.
+-}
 readMVar :: MVar a -> IO a
 readMVar m =
   block $ do
@@ -59,11 +65,17 @@ readMVar m =
     putMVar m a
     return a
 
+-- |Swap the contents of an 'MVar' for a new value.
 swapMVar :: MVar a -> a -> IO a
 swapMVar mvar new = modifyMVar mvar (\old -> return (new,old))
 #endif
 
--- put back the same value, return something
+{-|
+  'withMVar' is a safe wrapper for operating on the contents of an
+  'MVar'.  This operation is exception-safe: it will replace the
+  original contents of the 'MVar' if an exception is raised (see
+  "Control.Exception").
+-}
 withMVar :: MVar a -> (a -> IO b) -> IO b
 withMVar m io = 
   block $ do
@@ -73,7 +85,11 @@ withMVar m io =
     putMVar m a
     return b
 
--- put back a new value, return ()
+{-|
+  A safe wrapper for modifying the contents of an 'MVar'.  Like 'withMVar', 
+  'modifyMVar' will replace the original contents of the 'MVar' if an
+  exception is raised during the operation.
+-}
 modifyMVar_ :: MVar a -> (a -> IO a) -> IO ()
 modifyMVar_ m io = 
   block $ do
@@ -82,7 +98,10 @@ modifyMVar_ m io =
            (\e -> do putMVar m a; throw e)
     putMVar m a'
 
--- put back a new value, return something
+{-|
+  A slight variation on 'modifyMVar_' that allows a value to be
+  returned (@b@) in addition to the modified value of the 'MVar'.
+-}
 modifyMVar :: MVar a -> (a -> IO (a,b)) -> IO b
 modifyMVar m io = 
   block $ do
index 04064e4..2ef6dff 100644 (file)
@@ -6,14 +6,15 @@
 -- 
 -- Maintainer  :  libraries@haskell.org
 -- Stability   :  experimental
--- Portability :  non-portable
+-- Portability :  non-portable (concurrency)
 --
--- General semaphores
+-- Simple quantity semaphores.
 --
 -----------------------------------------------------------------------------
 
 module Control.Concurrent.QSem
-       ( QSem,         -- abstract
+       ( -- * Simple Quantity Semaphores
+         QSem,         -- abstract
          newQSem,      -- :: Int  -> IO QSem
          waitQSem,     -- :: QSem -> IO ()
          signalQSem    -- :: QSem -> IO ()
@@ -29,13 +30,17 @@ import Control.Concurrent.MVar
 -- representing threads currently waiting. The counter is a shared
 -- variable, ensuring the mutual exclusion on its access.
 
+-- |A 'QSem' is a simple quantity semaphore, in which the available
+-- \"quantity\" is always dealt with in units of one.
 newtype QSem = QSem (MVar (Int, [MVar ()]))
 
+-- |Build a new 'QSem'
 newQSem :: Int -> IO QSem
 newQSem init = do
    sem <- newMVar (init,[])
    return (QSem sem)
 
+-- |Wait for a unit to become available
 waitQSem :: QSem -> IO ()
 waitQSem (QSem sem) = do
    (avail,blocked) <- takeMVar sem  -- gain ex. access
@@ -55,6 +60,7 @@ waitQSem (QSem sem) = do
      putMVar sem (0, blocked++[block])
      takeMVar block
 
+-- |Signal that a unit of the 'QSem' is available
 signalQSem :: QSem -> IO ()
 signalQSem (QSem sem) = do
    (avail,blocked) <- takeMVar sem
index 9ba46a2..8e95903 100644 (file)
@@ -6,14 +6,16 @@
 -- 
 -- Maintainer  :  libraries@haskell.org
 -- Stability   :  experimental
--- Portability :  non-portable
+-- Portability :  non-portable (concurrency)
 --
--- Quantity semaphores
+-- Quantity semaphores in which each thread may wait for an arbitrary
+-- \"amount\".
 --
 -----------------------------------------------------------------------------
 
 module Control.Concurrent.QSemN
-       ( QSemN,        -- abstract
+       (  -- * General Quantity Semaphores
+         QSemN,        -- abstract
          newQSemN,     -- :: Int   -> IO QSemN
          waitQSemN,    -- :: QSemN -> Int -> IO ()
          signalQSemN   -- :: QSemN -> Int -> IO ()
@@ -23,13 +25,17 @@ import Prelude
 
 import Control.Concurrent.MVar
 
+-- |A 'QSemN' is a quantity semaphore, in which the available
+-- \"quantity\" may be signalled or waited for in arbitrary amounts.
 newtype QSemN = QSemN (MVar (Int,[(Int,MVar ())]))
 
+-- |Build a new 'QSemN' with a supplied initial quantity.
 newQSemN :: Int -> IO QSemN 
 newQSemN init = do
    sem <- newMVar (init,[])
    return (QSemN sem)
 
+-- |Wait for the specified quantity to become available
 waitQSemN :: QSemN -> Int -> IO ()
 waitQSemN (QSemN sem) sz = do
   (avail,blocked) <- takeMVar sem   -- gain ex. access
@@ -42,6 +48,7 @@ waitQSemN (QSemN sem) sz = do
      putMVar sem (avail, blocked++[(sz,block)])
      takeMVar block
 
+-- |Signal that a given quantity is now available from the 'QSemN'.
 signalQSemN :: QSemN -> Int  -> IO ()
 signalQSemN (QSemN sem) n = do
    (avail,blocked)   <- takeMVar sem
index 2b29092..ccb93e1 100644 (file)
@@ -6,7 +6,7 @@
 -- 
 -- Maintainer  :  libraries@haskell.org
 -- Stability   :  experimental
--- Portability :  non-portable
+-- Portability :  non-portable (concurrency)
 --
 -- Sample variables
 --
@@ -14,6 +14,7 @@
 
 module Control.Concurrent.SampleVar
        (
+        -- * Sample Variables
          SampleVar,         -- :: type _ =
  
         newEmptySampleVar, -- :: IO (SampleVar a)
@@ -28,20 +29,21 @@ import Prelude
 
 import Control.Concurrent.MVar
 
--- Sample variables are slightly different from a normal MVar:
+-- |
+-- Sample variables are slightly different from a normal 'MVar':
 -- 
---  * Reading an empty SampleVar causes the reader to block.
---    (same as takeMVar on empty MVar)
+--  * Reading an empty 'SampleVar' causes the reader to block.
+--    (same as 'takeMVar' on empty 'MVar')
 -- 
---  * Reading a filled SampleVar empties it and returns value.
---    (same as takeMVar)
+--  * Reading a filled 'SampleVar' empties it and returns value.
+--    (same as 'takeMVar')
 -- 
---  * Writing to an empty SampleVar fills it with a value, and
---    potentially, wakes up a blocked reader (same as for putMVar on
---    empty MVar).
+--  * Writing to an empty 'SampleVar' fills it with a value, and
+--    potentially, wakes up a blocked reader (same as for 'putMVar' on
+--    empty 'MVar').
 --
---  * Writing to a filled SampleVar overwrites the current value.
---    (different from putMVar on full MVar.)
+--  * Writing to a filled 'SampleVar' overwrites the current value.
+--    (different from 'putMVar' on full 'MVar'.)
 
 type SampleVar a
  = MVar (Int,          -- 1  == full
@@ -49,19 +51,20 @@ type SampleVar a
                        -- <0 no of readers blocked
           MVar a)
 
--- Initally, a SampleVar is empty/unfilled.
-
+-- |Build a new, empty, 'SampleVar'
 newEmptySampleVar :: IO (SampleVar a)
 newEmptySampleVar = do
    v <- newEmptyMVar
    newMVar (0,v)
 
+-- |Build a 'SampleVar' with an initial value.
 newSampleVar :: a -> IO (SampleVar a)
 newSampleVar a = do
    v <- newEmptyMVar
    putMVar v a
    newMVar (1,v)
 
+-- |If the SampleVar is full, leave it empty.  Otherwise, do nothing.
 emptySampleVar :: SampleVar a -> IO ()
 emptySampleVar v = do
    (readers, var) <- takeMVar v
@@ -70,22 +73,25 @@ emptySampleVar v = do
     else
      putMVar v (readers,var)
 
+-- |Wait for a value to become available, then take it and return.
+readSampleVar :: SampleVar a -> IO a
+readSampleVar svar = do
 --
 -- filled => make empty and grab sample
 -- not filled => try to grab value, empty when read val.
 --
-readSampleVar :: SampleVar a -> IO a
-readSampleVar svar = do
    (readers,val) <- takeMVar svar
    putMVar svar (readers-1,val)
    takeMVar val
 
+-- |Write a value into the 'SampleVar', overwriting any previous value that
+-- was there.
+writeSampleVar :: SampleVar a -> a -> IO ()
+writeSampleVar svar v = do
 --
 -- filled => overwrite
 -- not filled => fill, write val
 --
-writeSampleVar :: SampleVar a -> a -> IO ()
-writeSampleVar svar v = do
    (readers,val) <- takeMVar svar
    case readers of
      1 -> 
index dee798f..b2a54bf 100644 (file)
@@ -14,6 +14,7 @@
 --
 -----------------------------------------------------------------------------
 
+-- #hide
 module Data.Array.Base where
 
 import Prelude
index b19e83a..847c1fe 100644 (file)
@@ -777,10 +777,7 @@ unpacking the strings of error messages.
 \begin{code}
 unpackCString# :: Addr# -> [Char]
 {-# NOINLINE [1] unpackCString# #-}
-unpackCString# a = unpackCStringList# a
-
-unpackCStringList# :: Addr# -> [Char]
-unpackCStringList# addr 
+unpackCString# addr 
   = unpack 0#
   where
     unpack nh
@@ -849,10 +846,10 @@ unpackNBytes#  addr len# = unpack [] (len# -# 1#)
 
 {-# RULES
 "unpack"       [~1] forall a   . unpackCString# a                 = build (unpackFoldrCString# a)
-"unpack-list"  [1]  forall a   . unpackFoldrCString# a (:) [] = unpackCStringList# a
+"unpack-list"  [1]  forall a   . unpackFoldrCString# a (:) [] = unpackCString# a
 "unpack-append"     forall a n . unpackFoldrCString# a (:) n  = unpackAppendCString# a n
 
--- There's a built-in rule (in GHC.Rules.lhs) for
+-- There's a built-in rule (in PrelRules.lhs) for
 --     unpackFoldr "foo" c (unpackFoldr "baz" c n)  =  unpackFoldr "foobaz" c n
 
   #-}
index 4c4f322..54bdb25 100644 (file)
@@ -68,21 +68,58 @@ data ThreadId = ThreadId ThreadId#
 -- ToDo: data ThreadId = ThreadId (Weak ThreadId#)
 -- But since ThreadId# is unlifted, the Weak type must use open
 -- type variables.
+{- ^
+A 'ThreadId' is an abstract type representing a handle to a thread.
+'ThreadId' is an instance of 'Eq', 'Ord' and 'Show', where
+the 'Ord' instance implements an arbitrary total ordering over
+'ThreadId's. The 'Show' instance lets you convert an arbitrary-valued
+'ThreadId' to string form; showing a 'ThreadId' value is occasionally
+useful when debugging or diagnosing the behaviour of a concurrent
+program.
+
+NOTE: in GHC, if you have a 'ThreadId', you essentially have
+a pointer to the thread itself.  This means the thread itself can\'t be
+garbage collected until you drop the 'ThreadId'.
+This misfeature will hopefully be corrected at a later date.
+-}
 
 --forkIO has now been hoisted out into the Concurrent library.
 
+{- | 'killThread' terminates the given thread (Note: 'killThread' is
+not implemented in Hugs).  Any work already done by the thread isn\'t
+lost: the computation is suspended until required by another thread.
+The memory used by the thread will be garbage collected if it isn\'t
+referenced from anywhere.  The 'killThread' function may be defined in
+terms of 'throwTo':
+
+>   killThread = throwTo (AsyncException ThreadKilled)
+-}
 killThread :: ThreadId -> IO ()
 killThread (ThreadId id) = IO $ \ s ->
    case (killThread# id (AsyncException ThreadKilled) s) of s1 -> (# s1, () #)
 
+{- | 'throwTo' raises an arbitrary exception in the target thread.
+
+'throwTo' does not return until the exception has been raised in the
+target thread.  The calling thread can thus be certain that the target
+thread has received the exception.  This is a useful property to know
+when dealing with race conditions: eg. if there are two threads that
+can kill each other, it is guaranteed that only one of the threads
+will get to kill the other. -}
 throwTo :: ThreadId -> Exception -> IO ()
 throwTo (ThreadId id) ex = IO $ \ s ->
    case (killThread# id ex s) of s1 -> (# s1, () #)
 
+-- | Returns the 'ThreadId' of the calling thread.
 myThreadId :: IO ThreadId
 myThreadId = IO $ \s ->
    case (myThreadId# s) of (# s1, id #) -> (# s1, ThreadId id #)
 
+
+-- |The 'yield' action allows (forces, in a co-operative multitasking
+-- implementation) a context-switch to any other currently runnable
+-- threads (if any), and is occasionally useful when implementing
+-- concurrency abstractions.
 yield :: IO ()
 yield = IO $ \s -> 
    case (yield# s) of s1 -> (# s1, () #)
@@ -136,58 +173,70 @@ writes.
 \begin{code}
 --Defined in IOBase to avoid cycle: data MVar a = MVar (SynchVar# RealWorld a)
 
+-- |Create an 'MVar' which is initially empty.
 newEmptyMVar  :: IO (MVar a)
 newEmptyMVar = IO $ \ s# ->
     case newMVar# s# of
          (# s2#, svar# #) -> (# s2#, MVar svar# #)
 
+-- |Create an 'MVar' which contains the supplied value.
+newMVar :: a -> IO (MVar a)
+newMVar value =
+    newEmptyMVar       >>= \ mvar ->
+    putMVar mvar value >>
+    return mvar
+
+-- |Return the contents of the 'MVar'.  If the 'MVar' is currently
+-- empty, 'takeMVar' will wait until it is full.  After a 'takeMVar', 
+-- the 'MVar' is left empty.
+-- 
+-- If several threads are competing to take the same 'MVar', one is chosen
+-- to continue at random when the 'MVar' becomes full.
 takeMVar :: MVar a -> IO a
 takeMVar (MVar mvar#) = IO $ \ s# -> takeMVar# mvar# s#
 
+-- |Put a value into an 'MVar'.  If the 'MVar' is currently full,
+-- 'putMVar' will wait until it becomes empty.
+--
+-- If several threads are competing to fill the same 'MVar', one is
+-- chosen to continue at random with the 'MVar' becomes empty.
 putMVar  :: MVar a -> a -> IO ()
 putMVar (MVar mvar#) x = IO $ \ s# ->
     case putMVar# mvar# x s# of
         s2# -> (# s2#, () #)
 
-tryPutMVar  :: MVar a -> a -> IO Bool
-tryPutMVar (MVar mvar#) x = IO $ \ s# ->
-    case tryPutMVar# mvar# x s# of
-        (# s, 0# #) -> (# s, False #)
-        (# s, _  #) -> (# s, True #)
-
-newMVar :: a -> IO (MVar a)
-newMVar value =
-    newEmptyMVar       >>= \ mvar ->
-    putMVar mvar value >>
-    return mvar
-
--- tryTakeMVar is a non-blocking takeMVar
+-- |A non-blocking version of 'takeMVar'.  The 'tryTakeMVar' function
+-- returns immediately, with 'Nothing' if the 'MVar' was empty, or
+-- @'Just' a@ if the 'MVar' was full with contents @a@.  After 'tryTakeMVar',
+-- the 'MVar' is left empty.
 tryTakeMVar :: MVar a -> IO (Maybe a)
 tryTakeMVar (MVar m) = IO $ \ s ->
     case tryTakeMVar# m s of
        (# s, 0#, _ #) -> (# s, Nothing #)      -- MVar is empty
        (# s, _,  a #) -> (# s, Just a  #)      -- MVar is full
 
-{- 
- Low-level op. for checking whether an MVar is filled-in or not.
- Notice that the boolean value returned  is just a snapshot of
- the state of the MVar. By the time you get to react on its result,
- the MVar may have been filled (or emptied) - so be extremely
- careful when using this operation.  
-
- Use tryTakeMVar instead if possible.
+-- |A non-blocking version of 'putMVar'.  The 'tryPutMVar' function
+-- attempts to put the value @a@ into the 'MVar', returning 'True' if
+-- it was successful, or 'False' otherwise.
+tryPutMVar  :: MVar a -> a -> IO Bool
+tryPutMVar (MVar mvar#) x = IO $ \ s# ->
+    case tryPutMVar# mvar# x s# of
+        (# s, 0# #) -> (# s, False #)
+        (# s, _  #) -> (# s, True #)
 
- If you can re-work your abstractions to avoid having to
- depend on isEmptyMVar, then you're encouraged to do so,
- i.e., consider yourself warned about the imprecision in
- general of isEmptyMVar :-)
--}
+-- |Check whether a given 'MVar' is empty.
+--
+-- Notice that the boolean value returned  is just a snapshot of
+-- the state of the MVar. By the time you get to react on its result,
+-- the MVar may have been filled (or emptied) - so be extremely
+-- careful when using this operation.   Use 'tryTakeMVar' instead if possible.
 isEmptyMVar :: MVar a -> IO Bool
 isEmptyMVar (MVar mv#) = IO $ \ s# -> 
     case isEmptyMVar# mv# s# of
         (# s2#, flg #) -> (# s2#, not (flg ==# 0#) #)
 
--- Like addForeignPtrFinalizer, but for MVars
+-- |Add a finalizer to an 'MVar'.  See "Foreign.ForeignPtr" and
+-- "System.Mem.Weak" for more about finalizers.
 addMVarFinalizer :: MVar a -> IO () -> IO ()
 addMVarFinalizer (MVar m) finalizer = 
   IO $ \s -> case mkWeak# m () finalizer s of { (# s1, w #) -> (# s1, () #) }
@@ -200,20 +249,25 @@ addMVarFinalizer (MVar m) finalizer =
 %*                                                                     *
 %************************************************************************
 
-@threadDelay@ delays rescheduling of a thread until the indicated
-number of microseconds have elapsed.  Generally, the microseconds are
-counted by the context switch timer, which ticks in virtual time;
-however, when there are no runnable threads, we don't accumulate any
-virtual time, so we start ticking in real time.  (The granularity is
-the effective resolution of the context switch timer, so it is
-affected by the RTS -C option.)
-
 @threadWaitRead@ delays rescheduling of a thread until input on the
 specified file descriptor is available for reading (just like select).
 @threadWaitWrite@ is similar, but for writing on a file descriptor.
 
 \begin{code}
-threadDelay, threadWaitRead, threadWaitWrite :: Int -> IO ()
+-- |The 'threadDelay' operation will cause the current thread to
+-- suspend for a given number of microseconds.  Note that the resolution
+-- used by the Haskell runtime system\'s internal timer together with the
+-- fact that the thread may take some time to be rescheduled after the
+-- time has expired, means that the accuracy is more like 1\/50 second.
+threadDelay :: Int -> IO ()
+
+-- | Block the current thread until data is available to read on the
+-- given file descriptor.
+threadWaitRead :: Int -> IO ()
+
+-- | Block the current thread until data can be written to the
+-- given file descriptor.
+threadWaitWrite :: Int -> IO ()
 
 threadDelay     (I# ms) = IO $ \s -> case delay# ms s     of s -> (# s, () #)
 threadWaitRead  (I# fd) = IO $ \s -> case waitRead# fd s  of s -> (# s, () #)
index 3e6462a..b2ec6e6 100644 (file)
@@ -123,6 +123,11 @@ unsafeInterleaveIO (IO m)
 -- Handle type
 
 data MVar a = MVar (MVar# RealWorld a)
+{- ^
+An 'MVar' (pronounced \"em-var\") is a synchronising variable, used
+for communication between concurrent threads.  It can be thought of
+as a a box, which may be empty or full.
+-}
 
 -- pull in Eq (Mvar a) too, to avoid GHC.Conc being an orphan-instance module
 instance Eq (MVar a) where
index d926ef1..1e01ae9 100644 (file)
 module Text.ParserCombinators.ReadP
   ( 
   -- * The 'ReadP' type
-    ReadP      -- :: * -> *; instance Functor, Monad, MonadPlus
+  ReadP,      -- :: * -> *; instance Functor, Monad, MonadPlus
   
   -- * Primitive operations
-  , get        -- :: ReadP Char
-  , look       -- :: ReadP String
-  , (+++)      -- :: ReadP a -> ReadP a -> ReadP a
+  get,        -- :: ReadP Char
+  look,       -- :: ReadP String
+  (+++),      -- :: ReadP a -> ReadP a -> ReadP a
   
   -- * Other operations
-  , pfail      -- :: ReadP a
-  , satisfy    -- :: (Char -> Bool) -> ReadP Char
-  , char       -- :: Char -> ReadP Char
-  , string     -- :: String -> ReadP String
-  , munch      -- :: (Char -> Bool) -> ReadP String
-  , munch1     -- :: (Char -> Bool) -> ReadP String
-  , skipSpaces -- :: ReadP ()
-  , choice     -- :: [ReadP a] -> ReadP a
+  pfail,      -- :: ReadP a
+  satisfy,    -- :: (Char -> Bool) -> ReadP Char
+  char,       -- :: Char -> ReadP Char
+  string,     -- :: String -> ReadP String
+  munch,      -- :: (Char -> Bool) -> ReadP String
+  munch1,     -- :: (Char -> Bool) -> ReadP String
+  skipSpaces, -- :: ReadP ()
+  choice,     -- :: [ReadP a] -> ReadP a
   
   -- * Conversions
-  , readP_to_S -- :: ReadP a -> ReadS a
-  , readS_to_P -- :: ReadS a -> ReadP a
+  readP_to_S, -- :: ReadP a -> ReadS a
+  readS_to_P, -- :: ReadS a -> ReadP a
   )
  where
 
index bfb4fc2..b501a8e 100644 (file)
 -----------------------------------------------------------------------------
 
 module Text.ParserCombinators.ReadPrec
-  ( ReadPrec      -- :: * -> *; instance Functor, Monad, MonadPlus
+  ( 
+  ReadPrec,      -- :: * -> *; instance Functor, Monad, MonadPlus
   
   -- * Precedences
-  , Prec          -- :: *; = Int
-  , minPrec       -- :: Prec; = 0
+  Prec,          -- :: *; = Int
+  minPrec,       -- :: Prec; = 0
 
   -- * Primitive operations
-  , lift          -- :: ReadP a -> ReadPrec a
-  , prec          -- :: Prec -> ReadPrec a -> ReadPrec a
-  , step          -- :: ReadPrec a -> ReadPrec a
-  , reset         -- :: ReadPrec a -> ReadPrec a
+  lift,          -- :: ReadP a -> ReadPrec a
+  prec,          -- :: Prec -> ReadPrec a -> ReadPrec a
+  step,          -- :: ReadPrec a -> ReadPrec a
+  reset,         -- :: ReadPrec a -> ReadPrec a
 
   -- * Other operations
-  , get           -- :: ReadPrec Char
-  , look          -- :: ReadPrec String
-  , (+++)         -- :: ReadPrec a -> ReadPrec a -> ReadPrec a
-  , pfail         -- :: ReadPrec a
-  , choice        -- :: [ReadPrec a] -> ReadPrec a
+  get,           -- :: ReadPrec Char
+  look,          -- :: ReadPrec String
+  (+++),         -- :: ReadPrec a -> ReadPrec a -> ReadPrec a
+  pfail,         -- :: ReadPrec a
+  choice,        -- :: [ReadPrec a] -> ReadPrec a
 
   -- converters
-  , readPrec_to_P -- :: ReadPrec a       -> (Int -> ReadP a)
-  , readP_to_Prec -- :: (Int -> ReadP a) -> ReadPrec a
-  , readPrec_to_S -- :: ReadPrec a       -> (Int -> ReadS a)
-  , readS_to_Prec -- :: (Int -> ReadS a) -> ReadPrec a
+  readPrec_to_P, -- :: ReadPrec a       -> (Int -> ReadP a)
+  readP_to_Prec, -- :: (Int -> ReadP a) -> ReadPrec a
+  readPrec_to_S, -- :: ReadPrec a       -> (Int -> ReadS a)
+  readS_to_Prec, -- :: (Int -> ReadS a) -> ReadPrec a
   )
  where