9
1 Lecture 12 Transactional Memory Shared Memory Concurrency Lock-based programming is difficult There are many potential problems: Deadlock Starvation Priority inversion Convoying Non-compositionality Is there some way to eliminate at least some of these problems? 2 PPHT09 – TM Convoying Convoying occurs when a process has taken a mutex and is then pre-empted by the scheduler It has the effect that other processes may not be allowed to enter the mutex This inhibits concurrency 3 PPHT09 – TM Non-compositionality Lock-based programming doesn't compose Example: Suppose you have two thread safe buffers and you want to atomically take an element from one fh d h h of them and put it in the other 4 PPHT09 – TM class Buffer<E> { public E get(); public void put(E item); } Non-compositionality A not so nice solution: Expose the locks of the buffers Lock both buffers before moving the element 5 PPHT09 – TM class Buffer<E> { public void lock(); public void unlock(); public E get(); public void put(E item); } Non-compositionality Another not so nice solution Create a new lock which must be taken each time any of the two buffers are accessed The number of locks grows as we compose algorithms Takes time Increases the risk of programming errors 6 PPHT09 – TM

Convoying Non-compositionality · 2009-10-09 · • Still a hot research topic but already useful PPHT09 – TM 9 Software Transactional Memory • Access for the programmer As a

  • Upload
    others

  • View
    1

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Convoying Non-compositionality · 2009-10-09 · • Still a hot research topic but already useful PPHT09 – TM 9 Software Transactional Memory • Access for the programmer As a

1

Lecture 12

Transactional Memory

Shared Memory Concurrency

• Lock-based programming is difficult

• There are many potential problems:◦ Deadlock

◦ Starvation

◦ Priority inversion

◦ Convoying

◦ Non-compositionality

• Is there some way to eliminate at least some of these problems?

2PPHT09 – TM

Convoying

• Convoying occurs when a process has taken a mutex and is then pre-empted by the scheduler

• It has the effect that other processes may not be allowed to enter the mutex

• This inhibits concurrency

3PPHT09 – TM

Non-compositionality

• Lock-based programming doesn't compose

• Example:◦ Suppose you have two thread safe buffers and

you want to atomically take an element from one f h d h hof them and put it in the other

4PPHT09 – TM

class Buffer<E> {public E get();public void put(E item);

}

Non-compositionality

• A not so nice solution:◦ Expose the locks of the buffers

◦ Lock both buffers before moving the element

5PPHT09 – TM

class Buffer<E> {public void lock();public void unlock();public E get();public void put(E item);

}

Non-compositionality

• Another not so nice solution◦ Create a new lock which must be taken each time

any of the two buffers are accessed

• The number of locks grows as we compose algorithms◦ Takes time

◦ Increases the risk of programming errors

6PPHT09 – TM

Page 2: Convoying Non-compositionality · 2009-10-09 · • Still a hot research topic but already useful PPHT09 – TM 9 Software Transactional Memory • Access for the programmer As a

2

Optimistic Concurrency

• Lock-based synchronization:Pessimistic Concurrency◦ We always assume things go wrong and thus we

need mutual exclusion

• An alternative: Optimistic Concurrency◦ Assume we have mutual exclusion

◦ Perform our critical section

◦ Check if everything was OK

◦ Revert our actions if it wasn't

◦ Otherwise proceed7PPHT09 – TM

Non-blocking synchronisation

• It is possible to write shared-memory algorithms without locks◦ Usually using special hardware instructions — CAS

• In many circumstances such algorithms are faster than lock-based alternatives◦ They allow for more concurrency

• Very difficult to do in general

8PPHT09 – TM

Transactional Memory

• A concept for easy non-blocking programming

• Implementations◦ Hardware

• Sun’s Rock processor? (We need to visit The Oracle)

◦ Software• Using special HW instructions — CAS

• Using locks

• Still a hot research topic but already useful

9PPHT09 – TM

Software Transactional Memory

• Access for the programmer◦ As a library

• Java: jvstm, JSTM (XSTM), DSTM2

• C/C++: TinySTM, LibLTX, LibCTM, RSTM

• C# Python Lisp Ocaml Perl• C#, Python, Lisp, Ocaml, Perl, …

• Haskell

◦ As a language construct• Java CCR — not available, yet

10PPHT09 – TM

Transactions

• A standard database concept◦ A group of operations should execute atomically,

◦ Or not at all

• Transactional Memory takes this idea to operations on memory and specifically shared variables

11PPHT09 – TM

Transactions — Workings

• When writing to variables, don't actually modify them, instead:

• Keep a log over all the reads and writes that the transaction makes

• When the transaction is done:1) Validate: check that any read variables still have

the same value

2) Commit: make the changes permanent

◦ If the validation failed rerun the transaction

12PPHT09 – TM

Page 3: Convoying Non-compositionality · 2009-10-09 · • Still a hot research topic but already useful PPHT09 – TM 9 Software Transactional Memory • Access for the programmer As a

3

Transactions — Example

v = 1x = 2

P1 P2

13PPHT09 – TM

i = read xi = i + 1store i in xstore i in v

LOG

read x 2 read x 2

LOG

store x 3 store x 3

store v 3 store v 3

Transactions — Example

P1 P2

v = 3x = 3

14PPHT09 – TM

i = read xi = i + 1store i in xstore i in v

LOG

read x 2

LOG

store x 3

store v 3

Transactions — Example

v = 3x = 3

P2P1

15PPHT09 – TM

i = read xi = i + 1store i in xstore i in v

read x 3

LOG

store x 4

store v 4

LOG

Transactions — Example

v = 4x = 4

P2P1

16PPHT09 – TM

i = read xi = i + 1store i in xstore i in v

LOGLOG

Conditional Critical Regions

• STM is often presented using CCR

• Introduced by the atomic keyword

• Defines a transaction guarded by a condition

i ( di i ) {

• Whoa. Deja vu.

• A deja vu is usually a glitch in the Matrix17PPHT09 – TM

atomic (condition) {statements

}

Specifying Synchronisation

• The book uses a notation to specify atomic actions◦ Not part of JR/MPD

◦ Purely for describing the desired behaviour of a

from lecture 2

18PPHT09 – Shared Update

program

<S> – statement S is executed atomically

<await (B) S> – execute <S>, starting onlywhen B is true

Page 4: Convoying Non-compositionality · 2009-10-09 · • Still a hot research topic but already useful PPHT09 – TM 9 Software Transactional Memory • Access for the programmer As a

4

Implementing await

• await statement is very expressive◦ Mutual exclusion

◦ Conditional synchronisation

• Difficult to implement in general

from lecture 2

19PPHT09 – Shared Update

• Though, some special cases are easy◦ await statement without body

•<await (B) ;>• Sufficient for solving the shared update problem in

low-level programming

◦ Other interesting cases will come later

STM — Programming

• So you say await is expressive and easy to use in programming

• But we did not see anything that supports this◦ Give us some code to chew on

• OK◦ How about a question from an older exam

◦ (students did really well on this question)

20PPHT09 – TM

Readers/Writers Problem

• Another classic synchronisation problem

• Two kinds of processes share access to a “database”◦ Readers examine the contents

21PPHT09 – TM

◦ Multiple readers allowed concurrently

◦ Writers examine and modify

◦ A writer must have mutex

• Invariant◦ (nr==0 ∨ nw==0) ∧ nw<=1

Readers/Writers Controller

• Database is globally accessible◦ Cannot be “internal to the input statement”

• Encapsulate only the access protocol◦ Similar to the monitor and MP solution

22PPHT09 – TM

public void start_read();public void end_read();public void start_write();public void end_write();

Readers/Writers on One Slide

private process RWserver {int nr = 0;int nw = 0;while (true) {

inni void start read() st

from lecture 8

23PPHT09 – Message Passing

inni void start_read() stnw==0 { nr++; }

[] void end_read() { nr--; }[] void start_write() st

nr==0 && nw==0 { nw++; }[] void end_write() { nw--; }

}}

Readers/Writers on One Slide, too

class RW {private int nr = 0, nw = 0;public void startRead() {

atomic (nw == 0) { nr++; }}public void endRead() {

atomic { nr--; }}

24PPHT09 – TM

atomic { nr ; }}public void startWrite() {

atomic (nr == 0 && nw == 0) {nw++;

}}public void endWrite() {

atomic { nw--; }}}

Whoa. Deja vu.

Page 5: Convoying Non-compositionality · 2009-10-09 · • Still a hot research topic but already useful PPHT09 – TM 9 Software Transactional Memory • Access for the programmer As a

5

R/W – Coarse-grained Solution

process R((int i=0;i++;i<M)) {while(true){

<await (nw==0) nr++;>//read database<nr--;>

}}

from lecture 3

25PPHT09 – Semaphores

}}

process W((int i=0;i++;i<N)) {while(true){

<await (nr==0 && nw==0) nw++;>//write database<nw--;>

}} On one slide, too.

TM and Side Effects

• await statement is great

• Let’s help the Therac-25 programmersWhy Reasoning?

• Horror stories

26PPHT09 – TM

2PPHT09 – Reasoning

Horror stories◦ The Therac-25 incident

• What can happen when programmers are not even aware of the principles of concurrent programming

◦ The Gaisal train crash• August 1999

• Error: assumption about speed of train

• Investigation report hard to find

TM and Side Effects

public void start_treatment() {atomic (all_set_condition) {

...fire_the_beam()...

}

• How many times will it fire the beam?

• When will it be fired?

27PPHT09 – TM

}}

TM and Side Effects

• How many times will we be prompted to input something?

public String read_text() {t i {

28PPHT09 – TM

atomic {...char = input_from_keyboard()...

}}

TM and Side Effects

• Transactions cannot perform any operation that cannot be undone

• Side effects such as I/O do not mix well with transactional memory

29PPHT09 – TM

Isolating Side Effects — Haskell

• Functional

• Pure◦ Side effects cannot occur everywhere

◦ Ideally suited for STM

• GHC◦ A Haskell compiler

◦ Includes STM support in the run-time system• Though, the current implementation seems to have

some (additional) issues with fairness

◦ Exposes STM functionality as a library

30PPHT09 – TM

Page 6: Convoying Non-compositionality · 2009-10-09 · • Still a hot research topic but already useful PPHT09 – TM 9 Software Transactional Memory • Access for the programmer As a

6

Haskell

• Pure and side effecting computations are separated by the type system

”a string” :: StringreadLine :: IO String

31PPHT09 – TM

readLine :: IO StringputStrLn :: String -> IO ()

The type constructor IOindicates that this function can

perform side effects

Haskell

• I/O is isolated using the type system

• Therefore STM can be easily isolated from I/O

• But Haskell does not allow variables to be updated everywhere

• Solution◦ Add a new additional type constructor STM which

allows separation

◦ Shared variables can only be accessed inside STM

32PPHT09 – TM

Haskell STM

module Control.Concurrent.STM

data STM adata TVar a

newTVarIO :: a -> IO (TVar a)newTVar a > STM (TVar a)

33PPHT09 – TM

newTVar :: a -> STM (TVar a)readTVar :: TVar a -> STM awriteTVar :: TVar a -> a -> STM ()atomically :: STM a -> IO aretry :: STM aorElse :: STM a -> STM a -> STM a

instance Monad STM

A Counter Example

update :: TVar Int -> STM ()update counter =

do v <- readTVar counterwriteTVar counter (v+1)

STM means: part of a transaction

34PPHT09 – TM

updateIO :: TVar Int -> IO ()updateIO counter =

do putStrLn ”Before update”atomically (update counter)putStrLn ”After update”

Perform the transaction

A Shared Counterexample?

loop n counter =forM_ [1..n]

(\_ -> atomically (update counter))

main =do counter <- newTVarIO 0

• What is going on?

• Why is this not working?35PPHT09 – TM

do counter < newTVarIO 0forkIO (loop 100000 counter)loop 100000 counterval <- atomically (readTVar counter)putStrLn (show val)

Conditional Synchronisation

• Haskell transactions cannot be executed conditionally,instead

atomic (condition) {statements

}

• Conditions can be programmatically checked and the transaction re-run using◦ Normal conditional statements (if, case, …)

◦ And the retry function

36PPHT09 – TM

Page 7: Convoying Non-compositionality · 2009-10-09 · • Still a hot research topic but already useful PPHT09 – TM 9 Software Transactional Memory • Access for the programmer As a

7

Conditional Synchronisation

• Function retry◦ Abort the current transaction

◦ Re-run when appropriate

• When should a transaction re-run?◦ Recall: each transaction has a log of read variables

◦ Re-run only if a variable in the log has changed

37PPHT09 – TM

Conditional Synchronisation

newRWController = donrVar <- newTVar 0nwVar <- newTVar 0return (nrVar,nwVar)

startRead (nrVar,nwVar) = do dTV V

38PPHT09 – TM

nr <- readTVar nrVarnw <- readTVar nwVarif nw == 0

then writeTVar nrVar (nr+1)else retry

endRead (nrVar,nwVar) = donr <- readTVar nrVarwriteTVar nrVar (nr-1)

Conditional Synchronisation

startWrite (nrVar,nwVar) = donr <- readTVar nrVarnw <- readTVar nwVarif (nr == 0 && nw == 0)

then writeTVar nwVar (nw+1)else retry

• Not quite one slide solution but still quite readable in plain English

39PPHT09 – TM

else retry

endWrite (nvVar,nwVar) = donw <- readTVar nwVarwriteTVar nwVar (nw-1)

Controlling Fairness

• Previously we studied examples of how to explicitly control the fairness policies◦ Starving out writers?

◦ Servicing in the order of arrival, etc.

• This cannot be easily done in the Haskell transactional setting◦ All processes blocking on a particular variable are

woken up when the variable is modified

◦ It is up to the scheduler to ensure fairness

40PPHT09 – TM

Unbounded Buffers

• Producer can work freely

• Consumer must wait for producer

41PPHT09 – TM

Infinite bar

… …

Unbounded Buffer

newBuffer = newTVarIO []

put buffer item = dols <- readTVar bufferwriteTVar buffer (ls ++ [item])

42PPHT09 – TM

get buffer = dols <- readTVar buffercase ls of

[] -> retry(item:rest) -> do

writeTVar buffer restreturn item

Page 8: Convoying Non-compositionality · 2009-10-09 · • Still a hot research topic but already useful PPHT09 – TM 9 Software Transactional Memory • Access for the programmer As a

8

A Shared Counter Example

• We need to wait until all the threads stop◦ Use a buffer as a channel for termination messages

main =do counter <- newTVarIO 0

j i B ff

43PPHT09 – TM

join <- newBufferforkIO ((loop 100000 counter)

`finally`atomically (put join ()))

loop 100000 counteratomically (get join)val <- atomically (readTVar counter)putStrLn (show val)

Laziness Issues

• Lazy evaluation means expressions are only evaluated when their result is needed◦ Stores unevaluated expressions

◦ Possibly high memory use

◦ The shared counter example can overflow stack

44PPHT09 – TM

update counter =do v <- readTVar counter

writeTVar counter (v+1)

Laziness Issues

• Strictly evaluating expressions to be stored in a shared variable◦ Only values are stored

◦ Constant space

◦ A small change to the program is necessary:

45PPHT09 – TM

update counter =do v <- readTVar counter

writeTVar counter $! (v+1)

Compositionality

• (Sequentially) composing transactions is embarrassingly simple:

transfer buffer1 buffer2 = doitem <- get buffer1

• Transactions can be freely composed◦ They are only executed (“locked-down”) with the atomically call

46PPHT09 – TM

item < get buffer1put buffer2 item

Composing Alternatives

• The workings of orElse◦ Take two transactions

◦ Execute the first one, and if it succeed commit

◦ If the first one retries execute the second one

• Can be used to listen to several channels (buffers) at once◦ Similar to the JR input statement

47PPHT09 – TM

getEither buffer1 buffer2 =get buffer1 `orElse` get buffer2

Composing Alternatives

• retry allows to construct blocking functions

• orElse provides an elegant way to construct corresponding non-blocking functions

48PPHT09 – TM

tryGet :: Buffer a -> STM (Maybe a)tryGet buffer =

do item <- get bufferreturn (Just item)

`orElse`return Nothing

Page 9: Convoying Non-compositionality · 2009-10-09 · • Still a hot research topic but already useful PPHT09 – TM 9 Software Transactional Memory • Access for the programmer As a

9

Transactions — Benefits

• Many processes can be in the critical section at the same time◦ Potentially more parallelism

◦ They only need to re-run if there is an actual i fliruntime conflict

• Deadlocks cannot occur

• Easy to compose — at least in Haskell◦ Compose as you like

◦ Commit is only done when we run the transaction

49PPHT09 – TM

Transactions — Drawbacks

• Hard to guarantee fairness◦ A large transaction can be starved by many small

transactions

• All the (log) book keeping can be expensive

• Possibly poor performance in certain race-heavy work loads◦ But this is implementation dependent

• Side effects must be well controlled◦ An expressive type system can be an advantage

50PPHT09 – TM

Transactional Memory — Summary

• Advantages◦ Conceptually simple

◦ Expressivness

◦ No deadlocks

◦ Compositionality• In some variants as for example Haskell

• Question marks◦ Fairness

◦ Performance

◦ I/O51PPHT09 – TM

Next Week

• Monday◦ Urban Boquist from Ericsson:

Industrial use of Erlang

• Wednesday◦ What to expect on the exam

• Example questions

• Course overview — what you should know by now

52PPHT09 – TM