base-4.20.0.0: src/Control/Concurrent/MVar.hs
{-# LANGUAGE Safe #-}
-- |
--
-- Module : Control.Concurrent.MVar
-- Copyright : (c) The University of Glasgow 2001
-- License : BSD-style (see the file libraries/base/LICENSE)
--
-- Maintainer : libraries@haskell.org
-- Stability : stable
-- Portability : non-portable (concurrency)
--
-- 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 'Data.IORef.IORef's, but less flexibility
-- than '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 '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.
--
-- === Gotchas
--
-- Like many other Haskell data structures, 'MVar's are lazy. This
-- means that if you place an expensive unevaluated thunk inside an
-- '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](https://hackage.haskell.org/package/strict-concurrency) package.
--
-- === 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 'Data.IORef.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 'Control.Concurrent.Chan' and
-- 'Control.Concurrent.QSem'.
--
module Control.Concurrent.MVar
(-- * @MVar@s
MVar,
newEmptyMVar,
newMVar,
takeMVar,
putMVar,
readMVar,
swapMVar,
tryTakeMVar,
tryPutMVar,
isEmptyMVar,
withMVar,
withMVarMasked,
modifyMVar_,
modifyMVar,
modifyMVarMasked_,
modifyMVarMasked,
tryReadMVar,
mkWeakMVar,
addMVarFinalizer
) where
import GHC.Internal.Control.Concurrent.MVar