packages feed

strict-mutable-base-2.0.0.0: src/Control/Concurrent/MVar/Strict.hs

-- | 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
-- otherwise.  They can be used in multiple different ways:
--
--   1. As synchronized mutable variables,
--
--   2. As channels, with 'takeMVar' and 'putMVar' as receive and send, and
--
--   3. As a binary semaphore @'MVar' ()@, with 'takeMVar' and 'putMVar' as
--      wait and signal.
--
-- They were introduced in the paper
-- ["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
-- blocks.)
--
-- === Applicability
--
-- 'MVar's offer more flexibility than t'Data.IORef.Strict.IORef's, but less
-- flexibility than t'GHC.Conc.STM'.  They are appropriate for building
-- synchronization 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 t'GHC.Conc.STM'
-- instead.
--
-- In particular, the "bigger" functions in this module ('swapMVar',
-- 'withMVar', 'modifyMVar_' and 'modifyMVar') are simply
-- the composition of a 'takeMVar' followed by a 'putMVar' with
-- exception safety.
-- These have atomicity guarantees only if all other threads
-- perform a 'takeMVar' before a 'putMVar' as well;  otherwise, they may
-- block.
--
-- === Fairness
--
-- 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 (this is what GHC does),
-- but this is not guaranteed in the semantics.
--
-- === Ordering
--
-- 'MVar' operations are always observed to take place in the order
-- they are written in the program, regardless of the memory model of
-- the underlying machine.  This is in contrast to t'Data.IORef.Strict.IORef'
-- operations which may appear out-of-order to another thread in some cases.
--
-- === Example
--
-- Consider the following concurrent data structure, a skip channel.
-- This is a channel for an intermittent source of high bandwidth
-- information (for example, mouse movement events.)  Writing to the
-- channel never blocks, and reading from the channel only returns the
-- most recent value, or blocks if there are no new values.  Multiple
-- readers are supported with a @dupSkipChan@ operation.
--
-- A skip channel is a pair of 'MVar's. The first 'MVar' contains the
-- current value, and a list of semaphores that need to be notified
-- when it changes. The second 'MVar' is a semaphore for this particular
-- reader: it is full if there is a value in the channel that this
-- reader has not read yet, and empty otherwise.
--
-- @
-- data SkipChan a = SkipChan (MVar (a, [MVar ()])) (MVar ())
--
-- 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
--
-- 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)
-- @
--
-- This example was adapted from the original Concurrent Haskell paper.
-- For more examples of 'MVar's being used to build higher-level
-- synchronization primitives, see t'Control.Concurrent.Chan.Strict.Chan' and
-- t'Control.Concurrent.QSem.QSem'.
module Control.Concurrent.MVar.Strict
  ( MVar

    -- * Operations
  , newEmptyMVar
  , newMVar
  , takeMVar
  , putMVar
  , readMVar
  , swapMVar
  , tryTakeMVar
  , tryPutMVar
  , tryReadMVar
  , isEmptyMVar
  , withMVar
  , withMVarMasked
  , modifyMVar_
  , modifyMVar
  , modifyMVarMasked_
  , modifyMVarMasked
  , mkWeakMVar
  ) where

import Control.DeepSeq
import Control.Exception (evaluate)
import GHC.Exts (mkWeak#)
import GHC.IO (IO(..))
import GHC.Weak (Weak(..))
import qualified Control.Concurrent.MVar as Base
import qualified GHC.MVar as GHC

-- | An 'MVar' (pronounced \"em-var\") is a synchronising variable
-- used for communication between concurrent threads, which evaluates its content
-- to Weak Head Normal Form.
-- It can be thought of as a box, which may be empty or full.
newtype MVar a = MVar (GHC.MVar a)
  deriving (Eq, NFData, NFData1)

-- | Create an 'MVar' which is initially empty.
newEmptyMVar :: IO (MVar a)
newEmptyMVar = MVar <$> Base.newEmptyMVar

-- | Create an 'MVar' which contains the supplied value.
--
-- Evaluates the initial value to WHNF.
newMVar :: a -> IO (MVar a)
newMVar a = fmap MVar . Base.newMVar =<< evaluate a

-- | 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.
--
-- There are two further important properties of 'takeMVar':
--
--   * 'takeMVar' is single-wakeup.  That is, if there are multiple
--     threads blocked in 'takeMVar', and the 'MVar' becomes full,
--     only one thread will be woken up.  The runtime guarantees that
--     the woken thread completes its 'takeMVar' operation.
--
--   * When multiple threads are blocked on an 'MVar', they are
--     woken up in FIFO order.  This is useful for providing
--     fairness properties of abstractions built using 'MVar's.
--
takeMVar :: MVar a -> IO a
takeMVar (MVar var) = Base.takeMVar var

-- | Put a value into an 'MVar'.  If the 'MVar' is currently full,
-- 'putMVar' will wait until it becomes empty.
--
-- There are two further important properties of 'putMVar':
--
--   * 'putMVar' is single-wakeup.  That is, if there are multiple
--     threads blocked in 'putMVar', and the 'MVar' becomes empty,
--     only one thread will be woken up.  The runtime guarantees that
--     the woken thread completes its 'putMVar' operation.
--
--   * When multiple threads are blocked on an 'MVar', they are
--     woken up in FIFO order.  This is useful for providing
--     fairness properties of abstractions built using 'MVar's.
--
-- Evaluates the new value to WHNF.
putMVar :: MVar a -> a -> IO ()
putMVar (MVar var) a = Base.putMVar var =<< evaluate a

-- | Atomically read the contents of an 'MVar'.  If the 'MVar' is
-- currently empty, 'readMVar' will wait until it is full.
-- 'readMVar' is guaranteed to receive the next 'putMVar'.
--
-- '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.
readMVar :: MVar a -> IO a
readMVar (MVar var) = Base.readMVar var

-- | Take a value from an 'MVar', put a new value into the 'MVar' and
-- return the value taken. This function is atomic only if there are
-- no other producers for this 'MVar'. In other words, it cannot guarantee
-- that, by the time 'swapMVar' gets the chance to write to the 'MVar',
-- the value of the 'MVar' has not been altered
-- by a write operation from another thread.
--
-- Evaluates the new value to WHNF.
swapMVar :: MVar a -> a -> IO a
swapMVar (MVar var) a = Base.swapMVar var =<< evaluate a

-- | 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 var) = Base.tryTakeMVar var

-- | 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.
--
-- Evaluates the new value to WHNF.
tryPutMVar :: MVar a -> a -> IO Bool
tryPutMVar (MVar var) a = Base.tryPutMVar var =<< evaluate a

-- | A non-blocking version of 'readMVar'.  The 'tryReadMVar' function
-- returns immediately, with 'Nothing' if the 'MVar' was empty, or
-- @'Just' a@ if the 'MVar' was full with contents @a@.
tryReadMVar :: MVar a -> IO (Maybe a)
tryReadMVar (MVar var) = Base.tryReadMVar var

-- | 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 var) = Base.isEmptyMVar var

-- | 'withMVar' is an exception-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").  However, it is only atomic if there are no
-- other producers for this 'MVar'. In other words, it cannot guarantee
-- that, by the time 'withMVar' gets the chance to write to the 'MVar',
-- the value of the 'MVar' has not been altered
-- by a write operation from another thread.
withMVar :: MVar a -> (a -> IO b) -> IO b
withMVar (MVar var) action = Base.withMVar var action
{-# INLINE withMVar #-}

-- | Like 'withMVar', but the @IO@ action in the second argument is executed
-- with asynchronous exceptions masked.
withMVarMasked :: MVar a -> (a -> IO b) -> IO b
withMVarMasked (MVar var) action = Base.withMVarMasked var action
{-# INLINE withMVarMasked #-}

-- | An exception-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.  This
-- function is only atomic if there are no other producers for this
-- 'MVar'. In other words, it cannot guarantee that, by the time
-- 'modifyMVar_' gets the chance to write to the 'MVar', the value
-- of the 'MVar' has not been altered by a write operation from another thread.
--
-- Evaluates the new value to WHNF.
modifyMVar_ :: MVar a -> (a -> IO a) -> IO ()
modifyMVar_ (MVar var) action = Base.modifyMVar_ var $ \a0 -> do
  a <- action a0
  evaluate a
{-# INLINE modifyMVar_ #-}

-- | A slight variation on 'modifyMVar_' that allows a value to be
-- returned (@b@) in addition to the modified value of the 'MVar'.
--
-- Evaluates the new value to WHNF. The returned value is not evaluated.
modifyMVar :: MVar a -> (a -> IO (a, b)) -> IO b
modifyMVar (MVar var) action = Base.modifyMVar var $ \a0 -> do
  (a, b) <- action a0
  (, b) <$> evaluate a
{-# INLINE modifyMVar #-}

-- | Like 'modifyMVar_', but the @IO@ action in the second argument is executed with
-- asynchronous exceptions masked.
--
-- Evaluates the new value to WHNF.
modifyMVarMasked_ :: MVar a -> (a -> IO a) -> IO ()
modifyMVarMasked_ (MVar var) action = Base.modifyMVarMasked_ var $ \a0 -> do
  a <- action a0
  evaluate a
{-# INLINE modifyMVarMasked_ #-}

-- | Like 'modifyMVar', but the @IO@ action in the second argument is executed with
-- asynchronous exceptions masked.
--
-- Evaluates the new value to WHNF. The returned value is not evaluated.
modifyMVarMasked :: MVar a -> (a -> IO (a, b)) -> IO b
modifyMVarMasked (MVar var) action = Base.modifyMVarMasked var $ \a0 -> do
  (a, b) <- action a0
  (, b) <$> evaluate a
{-# INLINE modifyMVarMasked #-}

-- | Make a 'Weak' pointer to an 'MVar', using the second argument as
-- a finalizer to run when the 'MVar' is garbage-collected.
mkWeakMVar :: MVar a -> IO () -> IO (Weak (MVar a))
mkWeakMVar var@(MVar (GHC.MVar var#)) (IO finalizer) = IO $ \s0 ->
  case mkWeak# var# var finalizer s0 of
    (# s1, w #) -> (# s1, Weak w #)