Explicit concurrent programming in haskell
Download
1 / 23

Explicit Concurrent Programming in Haskell - PowerPoint PPT Presentation


  • 160 Views
  • Updated On :
  • Presentation posted in: General

Explicit Concurrent Programming in Haskell . QIAN XI COS597C 10/28/2010. Outline. Recap of IO Monad Thread Primitives Synchronization with Locks Message Passing Channels Software Transactional Memory Transactional Memory with Data Invariants. IO Monad in Haskell. Why Monad?

loader
I am the owner, or an agent authorized to act on behalf of the owner, of the copyrighted work described.
capcha

Download Presentation

Explicit Concurrent Programming in Haskell

An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -

Presentation Transcript



Outline l.jpg
Outline

  • Recap of IO Monad

  • Thread Primitives

  • Synchronization with Locks

  • Message Passing Channels

  • Software Transactional Memory

  • Transactional Memory with Data Invariants


Io monad in haskell l.jpg
IO Monad in Haskell

  • Why Monad?

    • Pure functional language needs determinism.

fx = e

f 3

...

f 3

= 7

  • What is Monad?

    • an abstract data type: IO a, e.g. IO Int

    • a container of impure, suspended actions/computations

= 7?

  • How to use Monad?

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

getChar :: IO Char

putChar :: Char -> IO ()

main :: IO ()

main = do c <- getChar

putCharc


Creating haskell threads l.jpg
Creating Haskell Threads

  • forkIO :: IO () -> IO ThreadId

effect-ful

computation

identification of a Haskell thread

must be used in an IO monad

  • 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.

  • forkOS :: IO () -> IO ThreadId

    • support certain kinds of foreign calls to external code.

Ex: fibEuler.hs


Mutable variable l.jpg
Mutable Variable

  • Haskell threads communicate through Mvars (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


Mvar operations l.jpg
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


Example make a rendezvous l.jpg
Example: Make A Rendezvous

main :: IO()

main

= do aMVar <- newEmptyMVar

bMVar <- newEmptyMVar

doneMVar <- newEmptyMVar

forkIO (threadA aMVar bMVar doneMVar)

forkIO (threadB aMVar bMVar)

takeMVar doneMVar

...

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.”


Message passing channels l.jpg
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


Haskell stm l.jpg
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.


Tvar operations l.jpg
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


Slide11 l.jpg

bal :: TVar Int

8

7

Thread 1

1 atomically (do

2v <- readTVar bal

3writeTVar bal (v+1)

4 )

Thread 2

1 atomically (do

2v <- readTVar bal

3writeTVar bal (v-3)

4 )

  • 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

  • Thread 1 commits

  • Shared bal variable is updated

  • Transaction log is discarded

7

8

7

4

bal

transaction log of Thread 1

transaction log of Thread 2


Slide12 l.jpg

bal :: TVar Int

5

8

Thread 1

1 atomically (do

2v <- readTVar bal

3writeTVar bal (v+1)

4 )

Thread 2

1 atomically (do

2v <- readTVar bal

3writeTVar bal (v-3)

4 )

5

transaction log of Thread 2

Ex: simpleSTM.hs


When to use retry and orelse l.jpg
When To Use retry and orElse?

  • retry :: STM a

    • abort the current transaction

    • re-execute it from the beginning using a fresh log

withdraw :: TVarInt −> Int −> STM ()

withdraw acc n

= do { bal <− readTVar acc;

if bal < n then retry;

writeTVar acc (bal-n)

}

Ex: account.hs

  • 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

atomically (do { withdraw a1 3

‘orElse‘

withdraw a2 3;

deposit b 3 }

)


Case study arrayblockingqueue discolo et al flops 06 l.jpg
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


Data structure l.jpg
Data Structure

data ArrayBlockingQueueIOe = ArrayBlockingQueueIO{

iempty :: QSem,

ifull :: QSem,

ilock :: MVar (),

ihead :: IORefInt,

itail :: IORefInt,

iused :: IORefInt,

ilen :: Int,

ia :: IOArrayInte

}

data ArrayBlockingQueueSTM e = ArrayBlockingQueueSTM {

shead :: TVar Int,

stail :: TVar Int,

sused :: TVar Int,

slen :: Int,

sa :: Array Int (TVar e)

}


Function take l.jpg
function: take

takeIO :: ArrayBlockingQueueIOe

-> IO e

takeIOabq

= do b <- waitQSem (iemptyabq)

e <- withMVar

(ilockabq)

(\dummy ->

readHeadElementIOabq True)

return e

takeSTM :: ArrayBlockingQueueSTMe

-> IO e

takeSTMabq

= do me <- atomically

( readHeadElementSTMabq

True True)

case me of

Just e -> return e


Function peek l.jpg
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

peekSTM :: ArrayBlockingQueueSTMe

-> IO (Maybe e)

peekSTMabq

= atomically

(readHeadElementSTMabq False

False)


Helper function readheadelement l.jpg
helper function: readHeadElement

readHeadElementIO ::

ArrayBlockingQueueIOe -> Bool -> IO e

readHeadElementIOabq remove

= do

h <- readIORef (iheadabq)

e <- readArray (iaabq) h

if remove

then do

let len = ilenabq

newh = h `mod` len

u <- readIORef (iusedabq)

writeIORef (iheadabq) newh

writeIORef (iusedabq) (u-1)

signalQSem (ifullabq)

else return ()

return e

readHeadElementSTM ::

ArrayBlockingQueueSTMe -> Bool -> Bool

-> STM (Maybe e)

readHeadElementSTMabq remove block

= do u <- readTVar (susedabq)

if u == 0

then if block

then retry

else return Nothing

else do h <- readTVar (sheadabq)

let tv = saabq ! h

e <- readTVartv

if remove

then do

let len = slenabq

let newh = h `mod` len

writeTVar (sheadabq) $! newh

writeTVar (susedabq) $! (u-1)

else return ()

return (Just e)


A more complex function polltimeout l.jpg
A More Complex Function: pollTimeout

  • lock-based mechanism has no support for composing two concurrency abstractions

pollTimeoutSTM :: ArrayBlockingQueueSTMe -> TimeDiff -> IO (Maybe e)

pollTimeoutSTMabq timeout

= do c <- startTimerIO timeout

atomically ((do readTChanc

return Nothing)

`orElse`

(do me <- readHeadElementSTMabq True True

return me)

)


Performance measurements discolo et al flops 06 l.jpg
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


Stm with data invariants l.jpg
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


References l.jpg
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_multicores

  • ...


ad
  • Login