Top Banner
Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007
178

Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

Dec 16, 2015

Download

Documents

Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

Concurrent Queues and Stacks

The Art of Multiprocessor Programming

Spring 2007

Page 2: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 2

Last Lecture

• Five approaches to concurrent data structure design: – Coarse-grained locking– Fine-grained locking– Optimistic synchronization– Lazy synchronization– Lock-free synchronization

Page 3: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 3

List-based Set

• We used an ordered list to implement a Set:– an unordered collection of objects– No duplicates– Methods

• Add() a new object• Remove() an object• Test if set Contains() object

Page 4: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 4

Course Grained Locking

a b d

c

Simple but hotspot + bottleneck

Page 5: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 5

Fine Grained (Lock-Coupling)

a b d

Allows concurency but everyonealways delayed by front guy =hotspots + bottleneck

Page 6: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 6

Optimistic List

b c ea

1. Limited Hotspots (Only at locked Add(), Remove(), Find() destination locations, not traversals)

2. But two traversals3. Yet traversals are wait-free!

Page 7: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 7

Lazy List

a 0 0 0a b c 0e1d

Lazy Add() and Remove() + Wait-free Contains()

Page 8: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 8

Lock-free List

a 0 0 0a b c 0e1c

1. Add() and Remove() physically remove marked nodes

2. Wait-free find() traverses both marked and removed nodes

Page 9: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 9

Today: Another Fundamental Problem

• We told you about – Sets implemented using linked lists

• Next: queues– Ubiquitous data structure– Often used to buffer requests …

Page 10: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 10

Shared Pools

• Queue belongs to broader pool class

• Pool: similar to Set but – Allows duplicates (it’s a Multiset)– No membership test (no Contains())

Page 11: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 11

Pool Flavors

• Bounded– Fixed capacity– Good when resources an issue

• Unbounded– Holds any number of objects

Page 12: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 12

Pool Flavors

• Problem cases:– Removing from empty pool– Adding to full (bounded) pool

• Blocking– Caller waits until state changes

• Non-Blocking– Method throws exception

Page 13: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 13

Queues & Stacks

• Add() and Remove(): Queue enqueue (Enq ()) and dequeue (Deq ()) Stack push and pop

• A Queue is a pool with FIFO order on enqueues and dequeues

• A Stack is a pool with LIFO order on pushes and pops

Page 14: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 14

This Lecture

• Bounded, Blocking, Lock-based Queue

• Unbounded, Non-Blocking, Lock-free Queue

• Examine effects of ABA problem• Unbounded Non-Blocking Lock-free

Stack• Elimination-Backoff Stack

Page 15: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 15

Queue: Concurrency

enq(x) y=deq()

enq() and deq() work at

different ends of the object

tail head

Page 16: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 16

Concurrency

enq(x)

Challenge: what if the queue is empty or full?

y=deq()ta

ilhead

Page 17: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 17

Bounded Queue

Sentinel

head

tail

Page 18: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 18

Bounded Queue

head

tail

First actual item

Page 19: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 19

Bounded Queue

head

tail

Lock out other deq() calls

deqLock

Page 20: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 20

Bounded Queue

head

tail

Lock out other enq() calls

deqLock

enqLock

Page 21: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 21

Not Done Yet

head

tail

deqLock

enqLock

Need to tell whether queue is

full or empty

Page 22: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 22

Not Done Yet

head

tail

deqLock

enqLock

Permission to enqueue 8 items

permits

8

Page 23: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 23

Not Done Yet

head

tail

deqLock

enqLock

Incremented by deq()Decremented by enq()

permits

8

Page 24: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 24

Enqueuer

head

tail

deqLock

enqLock

permits

8

Lock enqLock

Page 25: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 25

Enqueuer

head

tail

deqLock

enqLock

permits

8

Read permits

OK

Page 26: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 26

Enqueuer

head

tail

deqLock

enqLock

permits

8

No need to lock

tail

Page 27: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 27

Enqueuer

head

tail

deqLock

enqLock

permits

8

Enqueue Node

Page 28: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 28

Enqueuer

head

tail

deqLock

enqLock

permits

87

getAndDecrement()

Page 29: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 29

Enqueuer

head

tail

deqLock

enqLock

permits

8 Release lock7

Page 30: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 30

Enqueuer

head

tail

deqLock

enqLock

permits

7

If queue was empty, notify waiting

dequeuers

Page 31: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 31

Unsuccesful Enqueuer

head

tail

deqLock

enqLock

permits

0

Uh-oh

Read permits

Page 32: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 32

Dequeuer

head

tail

deqLock

enqLock

permits

8

Lock deqLock

Page 33: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 33

Dequeuer

head

tail

deqLock

enqLock

permits

7

Read sentinel’s next field

OK

Page 34: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 34

Dequeuer

head

tail

deqLock

enqLock

permits

7

Read value

Page 35: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 35

Dequeuer

head

tail

deqLock

enqLock

permits

7

Make first Node new sentinel

Page 36: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 36

Dequeuer

head

tail

deqLock

enqLock

permits

7Release deqLock

Page 37: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 37

Dequeuer

head

tail

deqLock

enqLock

permits

8

Increment permits

Page 38: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 38

Unsuccesful Dequeuer

head

tail

deqLock

enqLock

permits

8

Read sentinel’s next field

uh-oh

Page 39: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 39

Bounded Queue

public class BoundedQueue<T> { ReentrantLock enqLock, deqLock; Condition notEmptyCondition, notFullCondition; AtomicInteger permits; Node head; Node tail; int capacity; enqLock = new ReentrantLock(); notFullCondition = enqLock.newCondition(); deqLock = new ReentrantLock(); notEmptyCondition = deqLock.newCondition();}

Page 40: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 40

Bounded Queue

public class BoundedQueue<T> { ReentrantLock enqLock, deqLock; Condition notEmptyCondition, notFullCondition; AtomicInteger permits; Node head; Node tail; int capacity; enqLock = new ReentrantLock(); notFullCondition = enqLock.newCondition(); deqLock = new ReentrantLock(); notEmptyCondition = deqLock.newCondition();}

Enq & deq locks

Page 41: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 41

Monitor Locks

• The Reentrant Lock is a monitor

• Allows blocking on a condition rather than spinning

• Threads: – acquire and release lock– wait on a condition

Page 42: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 42

Java Monitor Locks

public interface Lock { void lock(); void lockInterruptibly() throw InterruptedException; boolean tryLock(); boolean tryLock(long time, TimeUnit unit); Condition newCondition(); void unlock;}

Page 43: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 43

Java Locks

public interface Lock { void lock(); void lockInterruptibly() throws InterruptedException; boolean tryLock(); boolean tryLock(long time, TimeUnit unit); Condition newCondition(); void unlock;}

Acquire lock

Page 44: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 44

Java Locks

public interface Lock { void lock(); void lockInterruptibly() throws InterruptedException; boolean tryLock(); boolean tryLock(long time, TimeUnit unit); Condition newCondition(); void unlock;}

Release lock

Page 45: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 45

Java Locks

public interface Lock { void lock(); void lockInterruptibly() throws InterruptedException; boolean tryLock(); boolean tryLock(long time, TimeUnit unit); Condition newCondition(); void unlock;}

Conditions to wait on

Page 46: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 46

Lock Conditions

public interface Condition { void await() throws InterruptedException; boolean await(long time, TimeUnit unit) throws InterruptedException; … void signal(); void signalAll(); }

Page 47: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 47

Lock Conditions

public interface Condition { void await() throws InterruptedException; boolean await(long time, TimeUnit unit) throws InterruptedException; … void signal(); void signalAll(); }

Release lock and wait on condition

Page 48: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 48

Lock Conditions

public interface Condition { void await() throws InterruptedException; boolean await(long time, TimeUnit unit) throws InterruptedException; … void signal(); void signalAll(); }

Signal release of next thread in line orall awaiting threads

Page 49: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 49

The await() Method

• Releases lock on q• Sleeps (gives up processor)• Awakens (resumes running)• Reacquires lock & returns

q.await()

Page 50: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 50

The signal() Method

• Awakens one waiting thread• Which will reacquire lock • Then returns

q.signal();

Page 51: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 51

The signalAll() Method

• Awakens all waiting threads• Which will reacquire lock • Then returns

q.signalAll();

Page 52: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 52

A Monitor Lock

Cri

tical S

ecti

on

waiting roomLock(

)

unLock()

Page 53: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 53

Awaiting a Condition

Cri

tical S

ecti

on

waiting roomLock(

)

Lock()

await()

await()

Page 54: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 54

Monitor Signalling

Cri

tical S

ecti

on

waiting room

Lock()

unLock()

Signal()

I will try to enter

Notice, woken thread might still loose lock to outside contender…

Page 55: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 55

Monitor Signaling All

Cri

tical S

ecti

on

waiting room

SignalAll()

Any one of us can

try to enter

Page 56: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 56

Java Synchronized Monitor

• await() is wait()• signal() is notify() • signalAll() is notifyAll()

Page 57: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 57

Back to our Bounded Queue

public class BoundedQueue<T> { ReentrantLock enqLock, deqLock; Condition notEmptyCondition, notFullCondition; AtomicInteger permits; Node head; Node tail; int capacity; enqLock = new ReentrantLock(); notFullCondition = enqLock.newCondition(); deqLock = new ReentrantLock(); notEmptyCondition = deqLock.newCondition();}

Page 58: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 58

Bounded Queue

public class BoundedQueue<T> { ReentrantLock enqLock, deqLock; Condition notEmptyCondition, notFullCondition; AtomicInteger permits; Node head; Node tail; int capacity; enqLock = new ReentrantLock(); notFullCondition = enqLock.newCondition(); deqLock = new ReentrantLock(); notEmptyCondition = deqLock.newCondition();}

Enq & deq locks

Page 59: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 59

Bounded Queue

public class BoundedQueue<T> { ReentrantLock enqLock, deqLock; Condition notEmptyCondition, notFullCondition; AtomicInteger permits; Node head; Node tail; int capacity; enqLock = new ReentrantLock(); notFullCondition = enqLock.newCondition(); deqLock = new ReentrantLock(); notEmptyCondition = deqLock.newCondition();}

Reentrant lock can have a condition for threads to wait on

Page 60: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 60

Bounded Queue

public class BoundedQueue<T> { ReentrantLock enqLock, deqLock; Condition notEmptyCondition, notFullCondition; AtomicInteger permits; Node head; Node tail; int capacity; enqLock = new ReentrantLock(); notFullCondition = enqLock.newCondition(); deqLock = new ReentrantLock(); notEmptyCondition = deqLock.newCondition();}

Num of permits ranges from 0 to capacity

Page 61: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 61

Bounded Queue

public class BoundedQueue<T> { ReentrantLock enqLock, deqLock; Condition notEmptyCondition, notFullCondition; AtomicInteger permits; Node head; Node tail; int capacity; enqLock = new ReentrantLock(); notFullCondition = enqLock.newCondition(); deqLock = new ReentrantLock(); notEmptyCondition = deqLock.newCondition();}

Head and Tail

Page 62: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 62

Bounded Queue Enq Part 1public void enq(T x) { boolean mustWakeDequeuers = false; enqLock.lock(); try { while (permits.get() == 0){ try {notFullCondition.await} } Node e = new Node(x); tail.next = e; tail = e; if (permits.getAndDecrement() == capacity) { mustWakeDequeuers = true; } } finally { enqLock.unlock(); } …

Page 63: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 63

Bounded Queue Enq Part 1public void enq(T x) { boolean mustWakeDequeuers = false; enqLock.lock(); try { while (permits.get() == 0){ try {notFullCondition.await} } Node e = new Node(x); tail.next = e; tail = e; if (permits.getAndDecrement() == capacity) { mustWakeDequeuers = true; } } finally { enqLock.unlock(); } …

Lock enq lock

Page 64: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 64

public void enq(T x) { boolean mustWakeDequeuers = false; enqLock.lock(); try { while (permits.get() == 0){ try {notFullCondition.await} } Node e = new Node(x); tail.next = e; tail = e; if (permits.getAndDecrement() == capacity) { mustWakeDequeuers = true; } } finally { enqLock.unlock(); } …

Bounded Queue Enq Part 1

If permits = 0 wait till notFullCondition becomes true

then check permits again…

Page 65: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 65

public void enq(T x) { boolean mustWakeDequeuers = false; enqLock.lock(); try { while (permits.get() == 0){ try {notFullCondition.await} } Node e = new Node(x); tail.next = e; tail = e; if (permits.getAndDecrement() == capacity) { mustWakeDequeuers = true; } } finally { enqLock.unlock(); } …

Bounded Queue Enq Part 1

Add a new node

Page 66: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 66

public void enq(T x) { boolean mustWakeDequeuers = false; enqLock.lock(); try { while (permits.get() == 0){ try {notFullCondition.await} } Node e = new Node(x); tail.next = e; tail = e; if (permits.getAndDecrement() == capacity) { mustWakeDequeuers = true; } } finally { enqLock.unlock(); } …

Bounded Queue Enq Part 1

If I was the enqueuer that changed queue state from empty to full will

need to wake dequeuers

Page 67: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 67

public void enq(T x) { boolean mustWakeDequeuers = false; enqLock.lock(); try { while (permits.get() == 0){ try {notFullCondition.await} } Node e = new Node(x); tail.next = e; tail = e; if (permits.getAndDecrement() == capacity) { mustWakeDequeuers = true; } } finally { enqLock.unlock(); } …

Bounded Queue Enq Part 1

Release the enq lock

Page 68: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 68

Bounded Queue Enq Part 2

public void enq(T x) { … if (mustWakeDequeuers) { deqLock.lock(); try { notEmptyCondition.signalAll(); } finally { deqLock.unlock(); } } }

Page 69: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 69

public void enq(T x) { … if (mustWakeDequeuers) { deqLock.lock(); try { notEmptyCondition.signalAll(); } finally { deqLock.unlock(); } } }

Bounded Queue Enq Part 2

To let the dequeuers know that the queue is non-empty, acquire deqLock

Page 70: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 70

public void enq(T x) { … if (mustWakeDequeuers) { deqLock.lock(); try { notEmptyCondition.signalAll(); } finally { deqLock.unlock(); } } }

Bounded Queue Enq Part 2

Signal all dequeuer waiting that they can attempt to re-acquire deqLock

Page 71: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 71

public void enq(T x) { … if (mustWakeDequeuers) { deqLock.lock(); try { notEmptyCondition.signalAll(); } finally { deqLock.unlock(); } } }

Bounded Queue Enq Part 2

Release deqLock

Page 72: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 72

The Shared Counter

• The enq() and deq() methods– Don’t access the same lock

concurrently– But they still share a counter– Which they both increment or

decrement on every method call– Can we get rid of this bottleneck?

Page 73: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 73

Split the Counter

• The enq() method– Decrements only– Cares only if value is zero

• The deq() method– Increments only– Cares only if value is capacity

Page 74: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 74

Split Counter

• Enqueuer decrements enqSidePermits• Dequeuer increments deqSidePermits• When enqueuer runs out

– Locks deqLock– Transfers permits

• Intermittent synchronization– Not with each method call– Need both locks! (careful …)

Page 75: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 75

A Lock-Free Queue

Sentinel

head

tail

Page 76: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 76

Compare and Set

CAS

Page 77: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 77

Enqueue Step One

head

tail

Enqueue Node

CAS

Page 78: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 78

Enqueue Step Two

head

tail

Enqueue Node

CAS

Page 79: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 79

Enqueue

• These two steps are not atomic• The tail field refers to either

– Actual last Node (good)– Penultimate Node (not so good)

• Be prepared!

Page 80: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 80

Enqueue

• What do you do if you find– A trailing tail?

• Stop and fix it– If node pointed to by tail has non-null

next field– CAS the queue’s tail field to tail.next

• Like in the universal construction

Page 81: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 81

When CASs Fail

• In Step One– Retry loop– Method still lock-free (why?)

• In Step Two– Ignore it (why?)

Page 82: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 82

Dequeuer

head

tail

Read value

Page 83: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 83

Dequeuer

head

tail

Make first Node new sentinel

CAS

Page 84: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 84

Memory Reuse?

• What do we do with nodes after we dequeue them?

• Java: let garbage collector deal?• Suppose there isn’t a GC, or we

don’t want to use it?

Page 85: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 85

Dequeuer

head

tail

CAS

Can recycle

Page 86: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 86

Simple Solution

• Each thread has a free list of unused queue nodes

• Allocate node: pop from list• Free node: push onto list• Deal with underflow somehow …

Page 87: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 87

Why Recycling is Hard

Free pool

head tail

Want to rediret

tailfrom

grey to red

zzz…

Page 88: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 88

Why Recycling is Hard

Free pool

zzz

head tail

Page 89: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 89

Why Recycling is Hard

Free pool

Yawn!

head tail

Page 90: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 90

Why Recycling is Hard

Free pool

CAShead tail

OK, here I go!

Page 91: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 91

Final State

Free pool

What went wrong?

head tail

Page 92: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 92

The Dreaded ABA Problem

Head pointer has value AThread reads value A

head tail

Page 93: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 93

Dreaded ABA continued

zzz Head pointer has value BNode A freed

head tail

Page 94: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 94

Dreaded ABA continued

Yawn! Head pointer has value A againNode A recycled & reinitialized

head tail

Page 95: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 95

Dreaded ABA continued

CAS succeeds because pointer matcheseven though pointer’s meaning has changed

CAShead tail

Page 96: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 96

The Dreaded ABA Problem

• Is a result of CAS() semantics (Sun, Intel, AMD)

• Does not arise with Load-Locked/Store-Conditional (IBM)

Page 97: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 97

Dreaded ABA – A Solution

• Tag each pointer with a counter• Unique over lifetime of node• Pointer size vs word size issues• Overflow?

– Don’t worry be happy?– Bounded tags?

• AtomicStampedReference class

Page 98: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 98

A Concurrent Stack

• Add() and Remove() of Stack are called push() and pop()

• A Stack is a pool with LIFO order on pushes and pops

Page 99: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 99

Unbounded Lock-free Stack

Top

Page 100: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 100

Unbounded Lock-free Stack

Top

Page 101: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 101

Push

TopCAS

Page 102: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 102

Push

Top

Page 103: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 103

Push

TopCAS

Page 104: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 104

Push

Top

Page 105: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 105

Push

Top

Page 106: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 106

Push

Top

Page 107: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 107

Push

TopCAS

Page 108: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 108

Push

Top

Page 109: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 109

Push

Top

Page 110: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 110

Pop

TopCAS

Page 111: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 111

Pop

TopCAS

Page 112: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 112

Pop

TopCAS

Page 113: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 113

Pop

Top

Page 114: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 114

public class LockFreeStack { private AtomicReference top = new AtomicReference(null); public void push(T value) { public void tryPush(Node node){ Node oldTop = top.get(); node.next = oldTop; return(top.compareAndSet(oldTop, node)) } Node node = new Node(value); while (true) { if (tryPush(node)) { return; } else backoff.backoff()}}

Lock-free Stack

Page 115: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 115

public class LockFreeStack { private AtomicReference top = new AtomicReference(null); public void push(T value) { public void tryPush(Node node){ Node oldTop = top.get(); node.next = oldTop; return(top.compareAndSet(oldTop, node)) } Node node = new Node(value); while (true) { if (tryPush(node)) { return; } else backoff.backoff()}}

Lock-free Stack

Push uses tryPush method

Page 116: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 116

public class LockFreeStack { private AtomicReference top = new AtomicReference(null); public void push(T value) { public void tryPush(Node node){ Node oldTop = top.get(); node.next = oldTop; return(top.compareAndSet(oldTop, node)) } Node node = new Node(value); while (true) { if (tryPush(node)) { return; } else backoff.backoff()}}

Lock-free Stack

Create a new node

Page 117: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 117

public class LockFreeStack { private AtomicReference top = new AtomicReference(null); public void push(T value) { public void tryPush(Node node){ Node oldTop = top.get(); node.next = oldTop; return(top.compareAndSet(oldTop, node)) } Node node = new Node(value); while (true) { if (tryPush(node)) { return; } else backoff.backoff()}}

Lock-free Stack

Then try to push: if tryPush fails back-off before retrying

Page 118: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 118

public class LockFreeStack { private AtomicReference top = new AtomicReference(null); public void push(T value) { public void tryPush(Node node){ Node oldTop = top.get(); node.next = oldTop; return(top.compareAndSet(oldTop, node)) } Node node = new Node(value); while (true) { if (tryPush(node)) { return; } else backoff.backoff()}}

Lock-free Stack

tryPush attempts to push a node at top

Page 119: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 119

public class LockFreeStack { private AtomicReference top = new AtomicReference(null); public void push(T value) { public void tryPush(Node node){ Node oldTop = top.get(); node.next = oldTop; return(top.compareAndSet(oldTop, node)) } Node node = new Node(value); while (true) { if (tryPush(node)) { return; } else backoff.backoff()}}

Lock-free Stack

Read top value

Page 120: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 120

public class LockFreeStack { private AtomicReference top = new AtomicReference(null); public void push(T value) { public void tryPush(Node node){ Node oldTop = top.get(); node.next = oldTop; return(top.compareAndSet(oldTop, node)) } Node node = new Node(value); while (true) { if (tryPush(node)) { return; } else backoff.backoff()}}

Lock-free Stack

current top will be new node’s successor

Page 121: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 121

public class LockFreeStack { private AtomicReference top = new AtomicReference(null); public void push(T value) { public void tryPush(Node node){ Node oldTop = top.get(); node.next = oldTop; return(top.compareAndSet(oldTop, node)) } Node node = new Node(value); while (true) { if (tryPush(node)) { return; } else backoff.backoff()}}

Lock-free Stack

Try to swing top to point at my new node

Page 122: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 122

Lock-free Stack

• Good: No locking • Bad: if no GC then ABA as in queue

(add time stamps)• Bad: Contention on top (add

backoff)• Bad: No parallelism• Is a stack inherently sequential?

Page 123: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 123

Elimination-Backoff Stack

• How to “turn contention into parallelism”

• Replace regular exponential-backoff

• with an alternative elimination-backoff mechanism

Page 124: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 124

Observation

Push( )

Pop()

linearizable stack

After any equal number of pushes and pops, stack stays the same

Page 125: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 125

Idea: Elimination Array

Push( )

Pop()

stack

Pick at random

Pick at random

Elimination Array

Page 126: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 126

Push Collides With Pop

Push( )

Pop()

stack

continue

continue

No need to access stack

Page 127: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 127

No Collision

Push( )

Pop()

stack

If no collision, access stack

Pop()

If pushes collide or pops collide access stack

Page 128: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 128

Elimination-Backoff Stack

• Lock-free stack + elimination array• Access Lock-free stack,

– If uncontended, apply operation – if contended, back off to elimination

array and attempt elimination

Page 129: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 129

Elimination-Backoff Stack

Push( )

Pop()TopCAS

If failed CAS back-off

Page 130: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 130

Dynamic Range and Delay

Push( )

Pick random range and max time to wait for collision based on level ofcontention encountered

Page 131: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 131

Linearizability

• Un-eliminated Lock-free stack calls: linearized as before

• Eliminated calls: linearize push immediately after the pop at the collision point

• Combination is a linearizable stack

Page 132: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 132

Backoff Has Dual Affect

• Elimination introduces parallelism• Backoff onto array cuts contention

on lock-free stack• Elimination in array cuts down

total number of threads ever accessing lock-free stack

Page 133: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 133

public class EliminationArray { private static final int duration = ...; private static final int timeUnit = ...; Exchanger<T>[] exchanger; Random random; public EliminationArray(int capacity) { exchanger = (Exchanger<T>[]) new Exchanger[capacity]; for (int i = 0; i < capacity; i++) { exchanger[i] = new Exchanger<T>(); } random = new Random(); } …}

Elimination Array

Page 134: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 134

public class EliminationArray { private static final int duration = ...; private static final int timeUnit = ...; Exchanger<T>[] exchanger; Random random; public EliminationArray(int capacity) { exchanger = (Exchanger<T>[]) new Exchanger[capacity]; for (int i = 0; i < capacity; i++) { exchanger[i] = new Exchanger<T>(); } random = new Random(); } …}

Elimination Array

An array of exchangers

Page 135: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 135

public class NBExchanger<T> { AtomicStampedReference<T> slot = new AtomicStampedReference<T>(null, 0);

A Lock-Free Exchanger

Page 136: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 136

public class NBExchanger<T> { AtomicStampedReference<T> slot = new AtomicStampedReference<T>(null, 0);

A Lock-Free Exchanger

Slot holds atomically modifiable reference and time stamp

Page 137: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 137

Atomic Stamped Reference

• AtomicStampedReference class– Java.util.concurrent.atomic package

address S

Stamp

Reference

Page 138: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 138

Extracting Reference & Stamp

Public T get(int[] stampHolder);

Page 139: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 139

Extracting Reference & Stamp

Public T get(int[] stampHolder);

Returns reference to object of type T

Returns stamp at array index

0!

Page 140: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 140

public T Exchange(T myItem, long nanos) throws TimeoutException { long timeBound = System.nanoTime() + nanos; int[] stampHolder = {0}; while (true) { if (System.nanoTime() > timeBound) throw new TimeoutException(); T herItem = slot.get(stampHolder); int stamp = stampHolder[0]; switch(stamp % 3) { case 0: // slot is free case 1: // someone waiting for me case 2: // others exchanging } }}

The Exchange

Page 141: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 141

public T Exchange(T myItem, long nanos) throws TimeoutException { long timeBound = System.nanoTime() + nanos; int[] stampHolder = {0}; while (true) { if (System.nanoTime() > timeBound) throw new TimeoutException(); T herItem = slot.get(stampHolder); int stamp = stampHolder[0]; switch(stamp % 3) { case 0: // slot is free case 1: // someone waiting for me case 2: // others exchanging } }}

The Exchange

Input item and max time to wait for exchange before timing out

Page 142: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 142

public T Exchange(T myItem, long nanos) throws TimeoutException { long timeBound = System.nanoTime() + nanos; int[] stampHolder = {0}; while (true) { if (System.nanoTime() > timeBound) throw new TimeoutException(); T herItem = slot.get(stampHolder); int stamp = stampHolder[0]; switch(stamp % 3) { case 0: // slot is free case 1: // someone waiting for me case 2: // others exchanging } }}

The Exchange

Array to hold extracted timestamp

Page 143: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 143

public T Exchange(T myItem, long nanos) throws TimeoutException { long timeBound = System.nanoTime() + nanos; int[] stampHolder = {0}; while (true) { if (System.nanoTime() > timeBound) throw new TimeoutException(); T herItem = slot.get(stampHolder); int stamp = stampHolder[0]; switch(stamp % 3) { case 0: // slot is free case 1: // someone waiting for me case 2: // others exchanging } }}

The Exchange

Loop as long as time to attempt exchange does not run out

Page 144: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 144

public T Exchange(T myItem, long nanos) throws TimeoutException { long timeBound = System.nanoTime() + nanos; int[] stampHolder = {0}; while (true) { if (System.nanoTime() > timeBound) throw new TimeoutException(); T herItem = slot.get(stampHolder); int stamp = stampHolder[0]; switch(stamp % 3) { case 0: // slot is free case 1: // someone waiting for me case 2: // others exchanging } }}

The Exchange

Get others item and time-stamp

Page 145: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 145

public T Exchange(T myItem, long nanos) throws TimeoutException { long timeBound = System.nanoTime() + nanos; int[] stampHolder = {0}; while (true) { if (System.nanoTime() > timeBound) throw new TimeoutException(); T herItem = slot.get(stampHolder); int stamp = stampHolder[0]; switch(stamp % 3) { case 0: // slot is free case 1: // someone waiting for me case 2: // others exchanging } }}

The ExchangeExchanger slot has three states determined by the timestamp mod 3

Page 146: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 146

Lock-free Exchanger

SlotState = 0

item stamp/state

0CAS

Page 147: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 147

Lock-free Exchanger

Slot

State changed to 1 wait for someone to

appear…

item stamp/state

1

Page 148: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 148

Lock-free Exchanger

Slot

Still waiting for someone to appear…

item stamp/state

2CAS

Try to exchange

item and set state to 2

Page 149: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 149

Lock-free Exchanger

Slot

2 means someone

showed up, take

item and reset to 0

item stamp/state

2

Page 150: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 150

Lock-free Exchanger

Slot

Read item and increment

timestamp to 0 mod 3

item stamp/state

20

Page 151: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 151

case 0: // slot is free if (slot.compareAndSet(herItem, myItem, stamp, stamp + 1)) { while (System.nanoTime() < timeBound){ herItem = slot.get(stampHolder); if (stampHolder[0] == stamp + 2) { slot.set(null, stamp + 3); return herItem; }} if (slot.compareAndSet(myItem, null, stamp + 1, stamp)) {throw new TimeoutException(); } else { herItem = slot.get(stampHolder); slot.set(null, stamp + 3); return herItem; }} break;

Exchanger State 0

Page 152: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 152

case 0: // slot is free if (slot.compareAndSet(herItem, myItem, stamp, stamp + 1)) { while (System.nanoTime() < timeBound){ herItem = slot.get(stampHolder); if (stampHolder[0] == stamp + 2) { slot.set(null, stamp + 3); return herItem; }} if (slot.compareAndSet(myItem, null, stamp + 1, stamp)) {throw new TimeoutException(); } else { herItem = slot.get(stampHolder); slot.set(null, stamp + 3); return herItem; }} break;

Exchanger State 0

Slot is free, try and insert myItem and change state to 1

Page 153: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 153

case 0: // slot is free if (slot.compareAndSet(herItem, myItem, stamp, stamp + 1)) { while (System.nanoTime() < timeBound){ herItem = slot.get(stampHolder); if (stampHolder[0] == stamp + 2) { slot.set(null, stamp + 3); return herItem; }} if (slot.compareAndSet(myItem, null, stamp + 1, stamp)) {throw new TimeoutException(); } else { herItem = slot.get(stampHolder); slot.set(null, stamp + 3); return herItem; }} break;

Exchanger State 0

Loop while still time left to try and exchange

Page 154: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 154

case 0: // slot is free if (slot.compareAndSet(herItem, myItem, stamp, stamp + 1)) { while (System.nanoTime() < timeBound){ herItem = slot.get(stampHolder); if (stampHolder[0] == stamp + 2) { slot.set(null, stamp + 3); return herItem; }} if (slot.compareAndSet(myItem, null, stamp + 1, stamp)) {throw new TimeoutException(); } else { herItem = slot.get(stampHolder); slot.set(null, stamp + 3); return herItem; }} break;

Exchanger State 0

Get item and stamp in slot and check if state changed to 2

Page 155: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 155

case 0: // slot is free if (slot.compareAndSet(herItem, myItem, stamp, stamp + 1)) { while (System.nanoTime() < timeBound){ herItem = slot.get(stampHolder); if (stampHolder[0] == stamp + 2) { slot.set(null, stamp + 3); return herItem; }} if (slot.compareAndSet(myItem, null, stamp + 1, stamp)) {throw new TimeoutException(); } else { herItem = slot.get(stampHolder); slot.set(null, stamp + 3); return herItem; }} break;

Exchanger State 0

If successful reset slot state to 0

Page 156: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 156

case 0: // slot is free if (slot.compareAndSet(herItem, myItem, stamp, stamp + 1)) { while (System.nanoTime() < timeBound){ herItem = slot.get(stampHolder); if (stampHolder[0] == stamp + 2) { slot.set(null, stamp + 3); return herItem; }} if (slot.compareAndSet(myItem, null, stamp + 1, stamp)) {throw new TimeoutException(); } else { herItem = slot.get(stampHolder); slot.set(null, stamp + 3); return herItem; }} break;

Exchanger State 0

and return item found in slot

Page 157: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 157

case 0: // slot is free if (slot.compareAndSet(herItem, myItem, stamp, stamp + 1)) { while (System.nanoTime() < timeBound){ herItem = slot.get(stampHolder); if (stampHolder[0] == stamp + 2) { slot.set(null, stamp + 3); return herItem; }} if (slot.compareAndSet(myItem, null, stamp + 1, stamp)) {throw new TimeoutException(); } else { herItem = slot.get(stampHolder); slot.set(null, stamp + 3); return herItem; }} break;

Exchanger State 0

Otherwise we ran out of time, try and reset state to 0, if successful time out

Page 158: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 158

case 0: // slot is free if (slot.compareAndSet(herItem, myItem, stamp, stamp + 1)) { while (System.nanoTime() < timeBound){ herItem = slot.get(stampHolder); if (stampHolder[0] == stamp + 2) { slot.set(null, stamp + 3); return herItem; }} if (slot.compareAndSet(myItem, null, stamp + 1, stamp)) {throw new TimeoutException(); } else { herItem = slot.get(stampHolder); slot.set(null, stamp + 3); return herItem; }} break;

Exchanger State 0

If reset failed can only be that someone showed up after all, take her item

Page 159: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 159

case 0: // slot is free if (slot.compareAndSet(herItem, myItem, stamp, stamp + 1)) { while (System.nanoTime() < timeBound){ herItem = slot.get(stampHolder); if (stampHolder[0] == stamp + 2) { slot.set(null, stamp + 3); return herItem; }} if (slot.compareAndSet(myItem, null, stamp + 1, stamp)) {throw new TimeoutException(); } else { herItem = slot.get(stampHolder); slot.set(null, stamp + 3); return herItem; }} break;

Exchanger State 0

Set slot to 0 with new time stamp and return the item found

Page 160: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 160

case 0: // slot is free if (slot.compareAndSet(herItem, myItem, stamp, stamp + 1)) { while (System.nanoTime() < timeBound){ herItem = slot.get(stampHolder); if (stampHolder[0] == stamp + 2) { slot.set(null, stamp + 3); return herItem; }} if (slot.compareAndSet(myItem, null, stamp + 1, stamp)) {throw new TimeoutException(); } else { herItem = slot.get(stampHolder); slot.set(null, stamp + 3); return herItem; }} break;

Exchanger State 0

If initial CAS failed then someone else changed slot from 0 to 1 so retry from start

Page 161: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 161

case 1: // someone waiting for me if (slot.compareAndSet(herItem, myItem, stamp, stamp + 1)) return herItem; break;case 2: // others in middle of exchanging break;default: // impossible break; } } }}

Exchanger States 1 and 2

Page 162: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 162

case 1: // someone waiting for me if (slot.compareAndSet(herItem, myItem, stamp, stamp + 1)) return herItem; break;case 2: // others in middle of exchanging break;default: // impossible break; } } }}

Exchanger States 1 and 2

state 1 means someone is waiting for an exchange, so attempt to CAS my Item in and change state to 2

Page 163: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 163

case 1: // someone waiting for me if (slot.compareAndSet(herItem, myItem, stamp, stamp + 1)) return herItem; break;case 2: // others in middle of exchanging break;default: // impossible break; } } }}

Exchanger States 1 and 2

If successful return her item, state is now 2, otherwise someone else took her item so try again from start

Page 164: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 164

case 1: // someone waiting for me if (slot.compareAndSet(herItem, myItem, stamp, stamp + 1)) return herItem; break;case 2: // others in middle of exchanging break;default: // impossible break; } } }}

Exchanger States 1 and 2

If state is 2 then some other threads are using slot to exchange so start again

Page 165: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 165

Our Exchanger Slot

• Notice that we showed a general lock-free exchanger

• Its lock-free because the only way an exchange can fail is if others repeatedly succeeded or no-one showed up

• The slot we need does not require symmetric exchange

Page 166: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 166

public class EliminationArray {…public T visit(T value, int Range) throws TimeoutException { int slot = random.nextInt(Range return (exchanger[slot].exchange(value, duration, timeUnit)) }}

Elimination Array

Page 167: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 167

public class EliminationArray {…public T visit(T value, int Range) throws TimeoutException { int slot = random.nextInt(Range) return (exchanger[slot].exchange(value, duration)) }}

Elimination Array

visit the elimination array with a value and a range (duration to wait is not dynamic)

Page 168: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 168

public class EliminationArray {…public T visit(T value, int Range) throws TimeoutException { int slot = random.nextInt(Range return (exchanger[slot].exchange(value, duration)) }}

Elimination Array

Pick a random array entry

Page 169: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 169

public class EliminationArray {…public T visit(T value, int Range) throws TimeoutException { int slot = random.nextInt(Range return (exchanger[slot].exchange(value, duration)) }}

Elimination Array

Exchange value or time out

Page 170: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 170

public void push(T value) {... while (true) { if (tryPush(node)) { return; } else try { T otherValue = eliminationArray.visit(value,policy.Range); if (otherValue == null) { return; }}

Elimination Stack Push

Page 171: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 171

public void push(T value) {... while (true) { if (tryPush(node)) { return; } else try { T otherValue = eliminationArray.visit(value,policy.Range); if (otherValue == null) { return; }}

Elimination Stack Push

First try to push

Page 172: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 172

public void push(T value) {... while (true) { if (tryPush(node)) { return; } else try { T otherValue = eliminationArray.visit(value,policy.Range); if (otherValue == null) { return; }}

Elimination Stack Push

If failed back-off to try and eliminate

Page 173: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 173

public void push(T value) {... while (true) { if (tryPush(node)) { return; } else try { T otherValue = eliminationArray.visit(value,policy.Range); if (otherValue == null) { return; }}

Elimination Stack Push

Value being pushed and range to try

Page 174: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 174

public void push(T value) {... while (true) { if (tryPush(node)) { return; } else try { T otherValue = eliminationArray.visit(value,policy.Range); if (otherValue == null) { return; }}

Elimination Stack Push

Only a pop has null value so elimination was successful

Page 175: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 175

public void push(T value) {... while (true) { if (tryPush(node)) { return; } else try { T otherValue = eliminationArray.visit(value,policy.Range); if (otherValue == null) { return; }}

Elimination Stack Push

Else retry push on lock-free stack

Page 176: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 176

public T pop() { ... while (true) { if (tryPop()) { return returnNode.value; } else try { T otherValue = eliminationArray.visit(null,policy.Range; if ( otherValue != null) { return otherValue; } }}}

Elimination Stack Pop

Page 177: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 177

public T pop() { ... while (true) { if (tryPop()) { return returnNode.value; } else try { T otherValue = eliminationArray.visit(null,policy.Range; if ( otherValue != null) { return otherValue; } }}}

Elimination Stack Pop

Same as push, if non-null other thread must have pushed so elimnation succeeds

Page 178: Concurrent Queues and Stacks The Art of Multiprocessor Programming Spring 2007.

© Herlihy-Shavit 2007 178

Summary• We saw both lock-based and lock-

free implementations of • queues and stacks• Don’t be quick to declare a data

structure inherently sequential– Linearizable stack is not inherently

sequential • ABA is a real problem, pay attention