Presentation is loading. Please wait.

Presentation is loading. Please wait.

QIAN XI COS597C 10/28/2010 Explicit Concurrent Programming in Haskell.

Similar presentations


Presentation on theme: "QIAN XI COS597C 10/28/2010 Explicit Concurrent Programming in Haskell."— Presentation transcript:

1 QIAN XI COS597C 10/28/2010 Explicit Concurrent Programming in Haskell

2 Outline Recap of IO Monad Thread Primitives Synchronization with Locks Message Passing Channels Software Transactional Memory Transactional Memory with Data Invariants

3 IO Monad in Haskell Why Monad?  Pure functional language needs determinism. getChar :: IO Char putChar :: Char -> IO () main :: IO () main = do c <- getChar putChar c getChar :: IO Char putChar :: Char -> IO () main :: IO () main = do c <- getChar putChar c f x = e f 3... f 3 f x = e f 3... f 3 What is Monad?  an abstract data type: IO a, e.g. IO Int  a container of impure, suspended actions/computations How to use Monad? = 7 = 7? do encloses a sequence of computations: an action, a pattern bounded to the result of an action using < - a set of local definitions introduced using let

4 Creating Haskell Threads forkIO :: IO () -> IO ThreadId effect-ful computatio n effect-ful computatio n identification of a Haskell thread must be used in an IO monad identification of a Haskell thread must be used in an IO monad forkOS :: IO () -> IO ThreadId  support certain kinds of foreign calls to external code.  Concurrency is “lightweight”: both thread creation and context switching overheads are extremely low.  The parent thread will not automatically wait for the child threads to terminate. Ex: fibEuler.hs

5 Mutable Variable Haskell threads communicate through Mvar s (mutable variables). MVar writes and reads occur atomically A MVar may be empty or it may contain a value write to occupied MVar, read from empty MVar:  will be blocked  will be rewoken when it’s empty/ a value is written and try again  wake up scheme: FIFO

6 MVar Operations data MVar a newEmptyMVar :: IO (MVar a) newMVar :: a −> IO (MVar a) takeMVar :: MVar a −> IO a putMVar :: MVar a −> a −> IO () readMVar :: MVar a −> IO a tryTakeMVar :: MVar a −> IO (Maybe a) tryPutMVar :: MVar a −> a −> IO Bool isEmptyMVar :: MVar a −> IO Bool …

7 Example: Make A Rendezvous module Main where import Control.Concurrent import Control.Concurrent.MVar threadA :: MVar String -> MVar String -> MVar Int -> IO() threadA valueToSendMVar valueReceiveMVar doneMVar = do putMVar valueToSendMVar "Are you going trick or treating tonight?” v <- takeMVar valueReceiveMVar putMVar doneMVar 1 threadB :: MVar String -> MVar String ->IO() threadB valueToReceiveMVar valueToSendMVar = do z <- takeMVar valueToReceiveMVar putMVar valueToSendMVar “Yes. Let’s meet at 8pm.” module Main where import Control.Concurrent import Control.Concurrent.MVar threadA :: MVar String -> MVar String -> MVar Int -> IO() threadA valueToSendMVar valueReceiveMVar doneMVar = do putMVar valueToSendMVar "Are you going trick or treating tonight?” v <- takeMVar valueReceiveMVar putMVar doneMVar 1 threadB :: MVar String -> MVar String ->IO() threadB valueToReceiveMVar valueToSendMVar = do z <- takeMVar valueToReceiveMVar putMVar valueToSendMVar “Yes. Let’s meet at 8pm.” main :: IO() main = do aMVar <- newEmptyMVar bMVar <- newEmptyMVar doneMVar <- newEmptyMVar forkIO (threadA aMVar bMVar doneMVar) forkIO (threadB aMVar bMVar) takeMVar doneMVar... main :: IO() main = do aMVar <- newEmptyMVar bMVar <- newEmptyMVar doneMVar <- newEmptyMVar forkIO (threadA aMVar bMVar doneMVar) forkIO (threadB aMVar bMVar) takeMVar doneMVar...

8 Message Passing Channels unbounded FIFO channel data Chan a newChan :: IO (Chan a) writeChan :: Chan a -> a -> IO () readChan :: Chan a -> IO a unGetChan :: Chan a -> a -> IO () isEmptyChan :: Chan a -> IO Bool dupChan :: Chan a -> IO (Chan a)... Ex: chat.hs

9 Haskell STM Programming with MVar can lead to deadlock  one thread is waiting for a value to appear in an MVar  no other thread will ever write a value to that MVar An alternative way to synchronize: software transactional memory (STM)  A special type of shared variable: TVar  TVars are used only inside atomic blocks.  The code inside an atomic block is executed as if it were an atomic instruction.  Functionally, no other thread is running in parallel/interleaved.  In reality, a log is used to roll back execution if conflicts.

10 TVar Operations data STM a −− A monad supporting atomic memory transactions atomically :: STM a −> IO a −− Perform a series of STM actions atomically data TVar a −− Shared memory locations that support atomic memory operations newTVar :: a −> STM (TVar a) −− Create a new TVar with an initial value readTVar :: TVar a −> STM a −− Return the current value stored in a TVar writeTVar :: TVar a −> a −> STM () −− Write the supplied value into a TVar

11 7 bal :: TVar Int Thread 1 1 atomically (do 2 v <- readTVar bal 3 writeTVar bal (v+1) 4 ) WhatValue Read Value Write bal WhatValue Read Value Write Thread 2 1 atomically (do 2 v <- readTVar bal 3 writeTVar bal (v-3) 4 ) 7 transaction log of Thread 2 7 transaction log of Thread 1 84 8 Thread 1 commits Shared bal variable is updated Transaction log is discarded bal Attempt to commit Thread 2 fails, because value in memory is not consistent with the value in the log Transaction re-runs from the beginning

12 bal :: TVar Int 8 Thread 1 1 atomically (do 2 v <- readTVar bal 3 writeTVar bal (v+1) 4 ) Thread 2 1 atomically (do 2 v <- readTVar bal 3 writeTVar bal (v-3) 4 ) WhatValue Read Value Write bal8 transaction log of Thread 2 5 5 Ex: simpleSTM.hs

13 When To Use retry and orElse? retry :: STM a  abort the current transaction  re-execute it from the beginning using a fresh log withdraw :: TVar Int −> Int −> STM () withdraw acc n = do { bal <− readTVar acc; if bal < n then retry; writeTVar acc (bal-n) } withdraw :: TVar Int −> Int −> STM () withdraw acc n = do { bal <− readTVar acc; if bal < n then retry; writeTVar acc (bal-n) } atomically (do { withdraw a1 3 ‘orElse‘ withdraw a2 3; deposit b 3 } ) atomically (do { withdraw a1 3 ‘orElse‘ withdraw a2 3; deposit b 3 } ) orElse :: STM a -> STM a -> STM a  compose two transactions  if one transaction aborts then the other transaction is executed  if it also aborts then the whole transaction is re-executed Ex: account.hs

14 Case Study: ArrayBlockingQueue (Discolo et al. FLOPS 06) from JSR-166, a java implementation of a fixed length queue select 3 representative interfaces:  take: Removes an element from the head of the queue, blocking if the queue is empty  peek: Removes an element from the head of the queue if one is immediately available, otherwise return Nothing  pullTimeout: Retrives and removes the head of this queue, waiting up to the specified wait time if necessary for an element to become available

15 Data Structure data ArrayBlockingQueueIO e = ArrayBlockingQueueIO{ iempty :: QSem, ifull :: QSem, ilock :: MVar (), ihead :: IORef Int, itail :: IORef Int, iused :: IORef Int, ilen :: Int, ia :: IOArray Int e } data ArrayBlockingQueueIO e = ArrayBlockingQueueIO{ iempty :: QSem, ifull :: QSem, ilock :: MVar (), ihead :: IORef Int, itail :: IORef Int, iused :: IORef Int, ilen :: Int, ia :: IOArray Int e } data ArrayBlockingQueueSTM e = ArrayBlockingQueueSTM { shead :: TVar Int, stail :: TVar Int, sused :: TVar Int, slen :: Int, sa :: Array Int (TVar e) } data ArrayBlockingQueueSTM e = ArrayBlockingQueueSTM { shead :: TVar Int, stail :: TVar Int, sused :: TVar Int, slen :: Int, sa :: Array Int (TVar e) }

16 function: take takeIO :: ArrayBlockingQueueIO e -> IO e takeIO abq = do b <- waitQSem (iempty abq) e <- withMVar (ilock abq) (\dummy -> readHeadElementIO abq True) return e takeIO :: ArrayBlockingQueueIO e -> IO e takeIO abq = do b <- waitQSem (iempty abq) e <- withMVar (ilock abq) (\dummy -> readHeadElementIO abq True) return e takeSTM :: ArrayBlockingQueueSTM e -> IO e takeSTM abq = do me <- atomically ( readHeadElementSTM abq True True) case me of Just e -> return e takeSTM :: ArrayBlockingQueueSTM e -> IO e takeSTM abq = do me <- atomically ( readHeadElementSTM abq True True) case me of Just e -> return e

17 function: peek peekIO :: ArrayBlockingQueueIO e -> IO (Maybe e) peekIO abq = do b <- tryWaitQSem (iempty abq) if b then do me <- withMVar (ilock abq) (\dummy -> do u <- readIORef (iused abq) if u == 0 then return Nothing else do e <- readHeadElementIO abq False return (Just e)) signalQSem (iempty abq) return me else return Nothing peekIO :: ArrayBlockingQueueIO e -> IO (Maybe e) peekIO abq = do b <- tryWaitQSem (iempty abq) if b then do me <- withMVar (ilock abq) (\dummy -> do u <- readIORef (iused abq) if u == 0 then return Nothing else do e <- readHeadElementIO abq False return (Just e)) signalQSem (iempty abq) return me else return Nothing peekSTM :: ArrayBlockingQueueSTM e -> IO (Maybe e) peekSTM abq = atomically (readHeadElementSTM abq False False) peekSTM :: ArrayBlockingQueueSTM e -> IO (Maybe e) peekSTM abq = atomically (readHeadElementSTM abq False False)

18 helper function: readHeadElement readHeadElementIO :: ArrayBlockingQueueIO e -> Bool -> IO e readHeadElementIO abq remove = do h <- readIORef (ihead abq) e <- readArray (ia abq) h if remove then do let len = ilen abq newh = h `mod` len u <- readIORef (iused abq) writeIORef (ihead abq) newh writeIORef (iused abq) (u-1) signalQSem (ifull abq) else return () return e readHeadElementIO :: ArrayBlockingQueueIO e -> Bool -> IO e readHeadElementIO abq remove = do h <- readIORef (ihead abq) e <- readArray (ia abq) h if remove then do let len = ilen abq newh = h `mod` len u <- readIORef (iused abq) writeIORef (ihead abq) newh writeIORef (iused abq) (u-1) signalQSem (ifull abq) else return () return e readHeadElementSTM :: ArrayBlockingQueueSTM e -> Bool -> Bool -> STM (Maybe e) readHeadElementSTM abq remove block = do u <- readTVar (sused abq) if u == 0 then if block then retry else return Nothing else do h <- readTVar (shead abq) let tv = sa abq ! h e <- readTVar tv if remove then do let len = slen abq let newh = h `mod` len writeTVar (shead abq) $! newh writeTVar (sused abq) $! (u-1) else return () return (Just e) readHeadElementSTM :: ArrayBlockingQueueSTM e -> Bool -> Bool -> STM (Maybe e) readHeadElementSTM abq remove block = do u <- readTVar (sused abq) if u == 0 then if block then retry else return Nothing else do h <- readTVar (shead abq) let tv = sa abq ! h e <- readTVar tv if remove then do let len = slen abq let newh = h `mod` len writeTVar (shead abq) $! newh writeTVar (sused abq) $! (u-1) else return () return (Just e)

19 A More Complex Function: pollTimeout lock-based mechanism has no support for composing two concurrency abstractions pollTimeoutSTM :: ArrayBlockingQueueSTM e -> TimeDiff -> IO (Maybe e) pollTimeoutSTM abq timeout = do c <- startTimerIO timeout atomically ((do readTChan c return Nothing) `orElse` (do me <- readHeadElementSTM abq True True return me) ) pollTimeoutSTM :: ArrayBlockingQueueSTM e -> TimeDiff -> IO (Maybe e) pollTimeoutSTM abq timeout = do c <- startTimerIO timeout atomically ((do readTChan c return Nothing) `orElse` (do me <- readHeadElementSTM abq True True return me) )

20 Performance Measurements (Discolo et al. FLOPS 06) The test  creates an ArrayBlockingQueue of type integer  creates an equal number of reader and writer threads that simply loops for the specific number of iterations performing taking or put operations on the queue  completes when all threads have terminated For each processor configuration (1-8 processors)  varies only the number of reader/writer threads

21

22 STM with Data Invariants STM can also deal with consistency of the program check E where E is an invariant that should be preserved by every atomic update check :: Bool -> STM a  check True = return ()  check False = retry account.hs with invariants

23 References “A Tutorial on Parallel and Concurrent Programming in Haskell”, Jones et al., AFP summer school notes, 2008 “Lock Free Data Structures using STM in Haskell”, Discolo et al., FLOPS 2006 http://haskell.org/haskellwiki/Haskell_for_multico res http://haskell.org/haskellwiki/Haskell_for_multico res...


Download ppt "QIAN XI COS597C 10/28/2010 Explicit Concurrent Programming in Haskell."

Similar presentations


Ads by Google