Transcript Chapter 10

Concurrent Queues and Stacks
Companion slides for
The Art of Multiprocessor
Programming
by Maurice Herlihy & Nir Shavit
The Five-Fold Path
•
•
•
•
•
Coarse-grained locking
Fine-grained locking
Optimistic synchronization
Lazy synchronization
Lock-free synchronization
Art of Multiprocessor
Programming© Herlihy-Shavit
2
Another Fundamental Problem
• We told you about
– Sets implemented using linked lists
• Next: queues
• Next: stacks
Art of Multiprocessor
Programming© Herlihy-Shavit
3
Queues & Stacks
• Both: pool of items
• Queue
– enq() & deq()
– First-in-first-out (FIFO) order
• Stack
– push() & pop()
– Last-in-first-out (LIFO) order
Art of Multiprocessor
Programming© Herlihy-Shavit
4
Bounded vs Unbounded
• Bounded
– Fixed capacity
– Good when resources an issue
• Unbounded
– Holds any number of objects
Art of Multiprocessor
Programming© Herlihy-Shavit
5
Blocking vs Non-Blocking
• Problem cases:
– Removing from empty pool
– Adding to full (bounded) pool
• Blocking
– Caller waits until state changes
• Non-Blocking
– Method throws exception
Art of Multiprocessor
Programming© Herlihy-Shavit
6
This Lecture
• Bounded, Blocking, Lock-based Queue
• Unbounded, Non-Blocking, Lock-free
Queue
• ABA problem
• Unbounded Non-Blocking Lock-free
Stack
• Elimination-Backoff Stack
Art of Multiprocessor
Programming© Herlihy-Shavit
7
Queue: Concurrency
enq(x)
tail
head
y=deq()
enq() and deq()
work at different
ends of the object
Art of Multiprocessor
Programming© Herlihy-Shavit
8
Concurrency
y=deq()
enq(x)
Challenge: what if
the queue is empty
or full?
Art of Multiprocessor
Programming© Herlihy-Shavit
9
Bounded Queue
head
tail
Sentinel
Art of Multiprocessor
Programming© Herlihy-Shavit
10
Bounded Queue
head
tail
First actual item
Art of Multiprocessor
Programming© Herlihy-Shavit
11
Bounded Queue
head
tail
deqLock
Lock out other
deq() calls
Art of Multiprocessor
Programming© Herlihy-Shavit
12
Bounded Queue
head
tail
deqLock
enqLock
Lock out other
enq() calls
Art of Multiprocessor
Programming© Herlihy-Shavit
13
Not Done Yet
head
tail
deqLock
enqLock
Need to tell
whether queue is
full or empty
Art of Multiprocessor
Programming© Herlihy-Shavit
14
Not Done Yet
head
tail
deqLock
enqLock
permits
8
Permission to enqueue 8 items
Art of Multiprocessor
Programming© Herlihy-Shavit
15
Not Done Yet
head
tail
deqLock
enqLock
permits
8
Incremented by deq()
Decremented by enq()
Art of Multiprocessor
Programming© Herlihy-Shavit
16
Enqueuer
head
tail
deqLock
Lock enqLock
enqLock
permits
8
Art of Multiprocessor
Programming© Herlihy-Shavit
17
Enqueuer
head
tail
deqLock
Read permits
enqLock
permits
8
OK
Art of Multiprocessor
Programming© Herlihy-Shavit
18
Enqueuer
head
tail
deqLock
No need to
lock tail
enqLock
permits
8
Art of Multiprocessor
Programming© Herlihy-Shavit
19
Enqueuer
head
tail
deqLock
enqLock
Enqueue Node
permits
8
Art of Multiprocessor
Programming© Herlihy-Shavit
20
Enqueuer
head
tail
deqLock
enqLock
permits
8
7
getAndDecrement()
Art of Multiprocessor
Programming© Herlihy-Shavit
21
Enqueuer
head
tail
deqLock
enqLock
permits
Release lock
8
7
Art of Multiprocessor
Programming© Herlihy-Shavit
22
Enqueuer
head
tail
deqLock
enqLock
permits
7
If queue was empty,
notify/signal waiting
dequeuers
Art of Multiprocessor
Programming© Herlihy-Shavit
23
Unsuccesful Enqueuer
head
tail
deqLock
Read permits
enqLock
permits
0
Uh-oh
Art of Multiprocessor
Programming© Herlihy-Shavit
24
Dequeuer
head
tail
deqLock
enqLock
Lock deqLock
permits
8
Art of Multiprocessor
Programming© Herlihy-Shavit
25
Dequeuer
head
tail
deqLock
enqLock
permits
Read sentinel’s
next field
7
OK
Art of Multiprocessor
Programming© Herlihy-Shavit
26
Dequeuer
head
tail
deqLock
Read value
enqLock
permits
7
Art of Multiprocessor
Programming© Herlihy-Shavit
27
Make first Node
new sentinel
Dequeuer
head
tail
deqLock
enqLock
permits
7
Art of Multiprocessor
Programming© Herlihy-Shavit
28
Dequeuer
head
tail
deqLock
Increment
permits
enqLock
permits
8
Art of Multiprocessor
Programming© Herlihy-Shavit
29
Dequeuer
head
tail
deqLock
enqLock
permits
7
Release
deqLock
Art of Multiprocessor
Programming© Herlihy-Shavit
30
Unsuccesful Dequeuer
head
tail
deqLock
enqLock
permits
Read sentinel’s
next field
8
uh-oh
Art of Multiprocessor
Programming© Herlihy-Shavit
31
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();
}
Art of Multiprocessor
Programming© Herlihy-Shavit
32
Bounded Queue
public class BoundedQueue<T> {
ReentrantLock enqLock, deqLock;
Condition notEmptyCondition, notFullCondition;
AtomicInteger permits;
Node head;
Node tail;
int capacity;
Enq & deq locks
enqLock = new ReentrantLock();
notFullCondition = enqLock.newCondition();
deqLock = new ReentrantLock();
notEmptyCondition = deqLock.newCondition();
}
Art of Multiprocessor
Programming© Herlihy-Shavit
33
Digression: Monitor Locks
• Java Synchronized objects and
Java ReentrantLocks are
monitors
• Allow blocking on a condition
rather than spinning
• Threads:
– acquire and release lock
– wait on a condition
Art of Multiprocessor
Programming© Herlihy-Shavit
34
The Java Lock Interface
public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit);
Condition newCondition();
void unlock;
}
Acquire lock
Art of Multiprocessor
Programming© Herlihy-Shavit
35
The Java Lock Interface
public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit);
Condition newCondition();
void unlock;
Release lock
}
Art of Multiprocessor
Programming© Herlihy-Shavit
36
The Java Lock Interface
public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit);
Condition newCondition();
void unlock;
}
Try for lock, but not too hard
Art of Multiprocessor
Programming© Herlihy-Shavit
37
The Java Lock Interface
public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit);
Condition newCondition();
void unlock;
}
Create condition to wait on
Art of Multiprocessor
Programming© Herlihy-Shavit
38
The Java Lock Interface
public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit);
Condition newCondition();
void unlock;
}
Guess what this method does?
Art of Multiprocessor
Programming© Herlihy-Shavit
39
Lock Conditions
public interface Condition {
void await();
boolean await(long time, TimeUnit unit);
…
void signal();
void signalAll();
}
Art of Multiprocessor
Programming© Herlihy-Shavit
40
Lock Conditions
public interface Condition {
void await();
boolean await(long time, TimeUnit unit);
…
void signal();
void signalAll();
Release lock and
}
wait on condition
Art of Multiprocessor
Programming© Herlihy-Shavit
41
Lock Conditions
public interface Condition {
void await();
boolean await(long time, TimeUnit unit);
…
void signal();
void signalAll();
}
Wake up one waiting thread
Art of Multiprocessor
Programming© Herlihy-Shavit
42
Lock Conditions
public interface Condition {
void await();
boolean await(long time, TimeUnit unit);
…
void signal();
void signalAll();
}
Wake up all waiting threads
Art of Multiprocessor
Programming© Herlihy-Shavit
43
Await
q.await()
•
•
•
•
Releases lock associated with q
Sleeps (gives up processor)
Awakens (resumes running)
Reacquires lock & returns
Art of Multiprocessor
Programming© Herlihy-Shavit
44
Signal
q.signal();
• Awakens one waiting thread
– Which will reacquire lock
Art of Multiprocessor
Programming© Herlihy-Shavit
45
Signal All
q.signalAll();
• Awakens all waiting threads
– Which will each reacquire lock
Art of Multiprocessor
Programming© Herlihy-Shavit
46
A Monitor Lock
Critical Section
Lock()
waiting room
unLock()
Art of Multiprocessor
Programming© Herlihy-Shavit
47
Unsuccessful Deq
Deq()
Critical Section
Lock()
waiting room
await()
Oh no,
Empty!
Art of Multiprocessor
Programming© Herlihy-Shavit
48
Another One
Deq()
Critical Section
Lock()
waiting room
await()
Oh no,
Empty!
Art of Multiprocessor
Programming© Herlihy-Shavit
49
Enqueur to the Rescue
Enq(
)
Critical Section
Lock()
Yawn!
Yawn!
waiting room
signalAll()
unLock()
Art of Multiprocessor
Programming© Herlihy-Shavit
50
Monitor Signalling
Yawn!
Yawn!
Critical Section
waiting room
Awakend thread
might still lose lock to
outside contender…
Art of Multiprocessor
Programming© Herlihy-Shavit
51
Dequeurs Signalled
Yawn!
Critical Section
waiting room
Found it
Art of Multiprocessor
Programming© Herlihy-Shavit
52
Dequeurs Signalled
Yawn!
Critical Section
waiting room
Still empty!
Art of Multiprocessor
Programming© Herlihy-Shavit
53
Dollar Short + Day Late
Critical Section
waiting room
Art of Multiprocessor
Programming© Herlihy-Shavit
54
Lost Wake-Up
Enq(
)
Critical Section
Lock()
Yawn!
waiting room
signal ()
unLock()
Art of Multiprocessor
Programming© Herlihy-Shavit
2007
55
Lost Wake-Up
Enq(
)
Critical Section
Lock()
Yawn!
waiting room
unLock()
Art of Multiprocessor
Programming© Herlihy-Shavit
56
Lost Wake-Up
Yawn!
Critical Section
waiting room
Art of Multiprocessor
Programming© Herlihy-Shavit
57
Lost Wake-Up
Critical Section
waiting room
Found it
Art of Multiprocessor
Programming© Herlihy-Shavit
58
What’s Wrong Here?
zzzz….!
Critical Section
waiting room
Art of Multiprocessor
Programming© Herlihy-Shavit
59
Solution to Lost Wakeup
• Always use signalAll and notifyAll
• Not signal and notify
Art of Multiprocessor
Programming© Herlihy-Shavit
60
Java Synchronized Methods
public class Queue<T> {
int head = 0, tail = 0;
T[QSIZE] items;
public synchronized T deq() {
while (tail – head == 0)
this.wait();
T result = items[head % QSIZE]; head++;
this.notifyAll();
return result;
}
…
}}
Art of Multiprocessor
Programming© Herlihy-Shavit
61
Java Synchronized Methods
public class Queue<T> {
int head = 0, tail = 0;
T[QSIZE] items;
public synchronized T deq() {
while (tail – head == 0)
this.wait();
T result = items[head % QSIZE]; head++;
this.notifyAll();
return result;
Each object has an implicit
}
…
lock with an implicit condition
}}
Art of Multiprocessor
Programming© Herlihy-Shavit
62
Java Synchronized Methods
public class Queue<T> {
int head = 0, tail = 0;
T[QSIZE] items;
Lock on entry,
unlock on return
public synchronized T deq() {
while (tail – head == 0)
this.wait();
T result = items[head % QSIZE]; head++;
this.notifyAll();
return result;
}
…
}}
Art of Multiprocessor
Programming© Herlihy-Shavit
63
Java Synchronized Methods
public class Queue<T> {
int head = 0, tail = 0;
T[QSIZE] items;
Wait on implicit
condition
public synchronized T deq() {
while (tail – head == 0)
this.wait();
T result = items[head % QSIZE]; head++;
this.notifyAll();
return result;
}
…
}}
Art of Multiprocessor
Programming© Herlihy-Shavit
64
Java Synchronized Methods
Signal all threads waiting
on condition
tail = 0;
public class Queue<T> {
int head = 0,
T[QSIZE] items;
public synchronized T deq() {
while (tail – head == 0)
this.wait();
T result = items[head % QSIZE]; head++;
this.notifyAll();
return result;
}
…
}}
Art of Multiprocessor
Programming© Herlihy-Shavit
65
(Pop!) The 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();
}
Art of Multiprocessor
Programming© Herlihy-Shavit
66
Bounded Queue Fields
public class BoundedQueue<T> {
ReentrantLock enqLock, deqLock;
Condition notEmptyCondition, notFullCondition;
AtomicInteger permits;
Node head;
Node tail;
int capacity;
Enq & deq locks
enqLock = new ReentrantLock();
notFullCondition = enqLock.newCondition();
deqLock = new ReentrantLock();
notEmptyCondition = deqLock.newCondition();
}
Art of Multiprocessor
Programming© Herlihy-Shavit
67
Bounded Queue Fields
public class BoundedQueue<T> {
ReentrantLock enqLock, deqLock;
Condition notEmptyCondition, notFullCondition;
AtomicInteger permits;
Enq lock’s associated
Node head;
Node tail;
condition
int capacity;
enqLock = new ReentrantLock();
notFullCondition = enqLock.newCondition();
deqLock = new ReentrantLock();
notEmptyCondition = deqLock.newCondition();
}
Art of Multiprocessor
Programming© Herlihy-Shavit
68
Bounded Queue Fields
public class BoundedQueue<T> {
ReentrantLock enqLock, deqLock;
Condition notEmptyCondition, notFullCondition;
AtomicInteger permits;
Node head;
Node tail;
Num permits: 0 to capacity
int capacity;
enqLock = new ReentrantLock();
notFullCondition = enqLock.newCondition();
deqLock = new ReentrantLock();
notEmptyCondition = deqLock.newCondition();
}
Art of Multiprocessor
Programming© Herlihy-Shavit
69
Bounded Queue Fields
public class BoundedQueue<T> {
ReentrantLock enqLock, deqLock;
Condition notEmptyCondition, notFullCondition;
AtomicInteger permits;
Head and Tail
Node head;
Node tail;
int capacity;
enqLock = new ReentrantLock();
notFullCondition = enqLock.newCondition();
deqLock = new ReentrantLock();
notEmptyCondition = deqLock.newCondition();
}
Art of Multiprocessor
Programming© Herlihy-Shavit
70
Enq Method Part One
public void enq(T x) {
boolean mustWakeDequeuers = false;
enqLock.lock();
try {
while (permits.get() == 0)
notFullCondition.await();
Node e = new Node(x);
tail.next = e;
tail = e;
if (permits.getAndDecrement() == capacity)
mustWakeDequeuers = true;
} finally {
enqLock.unlock();
}
…
}
Art of Multiprocessor
Programming© Herlihy-Shavit
71
Enq Method Part One
public void enq(T x) {
boolean mustWakeDequeuers = false;
enqLock.lock();
try {
Lock and unlock
while (permits.get() == 0)
enq lock
notFullCondition.await();
Node e = new Node(x);
tail.next = e;
tail = e;
if (permits.getAndDecrement() == capacity)
mustWakeDequeuers = true;
} finally {
enqLock.unlock();
}
…
}
Art of Multiprocessor
Programming© Herlihy-Shavit
72
Enq Method Part One
public void enq(T x) {
boolean mustWakeDequeuers = false;
enqLock.lock();
try {
while (permits.get() == 0)
notFullCondition.await();
Node e = new Node(x);
tail.next = e;
tail = e;
if (permits.getAndDecrement() == capacity)
mustWakeDequeuers = true;
} finally {
enqLock.unlock();
}
If queue is full, patiently
…
await further instructions …
}
Art of Multiprocessor
Programming© Herlihy-Shavit
73
Be Afraid
public void enq(T x) {
boolean mustWakeDequeuers = false;
enqLock.lock();
try {
while (permits.get() == 0)
notFullCondition.await();
Node e = new Node(x);
tail.next = e;
tail = e;
if (permits.getAndDecrement() == capacity)
mustWakeDequeuers = true;
} finally {
enqLock.unlock();
}
How do we know the
…
permits field won’t change?
}
Art of Multiprocessor
Programming© Herlihy-Shavit
74
Enq Method Part One
public void enq(T x) {
boolean mustWakeDequeuers = false;
enqLock.lock();
try {
while (permits.get() == 0)
notFullCondition.await();
Node e = new Node(x);
tail.next = e;
tail = e;
if (permits.getAndDecrement() == capacity)
mustWakeDequeuers = true;
} finally {
enqLock.unlock();
}
…
Add new node
}
Art of Multiprocessor
Programming© Herlihy-Shavit
75
Enq Method Part One
public void enq(T x) {
boolean mustWakeDequeuers = false;
enqLock.lock();
try {
while (permits.get() == 0)
notFullCondition.await();
Node e = new Node(x);
tail.next = e;
tail = e;
if (permits.getAndDecrement() == capacity)
mustWakeDequeuers = true;
} finally {
enqLock.unlock();
}
If queue was empty, wake
…
frustrated dequeuers
}
Art of Multiprocessor
Programming© Herlihy-Shavit
76
Enq Method Part Deux
public void enq(T x) {
…
if (mustWakeDequeuers) {
deqLock.lock();
try {
notEmptyCondition.signalAll();
} finally {
deqLock.unlock();
}
}
}
Art of Multiprocessor
Programming© Herlihy-Shavit
77
Enq Method Part Deux
public void enq(T x) {
…
if (mustWakeDequeuers) {
deqLock.lock();
try {
notEmptyCondition.signalAll();
} finally {
deqLock.unlock();
}
}
} re there dequeuers to be signaled?
A
Art of Multiprocessor
Programming© Herlihy-Shavit
78
Enq Method Part Deux
public void enq(T x) {
Lock and unlock
…
if (mustWakeDequeuers) {
deq lock
deqLock.lock();
try {
notEmptyCondition.signalAll();
} finally {
deqLock.unlock();
}
}
}
Art of Multiprocessor
Programming© Herlihy-Shavit
79
Enq Method Part Deux
public void enq(T x) {
Signal
dequeuers
that
…
queue
no longer empty{
if (mustWakeDequeuers)
deqLock.lock();
try {
notEmptyCondition.signalAll();
} finally {
deqLock.unlock();
}
}
}
Art of Multiprocessor
Programming© Herlihy-Shavit
80
The Enq() & Deq() Methods
• Share no locks
– That’s good
• But do share an atomic counter
– Accessed on every method call
– That’s not so good
• Can we alleviate this bottleneck?
Art of Multiprocessor
Programming© Herlihy-Shavit
81
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
Art of Multiprocessor
Programming© Herlihy-Shavit
82
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 …)
Art of Multiprocessor
Programming© Herlihy-Shavit
83
A Lock-Free Queue
head
tail
Sentinel
Art of Multiprocessor
Programming© Herlihy-Shavit
84
Compare and Set
CAS
Art of Multiprocessor
Programming© Herlihy-Shavit
85
Enqueue
head
tail
Enq(
Art of Multiprocessor
Programming© Herlihy-Shavit
)
86
Enqueue
head
tail
Art of Multiprocessor
Programming© Herlihy-Shavit
87
Logical Enqueue
CAS
head
tail
Art of Multiprocessor
Programming© Herlihy-Shavit
88
Physical Enqueue
head
tail
CAS
Enqueue Node
Art of Multiprocessor
Programming© Herlihy-Shavit
89
Enqueue
• These two steps are not atomic
• The tail field refers to either
– Actual last Node (good)
– Penultimate Node (not so good)
• Be prepared!
Art of Multiprocessor
Programming© Herlihy-Shavit
90
Enqueue
• What do you do if you find
– A trailing tail?
• Stop and help fix it
– If tail node has non-null next field
– CAS the queue’s tail field to tail.next
• As in the universal construction
Art of Multiprocessor
Programming© Herlihy-Shavit
2007
91
When CASs Fail
• During logical enqueue
– Abandon hope, restart
– Still lock-free (why?)
• During physical enqueue
– Ignore it (why?)
Art of Multiprocessor
Programming© Herlihy-Shavit
92
Dequeuer
head
tail
Read value
Art of Multiprocessor
Programming© Herlihy-Shavit
93
Make first Node
new sentinel
Dequeuer
CAS
head
tail
Art of Multiprocessor
Programming© Herlihy-Shavit
94
Memory Reuse?
• What do we do with nodes after we
dequeue them?
• Java: let garbage collector deal?
• Suppose there is no GC, or we prefer
not to use it?
Art of Multiprocessor
Programming© Herlihy-Shavit
95
Dequeuer
CAS
head
tail
Can recycle
Art of Multiprocessor
Programming© Herlihy-Shavit
96
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 …
Art of Multiprocessor
Programming© Herlihy-Shavit
97
Why Recycling is Hard
head
tail
Want to
redirect
tail
from
zzz…
grey to
red
Free pool
Art of Multiprocessor
Programming© Herlihy-Shavit
98
Both Nodes Reclaimed
head
tail
zzz
Free pool
Art of Multiprocessor
Programming© Herlihy-Shavit
99
One Node Recycled
head
tail
Yawn!
Free pool
Art of Multiprocessor
Programming© Herlihy-Shavit
100
Why Recycling is Hard
head
tail
CAS
OK,
here
I go!
Free pool
Art of Multiprocessor
Programming© Herlihy-Shavit
101
Final State
head
tail
Bad news
zOMG what went wrong?
Free pool
Art of Multiprocessor
Programming© Herlihy-Shavit
102
The Dreaded ABA Problem
head
tail
Head pointer has value A
Thread reads value A
Art of Multiprocessor
Programming© Herlihy-Shavit
103
Dreaded ABA continued
head
zzz
tail
Head pointer has value B
Node A freed
Art of Multiprocessor
Programming© Herlihy-Shavit
104
Dreaded ABA continued
head
Yawn!
tail
Head pointer has value A again
Node A recycled & reinitialized
Art of Multiprocessor
Programming© Herlihy-Shavit
105
Dreaded ABA continued
head
tail
CAS
CAS succeeds because pointer matches
even though pointer’s meaning has changed
Art of Multiprocessor
Programming© Herlihy-Shavit
106
The Dreaded ABA Problem
• Is a result of CAS() semantics
– I blame Sun, Intel, AMD, …
• Not with Load-Locked/StoreConditional
– Good for IBM?
Art of Multiprocessor
Programming© Herlihy-Shavit
107
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
Art of Multiprocessor
Programming© Herlihy-Shavit
108
Atomic Stamped Reference
• AtomicStampedReference class
– Java.util.concurrent.atomic package
Can get reference and stamp
atomically
Reference
address
S
Stamp
Art of Multiprocessor
Programming© Herlihy-Shavit
109
Concurrent Stack
• Methods
– push(x)
– pop()
• Last-in, First-out (LIFO) order
• Lock-Free!
Art of Multiprocessor
Programming© Herlihy-Shavit
110
Empty Stack
Top
Art of Multiprocessor
Programming© Herlihy-Shavit
111
Push
Top
Art of Multiprocessor
Programming© Herlihy-Shavit
112
Push
Top
CAS
Art of Multiprocessor
Programming© Herlihy-Shavit
113
Push
Top
Art of Multiprocessor
Programming© Herlihy-Shavit
114
Push
Top
Art of Multiprocessor
Programming© Herlihy-Shavit
115
Push
Top
Art of Multiprocessor
Programming© Herlihy-Shavit
116
Push
Top
CAS
Art of Multiprocessor
Programming© Herlihy-Shavit
117
Push
Top
Art of Multiprocessor
Programming© Herlihy-Shavit
118
Pop
Top
Art of Multiprocessor
Programming© Herlihy-Shavit
119
Pop
Top
CAS
Art of Multiprocessor
Programming© Herlihy-Shavit
120
Pop
Top
CAS
mine!
Art of Multiprocessor
Programming© Herlihy-Shavit
121
Pop
Top
CAS
Art of Multiprocessor
Programming© Herlihy-Shavit
122
Pop
Top
Art of Multiprocessor
Programming© Herlihy-Shavit
123
Lock-free Stack
public class LockFreeStack {
private AtomicReference top =
new AtomicReference(null);
public boolean tryPush(Node node){
Node oldTop = top.get();
node.next = oldTop;
return(top.compareAndSet(oldTop, node))
}
public void push(T value) {
Node node = new Node(value);
while (true) {
if (tryPush(node)) {
return;
} else backoff.backoff();
}}
Art of Multiprocessor
Programming© Herlihy-Shavit
124
Lock-free Stack
public class LockFreeStack {
private AtomicReference top = new
AtomicReference(null);
public Boolean tryPush(Node node){
Node oldTop = top.get();
node.next = oldTop;
return(top.compareAndSet(oldTop, node))
}
public void push(T value) {
Node node = new Node(value);
while (true) {
if (tryPush(node)) {
return;
} else backoff.backoff()
tryPush
attempts to push a node
}}
Art of Multiprocessor
Programming© Herlihy-Shavit
125
Lock-free Stack
public class LockFreeStack {
private AtomicReference top = new
AtomicReference(null);
public boolean tryPush(Node node){
Node oldTop = top.get();
node.next = oldTop;
return(top.compareAndSet(oldTop, node))
}
public void push(T value) {
Node node = new Node(value);
while (true) {
if (tryPush(node)) {
return;
Read top value
} else backoff.backoff()
}}
Art of Multiprocessor
Programming© Herlihy-Shavit
126
Lock-free Stack
public class LockFreeStack {
private AtomicReference top = new
AtomicReference(null);
public boolean tryPush(Node node){
Node oldTop = top.get();
node.next = oldTop;
return(top.compareAndSet(oldTop, node))
}
public void push(T value) {
Node node = new Node(value);
while (true) {
if (tryPush(node)) {
return;
} else top
backoff.backoff()
current
will be new node’s successor
}}
Art of Multiprocessor
Programming© Herlihy-Shavit
127
Lock-free Stack
public class LockFreeStack {
private AtomicReference top = new
AtomicReference(null);
public boolean tryPush(Node node){
Node oldTop = top.get();
node.next = oldTop;
return(top.compareAndSet(oldTop, node))
}
public void push(T value) {
Node node = new Node(value);
while (true) {
if (tryPush(node)) {
return;
else backoff.backoff()
Try to} swing
top, return success or failure
}}
Art of Multiprocessor
Programming© Herlihy-Shavit
128
Lock-free Stack
public class LockFreeStack {
private AtomicReference top = new
AtomicReference(null);
public boolean tryPush(Node node){
Node oldTop = top.get();
node.next = oldTop;
return(top.compareAndSet(oldTop, node))
}
public void push(T value) {
Node node = new Node(value);
while (true) {
if (tryPush(node)) {
return;
} else backoff.backoff()
Push calls tryPush
}}
Art of Multiprocessor
Programming© Herlihy-Shavit
129
Lock-free Stack
public class LockFreeStack {
private AtomicReference top = new
AtomicReference(null);
public boolean tryPush(Node node){
Node oldTop = top.get();
node.next = oldTop;
return(top.compareAndSet(oldTop, node))
}
public void push(T value) {
Node node = new Node(value);
while (true) {
if (tryPush(node)) {
return;
} else backoff.backoff()
Create new node
}}
Art of Multiprocessor
Programming© Herlihy-Shavit
130
Lock-free Stack
Makes scheduling benevolent so
public class LockFreeStack {
Method is effectively waitprivate AtomicReference top = new
free
AtomicReference(null);
public boolean tryPush(Node node){
Node oldTop
top.get(); fails,
If= tryPush()
node.next = oldTop;
back off before retrying
return(top.compareAndSet(oldTop,
node))
}
public void push(T value) {
Node node = new Node(value);
while (true) {
if (tryPush(node)) {
return;
} else backoff.backoff()
}}
Art of Multiprocessor
Programming© Herlihy-Shavit
131
Lock-free Stack
• Good
– No locking
• Bad
– Without GC, fear ABA
– Without backoff, huge contention at top
– In any case, no parallelism
Art of Multiprocessor
Programming© Herlihy-Shavit
132
Big Question
• Are stacks inherently sequential?
• Reasons why
– Every pop() call fights for top item
• Reasons why not
– Stay tuned …
Art of Multiprocessor
Programming© Herlihy-Shavit
133
Elimination-Backoff Stack
• How to
– “turn contention into parallelism”
• Replace familiar
– exponential backoff
• With alternative
– elimination-backoff
Art of Multiprocessor
Programming© Herlihy-Shavit
134
Observation
Push(
linearizable stack
)
Pop()
Yes!
After an equal number
of pushes and pops,
stack stays the same
Art of Multiprocessor
Programming© Herlihy-Shavit
135
Idea: Elimination Array
Push(
)
Pick at
random
stack
Pop()
Pick at
random
Elimination
Array
Art of Multiprocessor
Programming© Herlihy-Shavit
136
Push Collides With Pop
continue
Push(
stack
)
Pop()
continue
Yes!
No need to
access stack
Art of Multiprocessor
Programming© Herlihy-Shavit
137
No Collision
Push( )
Pop()
stack
If pushes collide
no collision,
orIfpops
collide
accessstack
stack
access
Art of Multiprocessor
Programming© Herlihy-Shavit
138
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
Art of Multiprocessor
Programming© Herlihy-Shavit
139
Elimination-Backoff Stack
If CAS fails, back off
Push( )
Pop()
CAS
Top
Art of Multiprocessor
Programming© Herlihy-Shavit
140
Dynamic Range and Delay
Push( )
Pick random range and
max time to wait for
collision based on level of
contention encountered
Art of Multiprocessor
Programming© Herlihy-Shavit
141
Linearizability
• Un-eliminated calls
– linearized as before
• Eliminated calls:
– linearize pop() immediately after matching
push()
• Combination is a linearizable stack
Art of Multiprocessor
Programming© Herlihy-Shavit
142
Un-Eliminated Linearizability
pop(v1)
push(v1)
time
time
Art of Multiprocessor
Programming© Herlihy-Shavit
143
Eliminated Linearizability
Collision
Point
pop(v1)
push(v1)
pop(v2)
push(v2)
Red calls are
eliminated
time
time
Art of Multiprocessor
Programming© Herlihy-Shavit
144
Backoff Has Dual Effect
• 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
Art of Multiprocessor
Programming© Herlihy-Shavit
145
Elimination Array
public class EliminationArray {
private static final int duration = ...;
private static final int timeUnit = ...;
Exchanger<T>[] exchanger;
public EliminationArray(int capacity) {
exchanger = new Exchanger[capacity];
for (int i = 0; i < capacity; i++)
exchanger[i] = new Exchanger<T>();
…
}
…
}
Art of Multiprocessor
Programming© Herlihy-Shavit
146
Elimination Array
public class EliminationArray {
private static final int duration = ...;
private static final int timeUnit = ...;
Exchanger<T>[] exchanger;
public EliminationArray(int capacity) {
exchanger = new Exchanger[capacity];
for (int i = 0; i < capacity; i++)
exchanger[i] = new Exchanger<T>();
…
}
…
An array of exchangers
}
Art of Multiprocessor
Programming© Herlihy-Shavit
147
A Lock-Free Exchanger
public class Exchanger<T> {
AtomicStampedReference<T> slot
= new AtomicStampedReference<T>(null, 0);
Art of Multiprocessor
Programming© Herlihy-Shavit
148
A Lock-Free Exchanger
public class Exchanger<T> {
AtomicStampedReference<T> slot
= new AtomicStampedReference<T>(null, 0);
Atomically modifiable
reference + time stamp
Art of Multiprocessor
Programming© Herlihy-Shavit
149
Atomic Stamped Reference
• AtomicStampedReference class
– Java.util.concurrent.atomic package
• In C or C++:
Reference
address
S
Stamp
Art of Multiprocessor
Programming© Herlihy-Shavit
150
Extracting Reference & Stamp
Public T get(int[] stampHolder);
Art of Multiprocessor
Programming© Herlihy-Shavit
151
Extracting Reference & Stamp
Public T get(int[] stampHolder);
Returns
reference
to object of
type T
Returns stamp at
array index 0!
Art of Multiprocessor
Programming© Herlihy-Shavit
152
Exchanger Status
enum Status {EMPTY, WAITING, BUSY};
Art of Multiprocessor
Programming© Herlihy-Shavit
153
Exchanger Status
enum Status {EMPTY, WAITING, BUSY};
Nothing yet
Art of Multiprocessor
Programming© Herlihy-Shavit
154
Exchange Status
enum Status {EMPTY, WAITING, BUSY};
Nothing yet
One thread is waiting
for rendez-vous
Art of Multiprocessor
Programming© Herlihy-Shavit
155
Exchange Status
enum Status {EMPTY, WAITING, BUSY};
Nothing yet
One thread is waiting
for rendez-vous
Other threads busy
with rendez-vous
Art of Multiprocessor
Programming© Herlihy-Shavit
156
The Exchange
public T Exchange(T myItem, long nanos)
throws TimeoutException {
long timeBound = System.nanoTime() + nanos;
int[] stampHolder = {EMPTY};
while (true) {
if (System.nanoTime() > timeBound)
throw new TimeoutException();
T herItem = slot.get(stampHolder);
int stamp = stampHolder[0];
switch(stamp) {
case EMPTY: …
// slot is free
case WAITING: … // someone waiting for me
case BUSY: …
// others exchanging
}
}
Art of Multiprocessor
Programming© Herlihy-Shavit
157
The Exchange
public T Exchange(T myItem, long nanos)
throws TimeoutException {
long timeBound = System.nanoTime() + nanos;
int[] stampHolder = {EMPTY};
while (true) {
if (System.nanoTime() > timeBound)
throw new TimeoutException();
T herItem = slot.get(stampHolder);
int stamp = stampHolder[0];
switch(stamp) {
Item
& timeout
case EMPTY: …
// slot is free
case WAITING: … // someone waiting for me
case BUSY: …
// others exchanging
}
}
Art of Multiprocessor
Programming© Herlihy-Shavit
158
The Exchange
public T Exchange(T myItem, long nanos)
throws TimeoutException {
long timeBound = System.nanoTime() + nanos;
int[] stampHolder = {EMPTY};
while (true) {
if (System.nanoTime() > timeBound)
throw new TimeoutException();
T herItem = slot.get(stampHolder);
int stamp = stampHolder[0];
switch(stamp) {
case EMPTY: …
// slot is free
case WAITING: … // someone waiting for me
case BUSY: …
// others exchanging
}
Array to hold timestamp
}
Art of Multiprocessor
Programming© Herlihy-Shavit
159
The Exchange
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) {
case EMPTY:
// slot is free
case WAITING: // someone waiting for me
case BUSY:
// others exchanging
}
Loop until timeout
}}
Art of Multiprocessor
Programming© Herlihy-Shavit
160
The Exchange
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) {
case EMPTY:
// slot is free
case WAITING: // someone waiting for me
case BUSY:
// others exchanging
}
Get other’s item and timestamp
}}
Art of Multiprocessor
Programming© Herlihy-Shavit
161
The Exchange
public T Exchange(T myItem, long nanos) throws
TimeoutException {
long timeBound = System.nanoTime() + nanos;
Exchanger
slot has= three
int[] stampHolder
{0}; states
while (true) {
if (System.nanoTime() > timeBound)
throw new TimeoutException();
T herItem = slot.get(stampHolder);
int stamp = stampHolder[0];
switch(stamp) {
case EMPTY: …
// slot is free
case WAITING: … // someone waiting for me
case BUSY: …
// others exchanging
}
}}
Art of Multiprocessor
Programming© Herlihy-Shavit
162
Lock-free Exchanger
EMPTY
Art of Multiprocessor
Programming© Herlihy-Shavit
163
Lock-free Exchanger
CAS
EMPTY
Art of Multiprocessor
Programming© Herlihy-Shavit
164
Lock-free Exchanger
WAITING
Art of Multiprocessor
Programming© Herlihy-Shavit
165
Lock-free Exchanger
In search of
partner …
WAITING
Art of Multiprocessor
Programming© Herlihy-Shavit
166
Try to
Lock-free Exchanger
exchange item
and set state
to BUSY
Still waiting …
Slot
CAS
WAITING
Art of Multiprocessor
Programming© Herlihy-Shavit
167
Lock-free Exchanger
Partner showed
up, take item and
reset to EMPTY
Slot
BUSY
item
stamp/state
Art of Multiprocessor
Programming© Herlihy-Shavit
168
Lock-free Exchanger
Partner showed
up, take item and
reset to EMPTY
Slot
BUSY
EMPTY
item
stamp/state
Art of Multiprocessor
Programming© Herlihy-Shavit
169
Exchanger State EMPTY
case EMPTY: // slot is free
if (slot.compareAndSet(herItem, myItem, EMPTY,
WAITING)) {
while (System.nanoTime() < timeBound){
herItem = slot.get(stampHolder);
if (stampHolder[0] == BUSY) {
slot.set(null, EMPTY);
return herItem;
}}
if (slot.compareAndSet(myItem, null, WAITING,
EMPTY)){throw new TimeoutException();
} else {
herItem = slot.get(stampHolder);
slot.set(null, EMPTY);
return herItem;
}
Art of Multiprocessor
170
} break;
Programming© Herlihy-Shavit
Exchanger State EMPTY
case EMPTY: // slot is free
if (slot.compareAndSet(herItem, myItem, EMPTY,
WAITING)) {
while (System.nanoTime() < timeBound){
herItem = slot.get(stampHolder);
if (stampHolder[0] == BUSY) {
slot.set(null, EMPTY);
return herItem;
}}
if (slot.compareAndSet(myItem, null, WAITING,
EMPTY)){throw new TimeoutException();
} else {
Slot is free, try to insert
herItem = slot.get(stampHolder);
myItem and
change state
slot.set(null,
EMPTY);
return
herItem;
to WAITING
}
Art of Multiprocessor
171
} break;
Programming© Herlihy-Shavit
Exchanger State EMPTY
case EMPTY: // slot is free
if (slot.compareAndSet(herItem, myItem, EMPTY,
WAITING)) {
while (System.nanoTime() < timeBound){
herItem = slot.get(stampHolder);
if (stampHolder[0] == BUSY) {
slot.set(null, EMPTY);
return herItem;
}}
if (slot.compareAndSet(myItem, null, WAITING,
EMPTY)){throw new TimeoutException();
} else {
Loop while still time left to
herItem = slot.get(stampHolder);
attempt exchange
slot.set(null,
EMPTY);
return herItem;
}
Art of Multiprocessor
172
} break;
Programming© Herlihy-Shavit
Exchanger State EMPTY
case EMPTY: // slot is free
if (slot.compareAndSet(herItem, myItem, WAITING,
BUSY)) {
while (System.nanoTime() < timeBound){
herItem = slot.get(stampHolder);
if (stampHolder[0] == BUSY) {
slot.set(null, EMPTY);
return herItem;
}}
if (slot.compareAndSet(myItem, null, WAITING,
EMPTY)){throw new TimeoutException();
} else {
Get item and stamp in slot
herItem = slot.get(stampHolder);
and check EMPTY);
if state changed
slot.set(null,
return
herItem;
to BUSY
}
Art of Multiprocessor
173
} break;
Programming© Herlihy-Shavit
Exchanger State EMPTY
case EMPTY: // slot is free
if (slot.compareAndSet(herItem, myItem, EMPTY,
WAITING)) {
while (System.nanoTime() < timeBound){
herItem = slot.get(stampHolder);
if (stampHolder[0] == BUSY) {
slot.set(null, EMPTY);
return herItem;
}}
if (slot.compareAndSet(myItem, null, WAITING,
EMPTY)){throw new TimeoutException();
} else {
If successful reset slot
herItem = slot.get(stampHolder);
state to EMPTY
slot.set(null,
EMPTY);
return herItem;
}
Art of Multiprocessor
174
} break;
Programming© Herlihy-Shavit
Exchanger State EMPTY
case EMPTY: // slot is free
if (slot.compareAndSet(herItem, myItem, WAITING,
BUSY)) {
while (System.nanoTime() < timeBound){
herItem = slot.get(stampHolder);
if (stampHolder[0] == BUSY) {
slot.set(null, EMPTY);
return herItem;
}}
if (slot.compareAndSet(myItem, null, WAITING,
EMPTY)){throw new TimeoutException();
} else {
herItem = slot.get(stampHolder);
slot.set(null, EMPTY);
return herItem;
}
and return item found in slot
Art of Multiprocessor
175
} break;
Programming© Herlihy-Shavit
Exchanger State EMPTY
case EMPTY: // slot is free
Otherwise
we ran out of
if (slot.compareAndSet(herItem,
myItem, EMPTY,
WAITING))
time, try{ to reset state to
while (System.nanoTime() < timeBound){
EMPTY,
if
successful
time
herItem = slot.get(stampHolder);
out if (stampHolder[0] == BUSY) {
slot.set(null, EMPTY);
return herItem;
}}
if (slot.compareAndSet(myItem, null, WAITING,
EMPTY)){throw new TimeoutException();
} else {
herItem = slot.get(stampHolder);
slot.set(null, EMPTY);
return herItem;
}
Art of Multiprocessor
176
} break;
Programming© Herlihy-Shavit
Exchanger State EMPTY
case EMPTY: // slot is free
If reset
failed, someone showedmyItem,
up WAITING,
if (slot.compareAndSet(herItem,
BUSY))
after {all, take that item
while (System.nanoTime() < timeBound){
herItem = slot.get(stampHolder);
if (stampHolder[0] == BUSY) {
slot.set(null, EMPTY);
return herItem;
}}
if (slot.compareAndSet(myItem, null, WAITING,
EMPTY)){throw new TimeoutException();
} else {
herItem = slot.get(stampHolder);
slot.set(null, EMPTY);
return herItem;
}
Art of Multiprocessor
177
} break;
Programming© Herlihy-Shavit
Exchanger State EMPTY
case EMPTY: // slot is free
Set if
slot
to EMPTY with new
(slot.compareAndSet(herItem,
myItem, EMPTY,
WAITING))
{ and return item found
timestamp
while (System.nanoTime() < timeBound){
herItem = slot.get(stampHolder);
if (stampHolder[0] == BUSY) {
slot.set(null, EMPTY);
return herItem;
}}
if (slot.compareAndSet(myItem, null, WAITING,
EMPTY)){throw new TimeoutException();
} else {
herItem = slot.get(stampHolder);
slot.set(null, EMPTY);
return herItem;
}
Art of Multiprocessor
178
} break;
Programming© Herlihy-Shavit
Exchanger State EMPTY
case EMPTY: // slot is free
if (slot.compareAndSet(herItem, myItem, EMPTY,
WAITING)) {
while (System.nanoTime() < timeBound){
herItem = slot.get(stampHolder);
if (stampHolder[0] == BUSY) {
slot.set(null,If
EMPTY);
initial CAS failed then
return herItem;
someone else changed state
}}
from EMPTYnull,
to WAITING
if (slot.compareAndSet(myItem,
WAITING,
EMPTY)){throw new TimeoutException();
so retry from start
} else {
herItem = slot.get(stampHolder);
slot.set(null, EMPTY);
return herItem;
}
Art of Multiprocessor
179
} break;
Programming© Herlihy-Shavit
States WAITING and BUSY
case WAITING:
// someone waiting for me
if (slot.compareAndSet(herItem, myItem,
WAITING, BUSY))
return herItem;
break;
case BUSY:
// others in middle of exchanging
break;
default: // impossible
break;
}
}
}
}
Art of Multiprocessor
Programming© Herlihy-Shavit
180
States WAITING and BUSY
case WAITING:
// someone waiting for me
if (slot.compareAndSet(herItem, myItem,
WAITING, BUSY))
return herItem;
break;
case BUSY:
// others in middle of exchanging
break;
WAITING means
default: state
// impossible
break;
someone is waiting for an
}
exchange, so attempt to
}
CAS my item in and change
}
}
state to BUSY
Art of Multiprocessor
Programming© Herlihy-Shavit
181
States WAITING and BUSY
case WAITING:
// someone waiting for me
if (slot.compareAndSet(herItem, myItem,
WAITING, BUSY))
return herItem;
break;
case BUSY:
// others in middle of exchanging
break;
default: // impossible
If successful return her item, state
break;
}
is now BUSY, otherwise someone
}
else took her item so try again from
}
}
start
Art of Multiprocessor
Programming© Herlihy-Shavit
182
States WAITING and BUSY
case WAITING:
// someone waiting for me
if (slot.compareAndSet(herItem, myItem,
WAITING, BUSY))
return herItem;
break;
case BUSY:
// others in middle of exchanging
break;
default: // impossible
If state is BUSY then
break;
some other threads are
}
using slot to exchange so
}
}
start again
}
Art of Multiprocessor
Programming© Herlihy-Shavit
183
The Exchanger Slot
• Exchanger is 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
Art of Multiprocessor
Programming© Herlihy-Shavit
184
Elimination Array
public class EliminationArray {
…
public T visit(T value, int Range) throws
TimeoutException {
int slot = random.nextInt(Range);
int nanodur = convertToNanos(duration, timeUnit));
return (exchanger[slot].exchange(value, nanodur)
}}
Art of Multiprocessor
Programming© Herlihy-Shavit
185
Elimination Array
public class EliminationArray {
…
public T visit(T value, int Range) throws
TimeoutException {
int slot = random.nextInt(Range);
int nanodur = convertToNanos(duration, timeUnit));
return (exchanger[slot].exchange(value, nanodur)
}}
visit the elimination array with a value
and a range (duration to wait is not
dynamic)
Art of Multiprocessor
Programming© Herlihy-Shavit
186
Elimination Array
public class EliminationArray {
…
Pick a random array entry
public T visit(T value, int Range) throws
TimeoutException {
int slot = random.nextInt(Range);
int nanodur = convertToNanos(duration, timeUnit));
return (exchanger[slot].exchange(value, nanodur)
}}
Art of Multiprocessor
Programming© Herlihy-Shavit
187
Elimination Array
public class EliminationArray {
…
public T visit(T
value,
int or
Range)
Exchange
value
timethrows
out
TimeoutException {
int slot = random.nextInt(Range);
int nanodur = convertToNanos(duration, timeUnit));
return (exchanger[slot].exchange(value, nanodur)
}}
Art of Multiprocessor
Programming© Herlihy-Shavit
188
Elimination Stack Push
public void push(T value) {
...
while (true) {
if (tryPush(node)) {
return;
} else try {
T otherValue =
eliminationArray.visit(value,policy.Range);
if (otherValue == null) {
return;
}
}
Art of Multiprocessor
Programming© Herlihy-Shavit
189
Elimination Stack Push
public void push(T value) {
...
while (true) {
if (tryPush(node)) {
return;
} else try {
T otherValue =
eliminationArray.visit(value,policy.Range);
if (otherValue == null) {
return;
First try to push
}
}
Art of Multiprocessor
Programming© Herlihy-Shavit
190
Elimination Stack Push
public void push(T value) {
...
If(true)
failed{ back-off to try to eliminate
while
if (tryPush(node)) {
return;
} else try {
T otherValue =
eliminationArray.visit(value,policy.Range);
if (otherValue == null) {
return;
}
}
Art of Multiprocessor
Programming© Herlihy-Shavit
191
Elimination Stack Push
public void push(T value) {
...
pushed and range to try
whileValue
(true)being
{
if (tryPush(node)) {
return;
} else try {
T otherValue =
eliminationArray.visit(value,policy.Range);
if (otherValue == null) {
return;
}
}
Art of Multiprocessor
Programming© Herlihy-Shavit
192
Elimination Stack Push
public void push(T value) {
...
Only a pop has null value
while (true) {
so elimination was successful
if (tryPush(node)) {
return;
} else try {
T otherValue =
eliminationArray.visit(value,policy.Range);
if (otherValue == null) {
return;
}
}
Art of Multiprocessor
Programming© Herlihy-Shavit
193
Elimination Stack Push
public void push(T value) {
... Else retry push on lock-free stack
while (true) {
if (tryPush(node)) {
return;
} else try {
T otherValue =
eliminationArray.visit(value,policy.Range);
if (otherValue == null) {
return;
}
}
Art of Multiprocessor
Programming© Herlihy-Shavit
194
Elimination Stack Pop
public T pop() {
...
while (true) {
if (tryPop()) {
return returnNode.value;
} else
try {
T otherValue =
eliminationArray.visit(null,policy.Range;
if otherValue != null) {
return otherValue;
}
}
}}
Art of Multiprocessor
Programming© Herlihy-Shavit
195
Elimination Stack Pop
public T pop() {
... Same as push, if non-null other
while (true) {
thread must
have pushed
if (tryPop())
{
return
returnNode.value;
so elimnation
succeeds
} else
try {
T otherValue =
eliminationArray.visit(null,policy.Range;
if ( otherValue != null) {
return otherValue;
}
}
}}
Art of Multiprocessor
Programming© Herlihy-Shavit
196
Summary
• We saw both lock-based and lockfree 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
Art of Multiprocessor
Programming© Herlihy-Shavit
197
This work is licensed under a Creative Commons AttributionShareAlike 2.5 License.
• You are free:
– to Share — to copy, distribute and transmit the work
– to Remix — to adapt the work
• Under the following conditions:
– Attribution. You must attribute the work to “The Art of
Multiprocessor Programming” (but not in any way that
suggests that the authors endorse you or your use of the
work).
– Share Alike. If you alter, transform, or build upon this work,
you may distribute the resulting work only under the same,
similar or a compatible license.
• For any reuse or distribution, you must make clear to others the
license terms of this work. The best way to do this is with a link
to
– http://creativecommons.org/licenses/by-sa/3.0/.
• Any of the above conditions can be waived if you get permission
from the copyright holder.
• Nothing in this license impairs or restricts the author's moral
rights.
Art of Multiprocessor
Programming© Herlihy-Shavit
198