Explicit concurrent programming in haskell l.jpg
This presentation is the property of its rightful owner.
Sponsored Links
1 / 23

Explicit Concurrent Programming in Haskell PowerPoint PPT Presentation


  • 139 Views
  • Uploaded 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?

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


Explicit concurrent programming in haskell l.jpg

Explicit Concurrent Programming in Haskell

QIAN XI

COS597C 10/28/2010


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

  • ...


  • Login