How to use streaming safe shared variables in Haskell

IORef s, MVar s, and TVar can be used to combine a common variable into a parallel context. I studied parallel hackell for some time, and now I am faced with some questions. After searching on stackoverflow and reading some related questions, my questions are not completely resolved.

To give a specific situation, I will show the actual problem. I have common variables that are never empty, and I want them to be mutable states, so some threads can change these variables at the same time.

Well, it seems that everything TVar decides everything clearly. But I am not satisfied with this, and I am very thirsty for an answer to the above questions. Any help is appreciated.

-------------- re: @GabrielGonzalez BFS interface code ------------------

Below is the code for my BFS interface using the state monad.

 {-# LANGUAGE TypeFamilies, FlexibleContexts #-} module Data.Graph.Par.Class where import Data.Ix import Data.Monoid import Control.Concurrent import Control.Concurrent.MVar import Control.Monad import Control.Monad.Trans.State class (Ix (Vertex g), Ord (Edge g), Ord (Path g)) => ParGraph g where type Vertex g :: * type Edge g :: * -- type Path g :: * -- useless type VertexProperty g :: * type EdgeProperty g :: * edges :: ga -> IO [Edge g] vertexes :: ga -> IO [Vertex g] adjacencies :: ga -> Vertex g -> IO [Vertex g] vertexProperty :: Vertex g -> ga -> IO (VertexProperty g) edgeProperty :: Edge g -> ga -> IO (EdgeProperty g) atomicModifyVertexProperty :: (VertexProperty g -> IO (VertexProperty g)) -> Vertex g -> ga -> IO (ga) -- fixed spanForest :: ParGraph g => [Vertex g] -> StateT (ga) IO () spanForest roots = parallelise (map spanTree roots) -- parallel version spanForestSeq :: ParGraph g => [Vertex g] -> StateT (ga) IO () spanForestSeq roots = forM_ roots spanTree -- sequencial version spanTree :: ParGraph g => Vertex g -> StateT (ga) IO () spanTree root = spanTreeOneStep root >>= \res -> case res of [] -> return () adjs -> spanForestSeq adjs spanTreeOneStep :: ParGraph g => Vertex g -> StateT (ga) IO [Vertex g] spanTreeOneStep v = StateT $ \g -> adjacencies gv >>= \adjs -> return (adjs, g) parallelise :: (ParGraph g, Monoid b) => [StateT (ga) IO b] -> StateT (ga) IO b parallelise [] = return mempty parallelise ss = syncGraphOp $ map forkGraphOp ss forkGraphOp :: (ParGraph g, Monoid b) => StateT (ga) IO b -> StateT (ga) IO (MVar b) forkGraphOp t = do s <- get mv <- mapStateT (forkHelper s) t return mv where forkHelper sx = do mv <- newEmptyMVar forkIO $ x >>= \(b, s) -> putMVar mv b return (mv, s) syncGraphOp :: (ParGraph g, Monoid b) => [StateT (ga) IO (MVar b)] -> StateT (ga) IO b syncGraphOp [] = return mempty syncGraphOp ss = collectMVars ss >>= waitResults where collectMVars [] = return [] collectMVars (x:xs) = do mvx <- x mvxs <- collectMVars xs return (mvx:mvxs) waitResults mvs = StateT $ \g -> forM mvs takeMVar >>= \res -> return ((mconcat res), g) 
+6
source share
1 answer
  • Modern processors offer a comparison and replacement command that atomically changes one pointer. I expect that if you track deep enough, you will find that this command is used to implement atomicModifyIORef . Therefore, it is easy to provide atomic access to a single pointer. However, since such hardware support is not supported by more than one pointer, all you need to be done in the software. This is usually due to the invention and manual application of the protocol in all of your threads, which is complex and error prone.

  • Yes, if all threads agree to use only a "single takeMVar followed by one putMVar behavior", then modifyMVar safe.

+4
source

All Articles