Companion slides for The Art of Multiprocessor Programming by Maurice Herlihy amp Nir Shavit Art of Multiprocessor Programming 2 2 The FiveFold Path Coarsegrained locking Finegrained locking ID: 778985
Download The PPT/PDF document "Concurrent Queues and Stacks" is the property of its rightful owner. Permission is granted to download and print the materials on this web site for personal, non-commercial use only, and to display it on your personal computer provided you do not modify the materials and that you retain all copyright notices contained in the materials. By downloading content from our website, you accept the terms of this agreement.
Slide1
Concurrent Queues and Stacks
Companion slides for
The Art of Multiprocessor Programming
by Maurice Herlihy & Nir Shavit
Slide2Art of Multiprocessor Programming
2
2
The Five-Fold PathCoarse-grained lockingFine-grained lockingOptimistic synchronization
Lazy synchronizationLock-free synchronization
Slide3Art of Multiprocessor Programming
3
3
Another Fundamental ProblemWe told you about Sets implemented by linked lists
Hash TablesNext: queuesNext: stacks
Slide4Art of Multiprocessor Programming
4
4
Queues & Stackspool of items
Slide5Art of Multiprocessor Programming
5
5
Queues
deq
()/
enq
( )
Total order
First in
First out
Slide6Art of Multiprocessor Programming
6
6
Stacks
pop()/
push( )
Total order
Last in
First out
Slide7Art of Multiprocessor Programming
7
7
BoundedFixed capacityGood when resources an issue
Slide8Art of Multiprocessor Programming
8
8
UnboundedUnlimited capacityOften more convenient
…
Slide9Art of Multiprocessor Programming
9
Blocking
zzz
…
Block on attempt to remove from empty stack or queue
Slide10Art of Multiprocessor Programming
10
Blocking
zzz
…
Block on attempt to add to full bounded stack or queue
Slide11Art of Multiprocessor Programming
11
Non-Blocking
Ouch!
Throw exception on attempt to remove from empty stack or queue
Slide12Art of Multiprocessor Programming
12
12
This LectureQueueBounded, blocking, lock-basedUnbounded, non-blocking, lock-free
StackUnbounded, non-blocking lock-freeElimination-backoff algorithm
Slide13Art of Multiprocessor Programming
13
13
Queue: Concurrency
enq(x)
y=deq()
enq()
and
deq()
work at different ends of the object
tail
head
Slide14Art of Multiprocessor Programming
14
14
Concurrency
enq(x)
Challenge: what if the queue is empty or full?
y=deq()
tail
head
Slide15Art of Multiprocessor Programming
15
15
Bounded Queue
Sentinel
head
tail
Slide16Art of Multiprocessor Programming
16
16
Bounded Queue
head
tail
First actual item
Slide17Art of Multiprocessor Programming
17
17
Bounded Queue
head
tail
Lock out other
deq()
calls
deqLock
Slide18Art of Multiprocessor Programming
18
18
Bounded Queue
head
tail
Lock out other
enq()
calls
deqLock
enqLock
Slide19Art of Multiprocessor Programming
19
19
Not Done Yet
head
tail
deqLock
enqLock
Need to tell whether queue is full or empty
Slide20Art of Multiprocessor Programming
20
20
Not Done Yet
head
tail
deqLock
enqLock
Max size is
8
items
size
1
Slide21Art of Multiprocessor Programming
21
21
Not Done Yet
head
tail
deqLock
enqLock
Incremented by
enq
()
Decremented by
deq
()
size
1
Slide22Art of Multiprocessor Programming
22
22
Enqueuer
1
Lock
enqLock
head
tail
deqLock
enqLock
size
Slide23Art of Multiprocessor Programming
23
23
Enqueuer
1
Read
size
OK
head
tail
deqLock
enqLock
size
Slide24Art of Multiprocessor Programming
24
24
Enqueuer
1
No need to lock
tail
head
tail
deqLock
enqLock
size
Slide25Art of Multiprocessor Programming
25
25
Enqueuer
1
Enqueue Node
head
tail
deqLock
enqLock
size
Slide26Art of Multiprocessor Programming
26
26
Enqueuer
size
1
2
getAndincrement()
head
tail
deqLock
enqLock
Slide27Art of Multiprocessor Programming
27
27
Enqueuer
8
Release lock
2
head
tail
deqLock
enqLock
size
Slide28Art of Multiprocessor Programming
28
28
Enqueuer
2
If queue was empty, notify waiting
dequeuers
head
tail
deqLock
enqLock
size
Slide29Art of Multiprocessor Programming
29
29
Unsuccesful Enqueuer
8
Uh-oh
Read size
…
head
tail
deqLock
enqLock
size
Slide30Art of Multiprocessor Programming
30
30
Dequeuer
2
Lock
deqLock
head
tail
deqLock
enqLock
size
Slide31Art of Multiprocessor Programming
31
31
Dequeuer
2
Read sentinel’s
next
field
OK
head
tail
deqLock
enqLock
size
Slide32Art of Multiprocessor Programming
32
32
Dequeuer
siz
2
Read
value
head
tail
deqLock
enqLock
size
Slide33Art of Multiprocessor Programming
33
33
Dequeuer
2
Make first Node new sentinel
head
tail
deqLock
enqLock
size
Slide34Art of Multiprocessor Programming
34
34
Dequeuer
1
Decrement
size
head
tail
deqLock
enqLock
size
Slide35Art of Multiprocessor Programming
35
35
Dequeuer
1
Release
deqLock
head
tail
deqLock
enqLock
size
Slide36Art of Multiprocessor Programming
36
36
Unsuccesful Dequeuer
0
Read sentinel’s
next
field
uh-oh
head
tail
deqLock
enqLock
size
Slide37Art of Multiprocessor Programming
37
37
Bounded Queue
public class
BoundedQueue
<T> {
ReentrantLock
enqLock
,
deqLock
;
Condition
notEmptyCondition
,
notFullCondition
;
AtomicInteger size; Node head; Node tail; int capacity; enqLock = new ReentrantLock
();
notFullCondition = enqLock.newCondition
();
deqLock = new ReentrantLock(); notEmptyCondition = deqLock.newCondition();}
Slide38Art of Multiprocessor Programming
38
38
Bounded Queue
public class BoundedQueue
<T> {
ReentrantLock
enqLock
,
deqLock
;
Condition
notEmptyCondition
,
notFullCondition
;
AtomicInteger
size; Node head; Node tail; int capacity; enqLock = new ReentrantLock
();
notFullCondition
=
enqLock.newCondition(); deqLock = new ReentrantLock(); notEmptyCondition = deqLock.newCondition();}
enq & deq locks
Slide39Art of Multiprocessor Programming
39
(push)
Slide40Art of Multiprocessor Programming
40
40
Digression: Monitor LocksJava
synchronized objects and ReentrantLocks are monitors
Allow blocking on a condition rather than spinning
Threads:
acquire
and
release
lock
wait
on a condition
Slide41Art of Multiprocessor Programming
41
41
public interface Lock {
void lock();
void
lockInterruptibly
() throws
InterruptedException
;
boolean
tryLock
();
boolean
tryLock
(long time,
TimeUnit unit); Condition newCondition(); void unlock;}The Java Lock Interface
Acquire lock
Slide42Art of Multiprocessor Programming
42
42
public interface Lock {
void lock(); void
lockInterruptibly
() throws
InterruptedException
;
boolean
tryLock
();
boolean
tryLock
(long time,
TimeUnit
unit);
Condition newCondition(); void unlock;}The Java Lock Interface
Release lock
Slide43Art of Multiprocessor Programming
43
43
public interface Lock {
void lock(); void
lockInterruptibly
() throws
InterruptedException
;
boolean
tryLock
();
boolean
tryLock
(
long
time,
TimeUnit unit); Condition newCondition(); void unlock;}The Java Lock Interface
Try for lock, but not too hard
Slide44Art of Multiprocessor Programming
44
44
public interface Lock {
void lock(); void
lockInterruptibly
() throws
InterruptedException
;
boolean
tryLock
();
boolean
tryLock
(long time,
TimeUnit
unit);
Condition newCondition(); void unlock;}The Java Lock Interface
Create condition to wait on
Slide45Art of Multiprocessor Programming
45
45
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;}
Never mind what this method does
Slide46Art of Multiprocessor Programming
46
46
Lock Conditions
public
interface
Condition {
void
await();
boolean
await(
long
time,
TimeUnit
unit);
…
void
signal();
void signalAll(); }
Slide47Art of Multiprocessor Programming
47
47
public interface Condition {
void await();
boolean
await(
long
time,
TimeUnit
unit);
…
void signal();
void
signalAll
();
}
Lock Conditions
Release lock and
wait on condition
Slide48Art of Multiprocessor Programming
48
48
public interface Condition {
void await();
boolean
await(long time,
TimeUnit
unit);
…
void
signal();
void
signalAll
();
}
Lock Conditions
Wake up one waiting thread
Slide49Art of Multiprocessor Programming
49
49
public interface Condition {
void await();
boolean
await(long time,
TimeUnit
unit);
…
void signal();
void
signalAll
();
}
Lock Conditions
Wake up all waiting threads
Slide50Art of Multiprocessor Programming
50
50
AwaitReleases lock associated with
qSleeps (gives up processor)Awakens (resumes running)Reacquires lock & returns
q.await
()
Slide51Art of Multiprocessor Programming
51
51
SignalAwakens one waiting thread
Which will reacquire lock
q.signal
();
Art of Multiprocessor Programming
52
52
Signal AllAwakens all
waiting threadsWhich will each reacquire lock
q.signalAll
();
Slide53Art of Multiprocessor Programming
53
53
A Monitor Lock
Critical Section
waiting room
lock
()
unlock
()
Slide54Art of Multiprocessor Programming
54
54
Unsuccessful Deq
Critical Section
waiting room
lock
()
await()
deq
()
Oh no,
empty
!
Slide55Art of Multiprocessor Programming
55
55
Another One
Critical Section
waiting room
lock
()
await()
deq
()
Oh no,
empty
!
Slide56Art of Multiprocessor Programming
56
56
Enqueuer to the Rescue
Critical Section
waiting room
lock
()
signalAll
()
enq
( )
unlock
()
Yawn!
Yawn!
Slide57Art of Multiprocessor Programming
57
57
Yawn!
Monitor Signalling
Critical Section
waiting room
Yawn!
Awakened thread
might still lose lock to
outside contender…
Slide58Art of Multiprocessor Programming
58
58
Dequeuers Signalled
Critical Section
waiting room
Found it
Yawn!
Slide59Art of Multiprocessor Programming
59
59
Yawn!
Dequeuers Signaled
Critical Section
waiting room
Still empty!
Slide60Art of Multiprocessor Programming
60
60
Dollar Short + Day Late
Critical Section
waiting room
Slide61Art of Multiprocessor Programming
61
61
public class
Queue<T> {
int
head = 0, tail = 0;
T[QSIZE] items;
public synchronized T
deq
() {
while
(tail – head == 0)
wait();
T result = items[head % QSIZE]; head++;
notifyAll
();
return result; } …}}Java Synchronized Methods
Slide62Art of Multiprocessor Programming
62
62
public class
Queue<T> {
int
head = 0, tail = 0;
T[QSIZE] items;
public synchronized T
deq
() {
while (tail – head == 0)
wait();
T result = items[head % QSIZE]; head++;
notifyAll
();
return result;
}
…
}}Java Synchronized Methods
Each object has an implicit lock with an implicit condition
Slide63Art of Multiprocessor Programming
63
63
public class Queue<T> {
int
head = 0, tail = 0;
T[QSIZE] items;
public
synchronized
T
deq
() {
while (tail – head == 0)
wait();
T result = items[head % QSIZE]; head++;
notifyAll
();
return result;
}
…}}Java Synchronized Methods
Lock on entry, unlock on return
Slide64Art of Multiprocessor Programming
64
64
public class Queue<T> {
int
head = 0, tail = 0;
T[QSIZE] items;
public synchronized T
deq
() {
while (tail – head == 0)
wait();
T result = items[head % QSIZE]; head++;
this.notifyAll
();
return result;
}
…
}}Java Synchronized Methods
Wait on implicit condition
Slide65Art of Multiprocessor Programming
65
65
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++;
notifyAll
();
return result;
} …}}Java Synchronized Methods
Signal all threads waiting on condition
Slide66Art of Multiprocessor Programming
66
66
(Pop!) The Bounded Queue
public class
BoundedQueue
<T> {
ReentrantLock
enqLock
,
deqLock
;
Condition
notEmptyCondition
,
notFullCondition
;
AtomicInteger size; Node head; Node tail; int capacity; enqLock = new ReentrantLock
();
notFullCondition = enqLock.newCondition
();
deqLock = new ReentrantLock(); notEmptyCondition = deqLock.newCondition();}
Slide67Art of Multiprocessor Programming
67
67
Bounded Queue Fields
public class BoundedQueue
<T> {
ReentrantLock
enqLock
,
deqLock
;
Condition
notEmptyCondition
,
notFullCondition
;
AtomicInteger
size; Node head; Node tail; int capacity; enqLock = new ReentrantLock
();
notFullCondition
=
enqLock.newCondition(); deqLock = new ReentrantLock(); notEmptyCondition = deqLock.newCondition();}
Enq & deq locks
Slide68Art of Multiprocessor Programming
68
68
Bounded Queue Fields
public class BoundedQueue
<T> {
ReentrantLock
enqLock
,
deqLock
;
Condition
notEmptyCondition
,
notFullCondition
;
AtomicInteger
size; Node head; Node tail; int capacity; enqLock
= new
ReentrantLock();
notFullCondition
= enqLock.newCondition(); deqLock = new ReentrantLock(); notEmptyCondition = deqLock.newCondition();}
Enq lock’s associated condition
Slide69Art of Multiprocessor Programming
69
69
Bounded Queue Fields
public class BoundedQueue
<T> {
ReentrantLock
enqLock
,
deqLock
;
Condition
notEmptyCondition
,
notFullCondition
;
AtomicInteger
size; Node head; Node tail; int capacity; enqLock = new
ReentrantLock
();
notFullCondition
= enqLock.newCondition(); deqLock = new ReentrantLock(); notEmptyCondition = deqLock.newCondition();}
size: 0 to capacity
Slide70Art of Multiprocessor Programming
70
70
Bounded Queue Fields
public class BoundedQueue
<T> {
ReentrantLock
enqLock
,
deqLock
;
Condition
notEmptyCondition
,
notFullCondition
;
AtomicInteger
size; Node head; Node tail; int capacity;
enqLock
= new ReentrantLock();
notFullCondition = enqLock.newCondition(); deqLock = new ReentrantLock(); notEmptyCondition = deqLock.newCondition
();}Head and Tail
Slide71Art of Multiprocessor Programming
71
71
Enq Method Part One
public void
enq
(T x) {
boolean
mustWakeDequeuers
=
false
;
enqLock.lock
();
try
{
while (size.get() == Capacity) notFullCondition.await(); Node e = new Node(x); tail.next = e; tail =
tail.next
; if (
size.getAndIncrement
() == 0) mustWakeDequeuers = true; } finally { enqLock.unlock(); } …}
Slide72Art of Multiprocessor Programming
72
72
public void
enq(T x) {
boolean
mustWakeDequeuers
= false;
enqLock.lock
();
try {
while (
size.get
() == capacity)
notFullCondition.await
();
Node e = new Node(x); tail.next = e; tail = tail.next; if (size.getAndIncrement
() == 0)
mustWakeDequeuers = true;
}
finally { enqLock.unlock(); } …}Enq Method Part OneLock and unlock enq
lock
Slide73Art of Multiprocessor Programming
73
73
public void
enq(T x) {
boolean
mustWakeDequeuers
= false;
enqLock.lock
();
try {
while
(
size.get
() == capacity)
notFullCondition.await
(); Node e = new Node(x); tail.next = e; tail = tail.next; if (
size.getAndIncrement
() == 0) mustWakeDequeuers
= true;
} finally { enqLock.unlock(); } …}Enq Method Part OneWait while queue is full …
Slide74Art of Multiprocessor Programming
74
74
public void
enq(T x) {
boolean
mustWakeDequeuers
= false;
enqLock.lock
();
try {
while
(
size.get
() == capacity)
notFullCondition.await
(); Node e = new Node(x); tail.next = e; tail = tail.next; if (
size.getAndIncrement
() == 0) mustWakeDequeuers
= true;
} finally { enqLock.unlock(); } …}Enq Method Part Onewhen await()
returns, you might still fail the test !
Slide75Art of Multiprocessor Programming
75
75
public void
enq(T x) {
boolean
mustWakeDequeuers
= false;
enqLock.lock
();
try {
while
(
size.get
() == capacity)
notFullCondition.await
(); Node e = new Node(x); tail.next = e; tail = tail.next; if (
size.getAndIncrement
() == 0) mustWakeDequeuers
= true;
} finally { enqLock.unlock(); } …}Be AfraidAfter the loop: how do we know the queue won’t become full again?
Slide76Art of Multiprocessor Programming
76
76
public void
enq(T x) {
boolean
mustWakeDequeuers
= false;
enqLock.lock
();
try {
while (
size.get
() == capacity)
notFullCondition.await
();
Node e = new Node(x); tail.next = e; tail = tail.next; if (
size.getAndIncrement
() == 0)
mustWakeDequeuers
= true; } finally { enqLock.unlock(); } …}Enq Method Part OneAdd new node
Slide77Art of Multiprocessor Programming
77
77
public void
enq(T x) {
boolean
mustWakeDequeuers
= false;
enqLock.lock
();
try {
while (
size.get
() == capacity)
notFullCondition.await
();
Node e = new Node(x);
tail.next = e; tail = tail.next; if (size.getAndIncrement
() == 0)
mustWakeDequeuers =
true
; } finally { enqLock.unlock(); } …}Enq Method Part OneIf queue was empty, wake frustrated
dequeuers
Slide78Art of Multiprocessor Programming
78
78
Beware Lost Wake-Ups
Critical Section
waiting room
lock
()
Queue empty so signal
()
enq
( )
unlock
()
Yawn!
Slide79Art of Multiprocessor Programming
79
79
Lost Wake-Up
Critical Section
waiting room
lock
()
enq
(
)
unlock
()
Yawn!
Queue not empty so no need to signal
Slide80Art of Multiprocessor Programming
80
80
Lost Wake-Up
Critical Section
waiting room
Yawn!
Slide81Art of Multiprocessor Programming
81
81
Lost Wake-Up
Critical Section
waiting room
Found it
Slide82Art of Multiprocessor Programming
82
82
What’s Wrong Here?
Critical Section
waiting room
Still waiting ….!
Slide83Art of Multiprocessor Programming
83
Solution to Lost Wakeup Always use
signalAll() and notifyAll() Not
signal() and notify()
83
Slide84Art of Multiprocessor Programming
84
(pop)
Slide85Art of Multiprocessor Programming
85
85
Enq Method Part Deux
public
void
enq
(T x) {
…
if
(
mustWakeDequeuers
) {
deqLock.lock
();
try
{
notEmptyCondition.signalAll(); } finally { deqLock.unlock(); } } }
Slide86Art of Multiprocessor Programming
86
86
Enq Method Part Deux
public void enq
(T x) {
…
if
(
mustWakeDequeuers
) {
deqLock.lock
();
try {
notEmptyCondition.signalAll
();
} finally {
deqLock.unlock(); } } }Are there dequeuers
to be signaled?
Slide87Art of Multiprocessor Programming
87
87
public void
enq(T x) {
…
if (
mustWakeDequeuers
) {
deqLock.lock
();
try {
notEmptyCondition.signalAll
();
} finally {
deqLock.unlock
();
} } }Enq Method Part DeuxLock and unlock deq
lock
Slide88Art of Multiprocessor Programming
88
88
public void
enq(T x) {
…
if (
mustWakeDequeuers
) {
deqLock.lock
();
try {
notEmptyCondition.signalAll
();
} finally {
deqLock.unlock
(); } } }Enq Method Part DeuxSignal dequeuers
that queue is no longer empty
Slide89Art of Multiprocessor Programming
89
89
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?
Slide90Art of Multiprocessor Programming
90
90
Split the CounterThe enq() method
Increments onlyCares only if value is capacityThe deq() methodDecrements only
Cares only if value is
zero
Slide91Art of Multiprocessor Programming
91
91
Split CounterEnqueuer increments
enqSizeDequeuer increments
deqSize
When
enqueuer
hits capacity
Locks
deqLock
Sets
size =
enqSize
-
DeqSize
Intermittent synchronization
Not with each method call
Need both locks! (careful …)
Slide92Art of Multiprocessor Programming
92
92
A Lock-Free Queue
Sentinel
head
tail
Slide93Art of Multiprocessor Programming
93
93
Compare and Set
CAS
Slide94Art of Multiprocessor Programming
94
94
Enqueue
head
tail
enq
( )
Slide95Art of Multiprocessor Programming
95
95
Enqueue
head
tail
Slide96Art of Multiprocessor Programming
96
96
Logical Enqueue
head
tail
CAS
Slide97Art of Multiprocessor Programming
97
97
Physical Enqueue
head
tail
CAS
Slide98Art of Multiprocessor Programming
98
98
EnqueueThese two steps are not atomicThe
tail field refers to eitherActual last Node (good)Penultimate Node (not so good)Be prepared!
Slide99Art of Multiprocessor Programming
99
99
EnqueueWhat do you do if you findA trailing tail
?Stop and help fix itIf tail node has non-null next field
CAS the queue’s
tail
field to
tail.next
As in the universal construction
Slide100Art of Multiprocessor Programming
100
100
When CASs FailDuring logical enqueue
Abandon hope, restartStill lock-free (why?)During physical enqueueIgnore it (why?)
Slide101Art of Multiprocessor Programming
101
101
Dequeuer
head
tail
Read value
Slide102Art of Multiprocessor Programming
102
102
Dequeuer
head
tail
Make first Node new sentinel
CAS
Slide103Art of Multiprocessor Programming
103
103
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?
Slide104Art of Multiprocessor Programming
104
104
Dequeuer
head
tail
CAS
Can recycle
Slide105Art of Multiprocessor Programming
105
105
Simple SolutionEach thread has a free list of unused queue nodesAllocate node:
pop from listFree node: push onto listDeal with underflow somehow …
Slide106Art of Multiprocessor Programming
106
106
Why Recycling is Hard
Free pool
head
tail
Want to redirect head from
gray
to red
zzz…
Slide107Art of Multiprocessor Programming
107
107
Both Nodes Reclaimed
Free pool
zzz
head
tail
Slide108Art of Multiprocessor Programming
108
108
One Node Recycled
Free pool
Yawn!
head
tail
Slide109Art of Multiprocessor Programming
109
109
Why Recycling is Hard
Free pool
CAS
head
tail
OK, here I go!
Slide110Art of Multiprocessor Programming
110
110
Recycle FAIL
Free pool
zOMG what went wrong?
head
tail
Slide111Art of Multiprocessor Programming
111
111
The Dreaded ABA Problem
head
tail
Head reference has value
A
Thread reads value
A
Slide112Art of Multiprocessor Programming
112
112
Dreaded ABA continued
zzz
head
tail
Head reference has value
B
Node
A
freed
Slide113Art of Multiprocessor Programming
113
113
Dreaded ABA continued
Yawn!
head
tail
Head reference has value
A
again
Node
A
recycled and reinitialized
Slide114Art of Multiprocessor Programming
114
114
Dreaded ABA continued
CAS
head
tail
CAS succeeds because references match,
even though reference’s meaning has changed
Slide115Art of Multiprocessor Programming
115
115
The Dreaded ABA FAILIs a result of CAS() semanticsOracle, Intel, AMD, …Not with Load-Locked/Store-Conditional
IBM …
Slide116Art of Multiprocessor Programming
116
116
Dreaded ABA – A SolutionTag each pointer with a counterUnique over lifetime of nodePointer size vs word size issues
Overflow?Don’t worry be happy?Bounded tags?AtomicStampedReference class
Slide117Art of Multiprocessor Programming
117
117
Atomic Stamped Reference
AtomicStampedReference
class
Java.util.concurrent.atomic
package
address
S
Stamp
Reference
Can get reference & stamp atomically
Slide118Art of Multiprocessor Programming
118
118
Concurrent StackMethodspush(x)
pop()Last-in, First-out (LIFO) orderLock-Free!
Slide119Art of Multiprocessor Programming
119
119
Empty Stack
Top
Slide120Art of Multiprocessor Programming
120
120
Push
Top
Slide121Art of Multiprocessor Programming
121
121
Push
Top
CAS
Slide122Art of Multiprocessor Programming
122
122
Push
Top
Slide123Art of Multiprocessor Programming
123
123
Push
Top
Slide124Art of Multiprocessor Programming
124
124
Push
Top
Slide125Art of Multiprocessor Programming
125
125
Push
Top
CAS
Slide126Art of Multiprocessor Programming
126
126
Push
Top
Slide127Art of Multiprocessor Programming
127
127
Pop
Top
Slide128Art of Multiprocessor Programming
128
128
Pop
Top
CAS
Slide129Art of Multiprocessor Programming
129
129
Pop
Top
CAS
mine!
Slide130Art of Multiprocessor Programming
130
130
Pop
Top
CAS
Slide131Art of Multiprocessor Programming
131
131
Pop
Top
Slide132Art of Multiprocessor Programming
132
132
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(); }}
Lock-free Stack
Slide133Art of Multiprocessor Programming
133
133
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()}}
Lock-free StacktryPush attempts to push a node
Slide134Art of Multiprocessor Programming
134
134
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()}}
Lock-free StackRead top value
Slide135Art of Multiprocessor Programming
135
135
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()
}} Lock-free Stack
current top will be new node’s successor
Slide136Art of Multiprocessor Programming
136
136
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()}}
Lock-free StackTry to swing top, return success or failure
Slide137Art of Multiprocessor Programming
137
137
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
()}} Lock-free StackPush calls tryPush
Slide138Art of Multiprocessor Programming
138
138
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
()}} Lock-free StackCreate new node
Slide139Art of Multiprocessor Programming
139
139
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()}} Lock-free Stack
If tryPush() fails,back off before retrying
Slide140Art of Multiprocessor Programming
140
140
Lock-free StackGoodNo locking
BadWithout GC, fear ABAWithout backoff, huge contention at top In any case, no parallelism
Slide141Art of Multiprocessor Programming
141
141
Big QuestionAre stacks inherently sequential?
Reasons whyEvery pop() call fights for top itemReasons why notStay tuned …
Slide142Art of Multiprocessor Programming
142
142
Elimination-Backoff StackHow to“turn contention into parallelism”Replace familiar
exponential backoff With alternativeelimination-backoff
Slide143Art of Multiprocessor Programming
143
143
Observation
Push( )
Pop()
linearizable stack
After an equal number
of pushes and pops,
stack stays the same
Yes!
Slide144Art of Multiprocessor Programming
144
144
Idea: Elimination Array
Push( )
Pop()
stack
Pick at
random
Pick at
random
Elimination
Array
Slide145Art of Multiprocessor Programming
145
145
Push Collides With Pop
Push( )
Pop()
stack
continue
continue
No need to
access stack
Yes!
Slide146Art of Multiprocessor Programming
146
146
No Collision
Push( )
Pop()
stack
If no collision,
access stack
If pushes collide or pops collide access stack
Slide147Art of Multiprocessor Programming
147
147
Elimination-Backoff StackLock-free stack + elimination arrayAccess Lock-free stack, If
uncontended, apply operation if contended, back off to elimination array and attempt elimination
Slide148Art of Multiprocessor Programming
148
148
Elimination-Backoff Stack
Push( )
Pop()
Top
CAS
If CAS fails, back off
Slide149Art of Multiprocessor Programming
149
149
Dynamic Range and Delay
Push( )
Pick random range and max waiting time based on level of contention encountered
Slide15050-50, Random Slots
0
20000
40000
60000
80000
100000
120000
2
4
8
1
21
62
4
3
24
04
85
66
4
Th
r
e
a
d
s
o
p
s
/
m
s
e
c
E
x
ch
an
ger
T
r
ei
ber
Slide151Asymmetric
Rendevous
pop
1
(
)
pop
2
(
)
Push( )
Pops find first vacant slot and spin. Pushes hunt for pops.
15
94
26
head:
Slide152Asymmetric vs. Symmetric
Slide153Slide154Effect of
Backoff
and Slot Choice
Slide155Effect of Slot Choice
Random choice Sequential choice
Darker shades mean more exchanges
Slide156Effect of Slot Choice
Slide157Art of Multiprocessor Programming
157
157
Linearizability
Un-eliminated callslinearized as beforeEliminated calls:linearize pop()
immediately after
matching
push()
Combination
is a
linearizable
stack
Slide158Art of Multiprocessor Programming
158
158
Un-Eliminated Linearizability
push(v1
)
time
time
linearizable
push(v
1
)
pop(v
1
)
pop(v
1
)
Slide159Art of Multiprocessor Programming
159
159
Eliminated Linearizability
pop(v2
)
push(v
1
)
push(v
2
)
time
time
push(v
2
)
pop(v
2
)
push(v
1
)
pop(v
1
)
Collision
Point
Red calls are
eliminated
pop(v
1)linearizable
Slide160Art of Multiprocessor Programming
160
160
Backoff Has Dual EffectElimination introduces parallelismBackoff to array cuts contention on lock-free stackElimination in array cuts down number of threads accessing lock-free stack
Slide161Art of Multiprocessor Programming
161
161
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>();
… } …}Elimination Array
Slide162Art of Multiprocessor Programming
162
162
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>();
… } …}Elimination ArrayAn array of Exchangers
Slide163Art of Multiprocessor Programming
163
163
public class
Exchanger<T> { AtomicStampedReference
<T> slot
=
new
AtomicStampedReference
<T>(
null
, 0);
Digression: A Lock-Free Exchanger
Slide164Art of Multiprocessor Programming
164
164
public class Exchanger<T> {
AtomicStampedReference<T> slot
= new
AtomicStampedReference
<T>(null, 0);
A Lock-Free Exchanger
Atomically modifiable reference + status
Slide165Art of Multiprocessor Programming
165
165
Atomic Stamped ReferenceAtomicStampedReference class
Java.util.concurrent.atomic packageIn C or C++
:
address
S
reference
stamp
Slide166Art of Multiprocessor Programming
166
166
Extracting Reference & Stamp
public
T get(
int
[]
stampHolder
);
Slide167Art of Multiprocessor Programming
167
167
Extracting Reference & Stamp
public
T
get(
int
[]
stampHolder
);
Returns reference to object of type T
Returns stamp at array index 0!
Slide168Art of Multiprocessor Programming
168
168
Exchanger Status
enum Status {EMPTY, WAITING, BUSY};
Slide169Art of Multiprocessor Programming
169
169
Exchanger Status
enum Status {
EMPTY
, WAITING, BUSY};
Nothing yet
Slide170enum
Status {
EMPTY, WAITING, BUSY};
Art of Multiprocessor Programming
170
170
Exchange Status
Nothing yet
One thread is waiting for
rendez-vous
Slide171Art of Multiprocessor Programming
171
171
Exchange Status
enum Status {
EMPTY, WAITING, BUSY};
Nothing yet
One thread is waiting for
rendez-vous
Other threads busy with
rendez-vous
Slide172Art of Multiprocessor Programming
172
172
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 } }The Exchange
Slide173Art of Multiprocessor Programming
173
173
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 } }The Exchange
Item and timeout
Slide174Art of Multiprocessor Programming
174
174
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 } }The Exchange
Array holds status
Slide175Art of Multiprocessor Programming
175
175
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 } }}The Exchange
Loop until timeout
Slide176Art of Multiprocessor Programming
176
176
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 } }}
The ExchangeGet other’s item and status
Slide177Art of Multiprocessor Programming
177
177
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 } }}
The ExchangeAn Exchanger has three possible states
Slide178Art of Multiprocessor Programming
178
178
Lock-free Exchanger
EMPTY
Slide179Art of Multiprocessor Programming
179
179
EMPTY
Lock-free Exchanger
CAS
Slide180Art of Multiprocessor Programming
180
180
WAITING
Lock-free Exchanger
Slide181Art of Multiprocessor Programming
181
181
Lock-free Exchanger
In search of partner …
WAITING
Slide182Art of Multiprocessor Programming
182
182
WAITING
Lock-free Exchanger
Slot
Still waiting …
Try to exchange item and set status to
BUSY
CAS
Slide183Art of Multiprocessor Programming
183
183
BUSY
Lock-free Exchanger
Slot
Partner showed up, take item and reset to
EMPTY
item
status
Slide184Art of Multiprocessor Programming
184
184
EMPTY
BUSY
Lock-free Exchanger
Slot
item
status
Partner showed up, take item and reset to
EMPTY
Slide185Art of Multiprocessor Programming
185
185
case EMPTY:
// slot is free
if
(
slot.CAS
(
herItem
,
myItem
, EMPTY, WAITING)) {
while
(
System.nanoTime
() <
timeBound
){
herItem = slot.get(stampHolder); if (stampHolder[0] == BUSY) { slot.set(null, EMPTY);
return
herItem;
}}
if (slot.CAS(myItem, null, WAITING, EMPTY)){ throw new TimeoutException(); } else
{ herItem = slot.get(stampHolder); slot.set(null
, EMPTY); return herItem; }} break; Exchanger State EMPTY
Slide186Art of Multiprocessor Programming
186
186
case EMPTY: // slot is free
if (slot.CAS(herItem
,
myItem
, EMPTY, WAITING)) {
while (
System.nanoTime
() <
timeBound
){
herItem
=
slot.get
(
stampHolder
);
if (stampHolder[0] == BUSY) { slot.set(null, EMPTY); return herItem;
}}
if (slot.CAS(myItem
, null, WAITING, EMPTY)){
throw new TimeoutException(); } else { herItem = slot.get(stampHolder); slot.set(null, EMPTY);
return herItem; }} break; Exchanger State EMPTY
Try to insert myItem and change state to WAITING
Slide187Art of Multiprocessor Programming
187
187
case EMPTY: // slot is free
if (slot.CAS(herItem
,
myItem
, EMPTY, WAITING)) {
while
(
System.nanoTime
() <
timeBound
){
herItem
=
slot.get
(
stampHolder
); if (stampHolder[0] == BUSY) { slot.set(null, EMPTY); return herItem
;
}} if (slot.CAS(
myItem, null, WAITING, EMPTY)){
throw new TimeoutException(); } else { herItem = slot.get(stampHolder);
slot.set(null, EMPTY); return herItem; }} break; Exchanger State EMPTY
Spin until either myItem is taken or timeout
Slide188Art of Multiprocessor Programming
188
188
case EMPTY: // slot is free
if (slot.CAS(herItem
,
myItem
, EMPTY, WAITING)) {
while (
System.nanoTime
() <
timeBound
){
herItem
=
slot.get
(
stampHolder
);
if (stampHolder[0] == BUSY) { slot.set(null, EMPTY);
return
herItem;
}}
if (slot.CAS(myItem, null, WAITING, EMPTY)){ throw new TimeoutException(); } else { herItem = slot.get(stampHolder
); slot.set(null, EMPTY); return herItem; }} break;
Exchanger State EMPTYmyItem was taken, so return herItem
that was put in its place
Slide189Art of Multiprocessor Programming
189
189
case EMPTY: // slot is free
if (slot.CAS(herItem
,
myItem
, EMPTY, WAITING)) {
while (
System.nanoTime
() <
timeBound
){
herItem
=
slot.get
(
stampHolder
);
if (
stampHolder[0] == BUSY) { slot.set(null, EMPTY); return herItem; }}
if (slot.CAS(myItem
,
null, WAITING, EMPTY)){ throw new TimeoutException(); } else { herItem = slot.get(stampHolder
); slot.set(null, EMPTY); return herItem; }} break;
Exchanger State EMPTYOtherwise we ran out of time, try to reset status to EMPTY and time out
Slide190Art of Multiprocessor Programming
190
Art of Multiprocessor Programming© Herlihy-
Shavit 2007
190
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; }} break; Exchanger State EMPTYIf reset failed,someone showed up after all, so take that item
Slide191Art of Multiprocessor Programming
191
191
case EMPTY: // slot is free
if (slot.CAS(herItem
,
myItem
, EMPTY, WAITING)) {
while (
System.nanoTime
() <
timeBound
){
herItem
=
slot.get
(
stampHolder
);
if (
stampHolder[0] == BUSY) { slot.set(null, EMPTY); return herItem;
}}
if (slot.CAS(myItem
, null, WAITING, EMPTY)){
throw new TimeoutException(); } else { herItem = slot.get(stampHolder); slot.set
(null, EMPTY); return herItem; }} break;
Exchanger State EMPTYClear slot and take that item
Slide192Art of Multiprocessor Programming
192
192
case EMPTY: // slot is free
if (slot.CAS(herItem
,
myItem
, EMPTY, WAITING)) {
while (
System.nanoTime
() <
timeBound
){
herItem
=
slot.get
(
stampHolder
);
if (
stampHolder[0] == BUSY) { slot.set(null, EMPTY); return herItem; }}
if (slot.CAS(
myItem, null, WAITING, EMPTY)){
throw new
TimeoutException(); } else { herItem = slot.get(stampHolder); slot.set(null, EMPTY);
return herItem; }} break; Exchanger State EMPTY
If initial CAS failed,then someone else changed status from EMPTY to WAITING,so retry from start
Slide193Art of Multiprocessor Programming
193
193
case
WAITING: // someone waiting for me
if
(
slot.CAS
(
herItem
,
myItem
, WAITING, BUSY))
return
herItem
;
break
;
case BUSY: // others in middle of exchanging break;default: // impossible break; }
}
}}
States WAITING and BUSY
Slide194Art of Multiprocessor Programming
194
194
case WAITING: // someone waiting for me
if (slot.CAS(
herItem
,
myItem
, WAITING, BUSY))
return
herItem
;
break;
case BUSY: // others in middle of exchanging
break;
default: // impossible
break;
}
} }}States WAITING and BUSY
someone is waiting to exchange,
so try to CAS my item in
and change state to
BUSY
Slide195Art of Multiprocessor Programming
195
195
case WAITING: // someone waiting for me
if (slot.CAS
(
herItem
,
myItem
, WAITING, BUSY))
return
herItem
;
break;
case BUSY: // others in middle of exchanging
break;
default: // impossible
break;
} } }}States WAITING and BUSY
If successful, return other’s item,
otherwise someone else took it,
so try again from start
Slide196Art of Multiprocessor Programming
196
196
case WAITING: // someone waiting for me
if (slot.CAS
(
herItem
,
myItem
, WAITING, BUSY))
return
herItem
;
break;
case
BUSY:
// others in middle of exchanging
break
;
default: // impossible
break; } } }}States WAITING and BUSY
If
BUSY
,
other threads exchanging, so start again
Slide197Art of Multiprocessor Programming
197
197
The Exchanger Slot Exchanger is lock-freeBecause the only way an exchange can fail is if others repeatedly succeeded or no-one showed upThe slot we need does not require symmetric exchange
Slide198Art of Multiprocessor Programming
198
198
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) }}
Back to the Stack: the Elimination Array
Slide199Art of Multiprocessor Programming
199
199
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
)
}}
Elimination Array
visit the elimination arraywith fixed value and range
Slide200Art of Multiprocessor Programming
200
200
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)
}}
Elimination Array
Pick a random array entry
Slide201Art of Multiprocessor Programming
201
201
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
) }}
Elimination Array
Exchange value or time out
Slide202Art of Multiprocessor Programming
202
202
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
Slide203Art of Multiprocessor Programming
203
203
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
Slide204Art of Multiprocessor Programming
204
204
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 I failed,
backoff
& try to eliminate
Slide205Art of Multiprocessor Programming
205
205
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 pushed and range to try
Slide206Art of Multiprocessor Programming
206
206
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
pop()
leaves null,so elimination was successful
Slide207Art of Multiprocessor Programming
207
207
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
Otherwise, retry
push()
on lock-free stack
Slide208Art of Multiprocessor Programming
208
208
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
Slide209Art of Multiprocessor Programming
209
209
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 PopIf value not null, other thread is a push(),so elimination succeeded
Slide210Art of Multiprocessor Programming
210
210
SummaryWe 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 (though it is
in the
worst case)
ABA is a real problem, pay attention
Slide211Art of Multiprocessor Programming
211
211
This work is licensed under a
Creative Commons Attribution-
ShareAlike
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.