--
-- 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
#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.
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 #)
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 ->
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 :-)
+-}
+++ /dev/null
------------------------------------------------------------------------------
--- |
--- 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
--
-- 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
-- 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))
-- @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
-- 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
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
-- 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
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
-- 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
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)
--
-- 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
#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
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
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
(\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
--
-- 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 ()
-- 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
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
--
-- 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 ()
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
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
--
-- Maintainer : libraries@haskell.org
-- Stability : experimental
--- Portability : non-portable
+-- Portability : non-portable (concurrency)
--
-- Sample variables
--
module Control.Concurrent.SampleVar
(
+ -- * Sample Variables
SampleVar, -- :: type _ =
newEmptySampleVar, -- :: IO (SampleVar a)
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
-- <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
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 ->
--
-----------------------------------------------------------------------------
+-- #hide
module Data.Array.Base where
import Prelude
\begin{code}
unpackCString# :: Addr# -> [Char]
{-# NOINLINE [1] unpackCString# #-}
-unpackCString# a = unpackCStringList# a
-
-unpackCStringList# :: Addr# -> [Char]
-unpackCStringList# addr
+unpackCString# addr
= unpack 0#
where
unpack nh
{-# 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
#-}
-- 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, () #)
\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, () #) }
%* *
%************************************************************************
-@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, () #)
-- 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
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
-----------------------------------------------------------------------------
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