2015-01-28 15:10:19 +03:00
|
|
|
{-# LANGUAGE TypeFamilies #-}
|
2014-12-18 14:03:17 +03:00
|
|
|
|
2015-01-28 15:10:19 +03:00
|
|
|
-- | This module captures the interface of @Conc@ monads in a
|
|
|
|
-- typeclass.
|
2014-12-18 14:03:17 +03:00
|
|
|
module Control.Monad.Conc.Class where
|
|
|
|
|
2014-12-18 14:18:06 +03:00
|
|
|
import Control.Concurrent (forkIO)
|
2014-12-21 12:37:52 +03:00
|
|
|
import Control.Concurrent.MVar (MVar, readMVar, newEmptyMVar, putMVar, tryPutMVar, takeMVar, tryTakeMVar)
|
2014-12-23 18:51:46 +03:00
|
|
|
import Control.Monad (unless, void)
|
2014-12-18 14:03:17 +03:00
|
|
|
|
2015-01-28 15:10:19 +03:00
|
|
|
-- | @MonadConc@ is like a combination of 'ParFuture' and 'ParIVar'
|
|
|
|
-- from the abstract-par package. It captures the interface of @Conc@
|
|
|
|
-- monads in terms of how they can operate on shared state.
|
|
|
|
--
|
|
|
|
-- There are a few notable differences: firstly, @Par@ imposes
|
|
|
|
-- 'NFData' constraints on everything, as it achieves its speed-up by
|
|
|
|
-- forcing evaluation in separate threads. @MonadConc@ doesn't do
|
|
|
|
-- that, and so you need to be careful about where evaluation occurs,
|
|
|
|
-- just like with 'MVar's. Secondly, this builds on futures by
|
|
|
|
-- allowing @CVar@s which threads can read from and write to, possibly
|
|
|
|
-- multiple times, whereas with the @Par@ monads it is illegal to
|
|
|
|
-- write multiple times to the same @IVar@ (or to non-blockingly read
|
|
|
|
-- from it), which removes the possibility of data races.
|
2014-12-18 14:03:17 +03:00
|
|
|
--
|
2015-01-09 05:29:21 +03:00
|
|
|
-- A minimal implementation consists of 'fork', 'newEmptyCVar',
|
2014-12-21 12:37:52 +03:00
|
|
|
-- 'tryPutCVar', and 'tryTakeCVar'. The default implementations of
|
|
|
|
-- 'takeCVar' and 'putCVar', however, are very inefficient, and should
|
|
|
|
-- probably always be overridden to make use of
|
|
|
|
-- implementation-specific blocking functionality.
|
2015-01-28 15:10:19 +03:00
|
|
|
class Monad m => MonadConc m where
|
|
|
|
-- | The mutable reference type. This may contain one value at a
|
|
|
|
-- time, attempting to read or take from an \"empty\" @CVar@ will
|
|
|
|
-- block until it is full, and attempting to put to a \"full\"
|
|
|
|
-- @CVar@ will block until it is empty.
|
|
|
|
type CVar m :: * -> *
|
|
|
|
|
2015-01-12 17:24:12 +03:00
|
|
|
-- | Fork a computation to happen concurrently. Communication may
|
|
|
|
-- happen over @CVar@s.
|
2014-12-18 14:03:17 +03:00
|
|
|
fork :: m () -> m ()
|
|
|
|
|
2015-01-28 15:10:19 +03:00
|
|
|
-- | Create a concurrent computation for the provided action, and
|
|
|
|
-- return a @CVar@ which can be used to query the result.
|
|
|
|
--
|
|
|
|
-- > spawn ma = do
|
|
|
|
-- > cvar <- newEmptyCVar
|
|
|
|
-- > fork $ ma >>= putCVar cvar
|
|
|
|
-- > return cvar
|
|
|
|
spawn :: m a -> m (CVar m a)
|
|
|
|
spawn ma = do
|
|
|
|
cvar <- newEmptyCVar
|
|
|
|
fork $ ma >>= putCVar cvar
|
|
|
|
return cvar
|
|
|
|
|
2015-01-12 17:24:12 +03:00
|
|
|
-- | Create a new empty @CVar@.
|
2015-01-28 15:10:19 +03:00
|
|
|
newEmptyCVar :: m (CVar m a)
|
2014-12-18 14:03:17 +03:00
|
|
|
|
2015-01-12 17:24:12 +03:00
|
|
|
-- | Put a value into a @CVar@. If there is already a value there,
|
2015-01-21 14:26:27 +03:00
|
|
|
-- this will block until that value has been taken, at which point
|
2014-12-18 14:03:17 +03:00
|
|
|
-- the value will be stored.
|
2014-12-21 12:37:52 +03:00
|
|
|
--
|
2014-12-23 18:51:46 +03:00
|
|
|
-- > putCVar cvar a = tryPutCVar cvar a >>= \b -> unless b $ putCVar cvar a
|
2015-01-28 15:10:19 +03:00
|
|
|
putCVar :: CVar m a -> a -> m ()
|
2014-12-23 18:51:46 +03:00
|
|
|
putCVar cvar a = tryPutCVar cvar a >>= \b -> unless b $ putCVar cvar a
|
2014-12-21 12:37:52 +03:00
|
|
|
|
2015-01-12 17:24:12 +03:00
|
|
|
-- | Attempt to put a value in a @CVar@, returning 'True' (and
|
|
|
|
-- filling the @CVar@) if there was nothing there, otherwise
|
|
|
|
-- returning 'False'.
|
2015-01-28 15:10:19 +03:00
|
|
|
tryPutCVar :: CVar m a -> a -> m Bool
|
|
|
|
|
|
|
|
-- | Block until a value is present in the @CVar@, and then return
|
|
|
|
-- it. As with 'readMVar', this does not \"remove\" the value,
|
|
|
|
-- multiple reads are possible.
|
|
|
|
readCVar :: CVar m a -> m a
|
2014-12-18 14:03:17 +03:00
|
|
|
|
2015-01-12 17:24:12 +03:00
|
|
|
-- | Take a value from a @CVar@. This \"empties\" the @CVar@,
|
2015-01-21 14:26:27 +03:00
|
|
|
-- allowing a new value to be put in. This will block if there is no
|
|
|
|
-- value in the @CVar@ already, until one has been put.
|
2014-12-18 14:03:17 +03:00
|
|
|
--
|
2014-12-21 12:37:52 +03:00
|
|
|
-- > takeCVar cvar = tryTakeCVar cvar >>= maybe (takeCVar cvar) return
|
2015-01-28 15:10:19 +03:00
|
|
|
takeCVar :: CVar m a -> m a
|
2014-12-21 10:47:45 +03:00
|
|
|
takeCVar cvar = tryTakeCVar cvar >>= maybe (takeCVar cvar) return
|
2014-12-18 14:03:17 +03:00
|
|
|
|
2015-01-12 17:24:12 +03:00
|
|
|
-- | Attempt to take a value from a @CVar@, returning a 'Just' (and
|
|
|
|
-- emptying the @CVar@) if there was something there, otherwise
|
|
|
|
-- returning 'Nothing'.
|
2015-01-28 15:10:19 +03:00
|
|
|
tryTakeCVar :: CVar m a -> m (Maybe a)
|
2014-12-18 14:18:06 +03:00
|
|
|
|
2015-01-28 15:10:19 +03:00
|
|
|
instance MonadConc IO where
|
|
|
|
type CVar IO = MVar
|
|
|
|
|
|
|
|
readCVar = readMVar
|
2014-12-21 10:47:45 +03:00
|
|
|
fork = void . forkIO
|
|
|
|
newEmptyCVar = newEmptyMVar
|
|
|
|
putCVar = putMVar
|
2014-12-21 12:37:52 +03:00
|
|
|
tryPutCVar = tryPutMVar
|
2014-12-21 10:47:45 +03:00
|
|
|
takeCVar = takeMVar
|
|
|
|
tryTakeCVar = tryTakeMVar
|