diff --git a/libraries/base/Control/Concurrent/Chan.hs b/libraries/base/Control/Concurrent/Chan.hs index 9d5510e1bf4e0fdd5bd90bf43a76461deb86cda2..4f0dd62784e83373312efa39eba30c11048fe314 100644 --- a/libraries/base/Control/Concurrent/Chan.hs +++ b/libraries/base/Control/Concurrent/Chan.hs @@ -13,9 +13,9 @@ -- -- Unbounded channels. -- --- The channels are implemented with @MVar@s and therefore inherit all the +-- The channels are implemented with 'Control.Concurrent.MVar's and therefore inherit all the -- caveats that apply to @MVar@s (possibility of races, deadlocks etc). The --- stm (software transactional memory) library has a more robust implementation +-- @stm@ (software transactional memory) library has a more robust implementation -- of channels called @TChan@s. -- ----------------------------------------------------------------------------- @@ -43,7 +43,7 @@ import Control.Exception (mask_) #define _UPK_(x) {-# UNPACK #-} !(x) -- A channel is represented by two @MVar@s keeping track of the two ends --- of the channel contents,i.e., the read- and write ends. Empty @MVar@s +-- 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. @@ -59,13 +59,13 @@ data ChItem a = ChItem a _UPK_(Stream a) -- although it leads to higher allocation, the channel data takes up -- less space and is therefore quicker to GC. --- See the Concurrent Haskell paper for a diagram explaining the +-- See the Concurrent Haskell paper for a diagram explaining -- how the different channel operations proceed. -- @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'. +-- |Build and return a new instance of 'Chan'. newChan :: IO (Chan a) newChan = do hole <- newEmptyMVar @@ -113,7 +113,7 @@ readChan (Chan readVar _) = 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 +-- 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. -- diff --git a/libraries/base/Control/Concurrent/MVar.hs b/libraries/base/Control/Concurrent/MVar.hs index 999c97f1b5a899e61f53b3113f050862d65a27d0..9abf68ad86c53eb86ff4b4e23174bbc237e24b3c 100644 --- a/libraries/base/Control/Concurrent/MVar.hs +++ b/libraries/base/Control/Concurrent/MVar.hs @@ -11,7 +11,7 @@ -- Stability : stable -- Portability : non-portable (concurrency) -- --- An @'MVar' t@ is mutable location that is either empty or contains a +-- An @'MVar' t@ is a mutable location that is either empty or contains a -- value of type @t@. It has two fundamental operations: 'putMVar' -- which fills an 'MVar' if it is empty and blocks otherwise, and -- 'takeMVar' which empties an 'MVar' if it is full and blocks @@ -25,7 +25,7 @@ -- wait and signal. -- -- They were introduced in the paper --- <https://www.haskell.org/ghc/docs/papers/concurrent-haskell.ps.gz "Concurrent Haskell"> +-- ["Concurrent Haskell"](https://www.microsoft.com/en-us/research/wp-content/uploads/1996/01/concurrent-haskell.pdf) -- by Simon Peyton Jones, Andrew Gordon and Sigbjorn Finne, though -- some details of their implementation have since then changed (in -- particular, a put on a full 'MVar' used to error, but now merely @@ -35,7 +35,7 @@ -- -- 'MVar's offer more flexibility than 'Data.IORef.IORef's, but less flexibility -- than 'GHC.Conc.STM'. They are appropriate for building synchronization --- primitives and performing simple interthread communication; however +-- primitives and performing simple inter-thread communication; however -- they are very simple and susceptible to race conditions, deadlocks or -- uncaught exceptions. Do not use them if you need to perform larger -- atomic operations such as reading from multiple variables: use 'GHC.Conc.STM' @@ -54,8 +54,8 @@ -- No thread can be blocked indefinitely on an 'MVar' unless another -- thread holds that 'MVar' indefinitely. One usual implementation of -- this fairness guarantee is that threads blocked on an 'MVar' are --- served in a first-in-first-out fashion, but this is not guaranteed --- in the semantics. +-- served in a first-in-first-out fashion (this is what GHC does), +-- but this is not guaranteed in the semantics. -- -- === Gotchas -- @@ -64,7 +64,7 @@ -- 'MVar', it will be evaluated by the thread that consumes it, not the -- thread that produced it. Be sure to 'evaluate' values to be placed -- in an 'MVar' to the appropriate normal form, or utilize a strict --- MVar provided by the strict-concurrency package. +-- @MVar@ provided by the [strict-concurrency](https://hackage.haskell.org/package/strict-concurrency) package. -- -- === Ordering -- @@ -89,33 +89,33 @@ -- reader has not read yet, and empty otherwise. -- -- @ --- data SkipChan a = SkipChan (MVar (a, [MVar ()])) (MVar ()) +-- data SkipChan a = SkipChan (MVar (a, [MVar ()])) (MVar ()) -- --- newSkipChan :: IO (SkipChan a) --- newSkipChan = do --- sem <- newEmptyMVar --- main <- newMVar (undefined, [sem]) --- return (SkipChan main sem) +-- newSkipChan :: IO (SkipChan a) +-- newSkipChan = do +-- sem <- newEmptyMVar +-- main <- newMVar (undefined, [sem]) +-- return (SkipChan main sem) -- --- putSkipChan :: SkipChan a -> a -> IO () --- putSkipChan (SkipChan main _) v = do --- (_, sems) <- takeMVar main --- putMVar main (v, []) --- mapM_ (\sem -> putMVar sem ()) sems +-- putSkipChan :: SkipChan a -> a -> IO () +-- putSkipChan (SkipChan main _) v = do +-- (_, sems) <- takeMVar main +-- putMVar main (v, []) +-- mapM_ (\\sem -> putMVar sem ()) sems -- --- getSkipChan :: SkipChan a -> IO a --- getSkipChan (SkipChan main sem) = do --- takeMVar sem --- (v, sems) <- takeMVar main --- putMVar main (v, sem:sems) --- return v +-- getSkipChan :: SkipChan a -> IO a +-- getSkipChan (SkipChan main sem) = do +-- takeMVar sem +-- (v, sems) <- takeMVar main +-- putMVar main (v, sem : sems) +-- return v -- --- dupSkipChan :: SkipChan a -> IO (SkipChan a) --- dupSkipChan (SkipChan main _) = do --- sem <- newEmptyMVar --- (v, sems) <- takeMVar main --- putMVar main (v, sem:sems) --- return (SkipChan main sem) +-- dupSkipChan :: SkipChan a -> IO (SkipChan a) +-- dupSkipChan (SkipChan main _) = do +-- sem <- newEmptyMVar +-- (v, sems) <- takeMVar main +-- putMVar main (v, sem : sems) +-- return (SkipChan main sem) -- @ -- -- This example was adapted from the original Concurrent Haskell paper. @@ -186,7 +186,7 @@ swapMVar mvar new = -} {-# INLINE withMVar #-} -- inlining has been reported to have dramatic effects; see --- http://www.haskell.org//pipermail/haskell/2006-May/017907.html +-- https://mail.haskell.org/pipermail/haskell/2006-May/017907.html withMVar :: MVar a -> (a -> IO b) -> IO b withMVar m io = mask $ \restore -> do @@ -274,7 +274,7 @@ addMVarFinalizer :: MVar a -> IO () -> IO () addMVarFinalizer = GHC.MVar.addMVarFinalizer -- | Make a 'Weak' pointer to an 'MVar', using the second argument as --- a finalizer to run when 'MVar' is garbage-collected +-- a finalizer to run when the 'MVar' is garbage-collected -- -- @since 4.6.0.0 mkWeakMVar :: MVar a -> IO () -> IO (Weak (MVar a)) diff --git a/libraries/base/Control/Concurrent/QSem.hs b/libraries/base/Control/Concurrent/QSem.hs index f6e686154e46d83ee70f287a895310615adccd8a..b10fc02091ab14c2bbedacae317aa96b086b6c74 100644 --- a/libraries/base/Control/Concurrent/QSem.hs +++ b/libraries/base/Control/Concurrent/QSem.hs @@ -34,7 +34,7 @@ import Data.Maybe -- -- The pattern -- --- > bracket_ waitQSem signalQSem (...) +-- > bracket_ waitQSem signalQSem (...) -- -- is safe; it never loses a unit of the resource. -- @@ -67,7 +67,7 @@ newQSem initial sem <- newMVar (initial, [], []) return (QSem sem) --- |Wait for a unit to become available +-- |Wait for a unit to become available. waitQSem :: QSem -> IO () waitQSem (QSem m) = mask_ $ do @@ -91,7 +91,7 @@ waitQSem (QSem m) = else do putMVar b (); return (i,b1,b2) putMVar m r') --- |Signal that a unit of the 'QSem' is available +-- |Signal that a unit of the 'QSem' is available. signalQSem :: QSem -> IO () signalQSem (QSem m) = uninterruptibleMask_ $ do -- Note [signal uninterruptible] diff --git a/libraries/base/Control/Concurrent/QSemN.hs b/libraries/base/Control/Concurrent/QSemN.hs index 42400b6a936b229e125cd995e79d0dc5dba0f45b..fbdbb7b5be4c800546368028b040fcba052faf05 100644 --- a/libraries/base/Control/Concurrent/QSemN.hs +++ b/libraries/base/Control/Concurrent/QSemN.hs @@ -32,12 +32,12 @@ import Data.IORef (IORef, newIORef, atomicModifyIORef) import System.IO.Unsafe (unsafePerformIO) -- | 'QSemN' is a quantity semaphore in which the resource is acquired --- and released in units of one. It provides guaranteed FIFO ordering +-- and released in arbitrary amounts. It provides guaranteed FIFO ordering -- for satisfying blocked `waitQSemN` calls. -- -- The pattern -- --- > bracket_ (waitQSemN n) (signalQSemN n) (...) +-- > bracket_ (waitQSemN n) (signalQSemN n) (...) -- -- is safe; it never loses any of the resource. -- @@ -71,7 +71,7 @@ newQSemN initial -- An unboxed version of Maybe (MVar a) data MaybeMV a = JustMV !(MVar a) | NothingMV --- |Wait for the specified quantity to become available +-- |Wait for the specified quantity to become available. waitQSemN :: QSemN -> Int -> IO () -- We need to mask here. Once we've enqueued our MVar, we need -- to be sure to wait for it. Otherwise, we could lose our diff --git a/libraries/base/GHC/MVar.hs b/libraries/base/GHC/MVar.hs index e76a69a05c90b4597fa0d34d8e1d777227b88c24..cba610661ae4a01621cdafd50af575ea46e54dee 100644 --- a/libraries/base/GHC/MVar.hs +++ b/libraries/base/GHC/MVar.hs @@ -42,9 +42,11 @@ as a box, which may be empty or full. -} -- pull in Eq (Mvar a) too, to avoid GHC.Conc being an orphan-instance module --- | @since 4.1.0.0 +-- | Compares the underlying pointers. +-- +-- @since 4.1.0.0 instance Eq (MVar a) where - (MVar mvar1#) == (MVar mvar2#) = isTrue# (sameMVar# mvar1# mvar2#) + (MVar mvar1#) == (MVar mvar2#) = isTrue# (sameMVar# mvar1# mvar2#) {- M-Vars are rendezvous points for concurrent threads. They begin @@ -66,9 +68,9 @@ newEmptyMVar = IO $ \ s# -> -- |Create an 'MVar' which contains the supplied value. newMVar :: a -> IO (MVar a) -newMVar value = - newEmptyMVar >>= \ mvar -> - putMVar mvar value >> +newMVar value = do + mvar <- newEmptyMVar + putMVar mvar value return mvar -- |Return the contents of the 'MVar'. If the 'MVar' is currently @@ -95,6 +97,7 @@ takeMVar (MVar mvar#) = IO $ \ s# -> takeMVar# mvar# s# -- -- 'readMVar' is multiple-wakeup, so when multiple readers are -- blocked on an 'MVar', all of them are woken up at the same time. +-- The runtime guarantees that all woken threads complete their 'readMVar' operation. -- -- /Compatibility note:/ Prior to base 4.7, 'readMVar' was a combination -- of 'takeMVar' and 'putMVar'. This mean that in the presence of @@ -104,12 +107,12 @@ takeMVar (MVar mvar#) = IO $ \ s# -> takeMVar# mvar# s# -- can be recovered by implementing 'readMVar as follows: -- -- @ --- readMVar :: MVar a -> IO a --- readMVar m = --- mask_ $ do --- a <- takeMVar m --- putMVar m a --- return a +-- readMVar :: MVar a -> IO a +-- readMVar m = +-- mask_ $ do +-- a <- takeMVar m +-- putMVar m a +-- return a -- @ readMVar :: MVar a -> IO a readMVar (MVar mvar#) = IO $ \ s# -> readMVar# mvar# s#