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

Slides:



Advertisements
Similar presentations
Copyright 2008 Sun Microsystems, Inc Better Expressiveness for HTM using Split Hardware Transactions Yossi Lev Brown University & Sun Microsystems Laboratories.
Advertisements

Concurrency unlocked transactional memory for composable concurrency Tim Harris Maurice Herlihy Simon Marlow Simon Peyton Jones.
Concurrent programming for dummies (and smart people too) Tim Harris & Keir Fraser.
1 Chapter 5 Concurrency: Mutual Exclusion and Synchronization Principals of Concurrency Mutual Exclusion: Hardware Support Semaphores Readers/Writers Problem.
Ch 7 B.
Concurrency Important and difficult (Ada slides copied from Ed Schonberg)
Ch. 7 Process Synchronization (1/2) I Background F Producer - Consumer process :  Compiler, Assembler, Loader, · · · · · · F Bounded buffer.
Kathleen Fisher cs242 Reading: “Beautiful Concurrency”,Beautiful Concurrency “The Transactional Memory / Garbage Collection Analogy”The Transactional Memory.
Silberschatz, Galvin and Gagne ©2009 Operating System Concepts – 8 th Edition, Chapter 6: Process Synchronization.
Operating System Concepts and Techniques Lecture 12 Interprocess communication-1 M. Naghibzadeh Reference M. Naghibzadeh, Operating System Concepts and.
CH7 discussion-review Mahmoud Alhabbash. Q1 What is a Race Condition? How could we prevent that? – Race condition is the situation where several processes.
Chapter 16 Communicating With the Outside World. Motivation  In Chapter 3 we learned how to do basic IO, but it was fairly limited.  In this chapter.
Software Transactional Memory Steve Severance – Alpha Heavy Industries.
Parallel and Concurrent Haskell Part II Simon Marlow (Microsoft Research, Cambridge, UK)
Simon Peyton Jones (Microsoft Research) Tokyo Haskell Users Group April 2010.
Parallelism and Concurrency Koen Lindström Claessen Chalmers University Gothenburg, Sweden Ulf Norell.
Semi-Explicit Parallel Programming in Haskell Satnam Singh Microsoft Research Cambridge Leeds2009.
Introduction to Lock-free Data-structures and algorithms Micah J Best May 14/09.
1 Lecture 7: Transactional Memory Intro Topics: introduction to transactional memory, “lazy” implementation.
Inter Process Communication:  It is an essential aspect of process management. By allowing processes to communicate with each other: 1.We can synchronize.
Concurrency: Mutual Exclusion, Synchronization, Deadlock, and Starvation in Representative Operating Systems.
Concurrency and Software Transactional Memories Satnam Singh, Microsoft Faculty Summit 2005.
Department of Computer Science Presenters Dennis Gove Matthew Marzilli The ATOMO ∑ Transactional Programming Language.
29-Jun-15 Java Concurrency. Definitions Parallel processes—two or more Threads are running simultaneously, on different cores (processors), in the same.
1 Organization of Programming Languages-Cheng (Fall 2004) Concurrency u A PROCESS or THREAD:is a potentially-active execution context. Classic von Neumann.
Race Conditions CS550 Operating Systems. Review So far, we have discussed Processes and Threads and talked about multithreading and MPI processes by example.
Lightweight Concurrency in GHC KC Sivaramakrishnan Tim Harris Simon Marlow Simon Peyton Jones 1.
David Walker Optional Reading: “Beautiful Concurrency”, “The Transactional Memory / Garbage Collection Analogy” “A Tutorial on Parallel and Concurrent.
Experience with Processes and Monitors in Mesa
Chapter 3: Processes. 3.2 Silberschatz, Galvin and Gagne ©2005 Operating System Concepts Chapter 3: Processes Process Concept Process Scheduling Operations.
Kathleen Fisher cs242 Reading: “Beautiful Concurrency”,Beautiful Concurrency “The Transactional Memory / Garbage Collection Analogy”The Transactional Memory.
Cosc 4740 Chapter 6, Part 3 Process Synchronization.
CSE 230 Concurrency: STM Slides due to: Kathleen Fisher, Simon Peyton Jones, Satnam Singh, Don Stewart.
Lecture 3 Process Concepts. What is a Process? A process is the dynamic execution context of an executing program. Several processes may run concurrently,
Semaphores, Locks and Monitors By Samah Ibrahim And Dena Missak.
COP-3330: Object Oriented Programming Flow Control May 16, 2012 Eng. Hector M Lugo-Cordero, MS.
Haskell - A Perspective Presented by Gábor Lipták April 2011.
CSC321 Concurrent Programming: §5 Monitors 1 Section 5 Monitors.
© M. Winter COSC 4P41 – Functional Programming Programming with actions Why is I/O an issue? I/O is a kind of side-effect. Example: Suppose there.
ICS 313: Programming Language Theory Chapter 13: Concurrency.
0 Odds and Ends in Haskell: Folding, I/O, and Functors Adapted from material by Miran Lipovaca.
Lee CSCE 314 TAMU 1 CSCE 314 Programming Languages Interactive Programs: I/O and Monads Dr. Hyunyoung Lee.
Software Transactional Memory Should Not Be Obstruction-Free Robert Ennals Presented by Abdulai Sei.
13-1 Chapter 13 Concurrency Topics Introduction Introduction to Subprogram-Level Concurrency Semaphores Monitors Message Passing Java Threads C# Threads.
CS533 – Spring Jeanie M. Schwenk Experiences and Processes and Monitors with Mesa What is Mesa? “Mesa is a strongly typed, block structured programming.
Fall 2008Programming Development Techniques 1 Topic 20 Concurrency Section 3.4.
Chapter 2 Process Management. 2 Objectives After finish this chapter, you will understand: the concept of a process. the process life cycle. process states.
Chapter 5 Concurrency: Mutual Exclusion and Synchronization Operating Systems: Internals and Design Principles, 6/E William Stallings Patricia Roy Manatee.
AtomCaml: First-class Atomicity via Rollback Michael F. Ringenburg and Dan Grossman University of Washington International Conference on Functional Programming.
1 Previous Lecture Overview  semaphores provide the first high-level synchronization abstraction that is possible to implement efficiently in OS. This.
Scalable lock-free Stack Algorithm Wael Yehia York University February 8, 2010.
CSCI1600: Embedded and Real Time Software Lecture 17: Concurrent Programming Steven Reiss, Fall 2015.
4 November 2005 CS 838 Presentation 1 Nested Transactional Memory: Model and Preliminary Sketches J. Eliot B. Moss and Antony L. Hosking Presented by:
24-Jun-16 Haskell Dealing with impurity. Purity Haskell is a “pure” functional programming language Functions have no side effects Input/output is a side.
Parallelism and Concurrency Koen Lindström Claessen Chalmers University Gothenburg, Sweden Patrik Jansson.
Parallelism and Concurrency
(Nested) Open Memory Transactions in Haskell
CS510 Operating System Foundations
Software Transactional Memory
CSCI1600: Embedded and Real Time Software
Transactions with Nested Parallelism
CSE 451: Operating Systems Autumn 2003 Lecture 7 Synchronization
CSE 451: Operating Systems Autumn 2005 Lecture 7 Synchronization
CSE 451: Operating Systems Winter 2003 Lecture 7 Synchronization
CSE 153 Design of Operating Systems Winter 19
Programming Languages
Outline Chapter 2 (cont) Chapter 3: Processes Virtual machines
CSCI1600: Embedded and Real Time Software
Process/Thread Synchronization (Part 2)
Advanced Functional Programming
Presentation transcript:

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

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?  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

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

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

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

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

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

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 Ex: simpleSTM.hs

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

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 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) }

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

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)

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)

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) )

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 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 “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 res res...