-- | -- Module : Simulation.Aivika.Experiment.MRef -- Copyright : Copyright (c) 2012-2015, David Sorokin -- License : BSD3 -- Maintainer : David Sorokin -- Stability : experimental -- Tested with: GHC 7.10.1 -- -- The module defines a shared mutable reference accessible from different threads. -- module Simulation.Aivika.Experiment.MRef (MRef, newMRef, readMRef, maybeReadMRef, writeMRef, maybeWriteMRef, modifyMRef_, modifyMRef, withMRef) where import Control.Concurrent.MVar import Data.Maybe import Data.IORef -- | This is a shared mutable reference accessible from different threads. data MRef a = MRef { mrefLock :: MVar (), -- ^ A reference lock. mrefData :: IORef a -- ^ The reference itself. } -- | Create a new shared reference with the specified initial value. newMRef :: a -> IO (MRef a) newMRef a = do v <- newMVar () r <- newIORef a return MRef { mrefLock = v, mrefData = r } -- | Read the contents of the shared reference. readMRef :: MRef a -> IO a readMRef = readIORef . mrefData -- | Like 'maybe' but for the shared reference under assumption -- that the reference is updated only once by its initial value. maybeReadMRef :: b -> (a -> IO b) -> MRef (Maybe a) -> IO b maybeReadMRef b0 f x = do a <- readIORef (mrefData x) case a of Just a -> f a Nothing -> withMVar (mrefLock x) $ \() -> do a <- readIORef (mrefData x) case a of Just a -> f a Nothing -> return b0 -- | Update the contents of the shared reference. writeMRef :: MRef a -> a -> IO () writeMRef x a = withMVar (mrefLock x) $ \() -> writeIORef (mrefData x) a -- | Update the contents if the reference was empty and then return a result of -- applying the specified function to either the initial or current value. maybeWriteMRef :: MRef (Maybe a) -> IO a -> (a -> IO b) -> IO b maybeWriteMRef x m0 f = do a <- readIORef (mrefData x) case a of Just a -> f a Nothing -> withMVar (mrefLock x) $ \() -> do a <- readIORef (mrefData x) case a of Just a -> f a Nothing -> do a0 <- m0 writeIORef (mrefData x) (Just a0) f a0 -- | Modify the contents of the shared reference. modifyMRef_ :: MRef a -> (a -> IO a) -> IO () modifyMRef_ x f = withMVar (mrefLock x) $ \() -> do a <- readIORef (mrefData x) a' <- f a writeIORef (mrefData x) a' -- | Modify the contents of the shared reference but allow returning the result. modifyMRef :: MRef a -> (a -> IO (a, b)) -> IO b modifyMRef x f = withMVar (mrefLock x) $ \() -> do a <- readIORef (mrefData x) (a', b) <- f a writeIORef (mrefData x) a' return b -- | A safe wrapper for operating with the contents of shared reference. withMRef :: MRef a -> (a -> IO b) -> IO b withMRef x f = withMVar (mrefLock x) $ \() -> readIORef (mrefData x) >>= f