Linked Lists: Locking, Lock-Free, and Beyond …
Companion slides for
The Art of Multiprocessor Programming
by Maurice Herlihy & Nir Shavit
Art of Multiprocessor Programming
2
Last Lecture: Spin-Locks
CS
Resets lock
upon exit
spin
lock
critical
section
.
.
.
Art of Multiprocessor Programming
3
Today: Concurrent Objects
Art of Multiprocessor Programming
4
Today: Concurrent Objects
Art of Multiprocessor Programming
5
Coarse-Grained Synchronization
Art of Multiprocessor Programming
6
Coarse-Grained Synchronization
Art of Multiprocessor Programming
7
Coarse-Grained Synchronization
Art of Multiprocessor Programming
8
Coarse-Grained Synchronization
Art of Multiprocessor Programming
9
Coarse-Grained Synchronization
Art of Multiprocessor Programming
10
Coarse-Grained Synchronization
Art of Multiprocessor Programming
11
This Lecture
Art of Multiprocessor Programming
12
This Lecture
Art of Multiprocessor Programming
13
First:�Fine-Grained Synchronization
Art of Multiprocessor Programming
14
Second:�Optimistic Synchronization
Art of Multiprocessor Programming
15
Third:�Lazy Synchronization
Art of Multiprocessor Programming
16
Fourth:�Lock-Free Synchronization
Art of Multiprocessor Programming
17
Linked List
Art of Multiprocessor Programming
18
Set Interface
Art of Multiprocessor Programming
19
List-Based Sets
public interface Set<T> {
public boolean add(T x);
public boolean remove(T x);
public boolean contains(T x);
}
Art of Multiprocessor Programming
20
List-Based Sets
public interface Set<T> {
public boolean add(T x);
public boolean remove(T x);
public boolean contains(T x);
}
Add item to set
Art of Multiprocessor Programming
21
List-Based Sets
public interface Set<T> {
public boolean add(T x);
public boolean remove(T x);
public boolean contains(Tt x);
}
Remove item from set
Art of Multiprocessor Programming
22
List-Based Sets
public interface Set<T> {
public boolean add(T x);
public boolean remove(T x);
public boolean contains(T x);
}
Is item in set?
Art of Multiprocessor Programming
23
List Node
public class Node {
public T item;
public int key;
public Node next;
}
Art of Multiprocessor Programming
24
List Node
public class Node {
public T item;
public int key;
public Node next;
}
item of interest
Art of Multiprocessor Programming
25
List Node
public class Node {
public T item;
public int key;
public Node next;
}
Usually hash code
Art of Multiprocessor Programming
26
List Node
public class Node {
public T item;
public int key;
public Node next;
}
Reference to next node
Art of Multiprocessor Programming
27
The List-Based Set
a
b
c
Sorted with Sentinel nodes
(min & max possible keys)
-∞
+∞
Art of Multiprocessor Programming
28
Reasoning about Concurrent Objects
Art of Multiprocessor Programming
29
Specifically …
Art of Multiprocessor Programming
30
Interference
Art of Multiprocessor Programming
31
Interference
Art of Multiprocessor Programming
32
Abstract Data Types
a
b
Art of Multiprocessor Programming
33
Abstract Data Types
a
b
Art of Multiprocessor Programming
34
Rep Invariant (partly)
Art of Multiprocessor Programming
35
Abstraction Map
Art of Multiprocessor Programming
36
Sequential List Based Set
a
c
d
a
b
c
Add()
Remove()
Art of Multiprocessor Programming
37
Sequential List Based Set
a
c
d
b
a
b
c
Add()
Remove()
Art of Multiprocessor Programming
38
Coarse-Grained Locking
a
b
d
Art of Multiprocessor Programming
39
Coarse-Grained Locking
a
b
d
c
Art of Multiprocessor Programming
40
honk!
Coarse-Grained Locking
a
b
d
c
Simple but hotspot + bottleneck
honk!
Art of Multiprocessor Programming
41
Coarse-Grained Locking
Easy, same as synchronized methods
Art of Multiprocessor Programming
42
Fine-grained Locking
Art of Multiprocessor Programming
43
Hand-over-Hand locking
a
b
c
Art of Multiprocessor Programming
44
Hand-over-Hand locking
a
b
c
Art of Multiprocessor Programming
45
Hand-over-Hand locking
a
b
c
Art of Multiprocessor Programming
46
Hand-over-Hand locking
a
b
c
Art of Multiprocessor Programming
47
Hand-over-Hand locking
a
b
c
Art of Multiprocessor Programming
48
Removing a Node
a
b
c
d
remove(b)
Art of Multiprocessor Programming
49
Removing a Node
a
b
c
d
remove(b)
Art of Multiprocessor Programming
50
Removing a Node
a
b
c
d
remove(b)
Art of Multiprocessor Programming
51
Removing a Node
a
b
c
d
remove(b)
Art of Multiprocessor Programming
52
Removing a Node
a
b
c
d
remove(b)
Art of Multiprocessor Programming
53
Removing a Node
a
c
d
remove(b)
Why hold 2 locks?
Art of Multiprocessor Programming
54
Concurrent Removes
a
b
c
d
remove(c)
remove(b)
Art of Multiprocessor Programming
55
Concurrent Removes
a
b
c
d
remove(b)
remove(c)
Art of Multiprocessor Programming
56
Concurrent Removes
a
b
c
d
remove(b)
remove(c)
Art of Multiprocessor Programming
57
Concurrent Removes
a
b
c
d
remove(b)
remove(c)
Art of Multiprocessor Programming
58
Concurrent Removes
a
b
c
d
remove(b)
remove(c)
Art of Multiprocessor Programming
59
Concurrent Removes
a
b
c
d
remove(b)
remove(c)
Art of Multiprocessor Programming
60
Concurrent Removes
a
b
c
d
remove(b)
remove(c)
Art of Multiprocessor Programming
61
Concurrent Removes
a
b
c
d
remove(b)
remove(c)
Art of Multiprocessor Programming
62
Concurrent Removes
a
b
c
d
remove(b)
remove(c)
Art of Multiprocessor Programming
63
Concurrent Removes
a
b
c
d
remove(b)
remove(c)
Art of Multiprocessor Programming
64
Uh, Oh
a
c
d
remove(b)
remove(c)
Art of Multiprocessor Programming
65
Uh, Oh
a
c
d
Bad news, c not removed
remove(b)
remove(c)
Art of Multiprocessor Programming
66
Problem
direct a pointer
to c
b
a
c
b
a
c
Art of Multiprocessor Programming
67
Insight
Art of Multiprocessor Programming
68
Hand-Over-Hand Again
a
b
c
d
remove(b)
Art of Multiprocessor Programming
69
Hand-Over-Hand Again
a
b
c
d
remove(b)
Art of Multiprocessor Programming
70
Hand-Over-Hand Again
a
b
c
d
remove(b)
Art of Multiprocessor Programming
71
Hand-Over-Hand Again
a
b
c
d
remove(b)
Found it!
Art of Multiprocessor Programming
72
Hand-Over-Hand Again
a
b
c
d
remove(b)
Found it!
Art of Multiprocessor Programming
73
Hand-Over-Hand Again
a
c
d
remove(b)
Art of Multiprocessor Programming
74
Removing a Node
a
b
c
d
remove(b)
remove(c)
Art of Multiprocessor Programming
75
Removing a Node
a
b
c
d
remove(b)
remove(c)
Art of Multiprocessor Programming
76
Removing a Node
a
b
c
d
remove(b)
remove(c)
Art of Multiprocessor Programming
77
Removing a Node
a
b
c
d
remove(b)
remove(c)
Art of Multiprocessor Programming
78
Removing a Node
a
b
c
d
remove(b)
remove(c)
Art of Multiprocessor Programming
79
Removing a Node
a
b
c
d
remove(b)
remove(c)
Art of Multiprocessor Programming
80
Removing a Node
a
b
c
d
remove(b)
remove(c)
Art of Multiprocessor Programming
81
Removing a Node
a
b
c
d
remove(b)
remove(c)
Art of Multiprocessor Programming
82
Removing a Node
a
b
c
d
Must acquire
Lock for b
remove(c)
Art of Multiprocessor Programming
83
Removing a Node
a
b
c
d
Cannot acquire lock for b
remove(c)
Art of Multiprocessor Programming
84
Removing a Node
a
b
c
d
Wait!
remove(c)
Art of Multiprocessor Programming
85
Removing a Node
a
b
d
Proceed to remove(b)
Art of Multiprocessor Programming
86
Removing a Node
a
b
d
remove(b)
Art of Multiprocessor Programming
87
Removing a Node
a
b
d
remove(b)
Art of Multiprocessor Programming
88
Removing a Node
a
d
remove(b)
Art of Multiprocessor Programming
89
Removing a Node
a
d
Art of Multiprocessor Programming
90
Remove method
public boolean remove(Item item) {
int key = item.hashCode();
Node pred, curr;
try {
…
} finally {
curr.unlock();
pred.unlock();
}}
Art of Multiprocessor Programming
91
Remove method
public boolean remove(Item item) {
int key = item.hashCode();
Node pred, curr;
try {
…
} finally {
curr.unlock();
pred.unlock();
}}
Key used to order node
Art of Multiprocessor Programming
92
Remove method
public boolean remove(Item item) {
int key = item.hashCode();
Node pred, curr;
try {
…
} finally {
currNode.unlock();
predNode.unlock();
}}
Predecessor and current nodes
Art of Multiprocessor Programming
93
Remove method
public boolean remove(Item item) {
int key = item.hashCode();
Node pred, curr;
try {
…
} finally {
curr.unlock();
pred.unlock();
}}
Make sure locks released
Art of Multiprocessor Programming
94
Remove method
public boolean remove(Item item) {
int key = item.hashCode();
Node pred, curr;
try {
…
} finally {
curr.unlock();
pred.unlock();
}}
Everything else
Art of Multiprocessor Programming
95
Remove method
try {
pred = this.head;
pred.lock();
curr = pred.next;
curr.lock();
…
} finally { … }
Art of Multiprocessor Programming
96
Remove method
try {
pred = this.head;
pred.lock();
curr = pred.next;
curr.lock();
…
} finally { … }
lock pred == head
Art of Multiprocessor Programming
97
Remove method
try {
pred = this.head;
pred.lock();
curr = pred.next;
curr.lock();
…
} finally { … }
Lock current
Art of Multiprocessor Programming
98
Remove method
try {
pred = this.head;
pred.lock();
curr = pred.next;
curr.lock();
…
} finally { … }
Traversing list
Art of Multiprocessor Programming
99
Remove: searching
while (curr.key <= key) {
if (item == curr.item) {
pred.next = curr.next;
return true;
}
pred.unlock();
pred = curr;
curr = curr.next;
curr.lock();
}
return false;
Art of Multiprocessor Programming
100
Remove: searching
while (curr.key <= key) {
if (item == curr.item) {
pred.next = curr.next;
return true;
}
pred.unlock();
pred = curr;
curr = curr.next;
curr.lock();
}
return false;
Search key range
Art of Multiprocessor Programming
101
Remove: searching
while (curr.key <= key) {
if (item == curr.item) {
pred.next = curr.next;
return true;
}
pred.unlock();
pred = curr;
curr = curr.next;
curr.lock();
}
return false;
At start of each loop: curr and pred locked
Art of Multiprocessor Programming
102
Remove: searching
while (curr.key <= key) {
if (item == curr.item) {
pred.next = curr.next;
return true;
}
pred.unlock();
pred = curr;
curr = curr.next;
curr.lock();
}
return false;
If item found, remove node
Art of Multiprocessor Programming
103
Remove: searching
while (curr.key <= key) {
if (item == curr.item) {
pred.next = curr.next;
return true;
}
pred.unlock();
pred = curr;
curr = curr.next;
curr.lock();
}
return false;
If node found, remove it
Art of Multiprocessor Programming
104
Remove: searching
while (curr.key <= key) {
if (item == curr.item) {
pred.next = curr.next;
return true;
}
pred.unlock();
pred = curr;
curr = curr.next;
curr.lock();
}
return false;
Unlock predecessor
Art of Multiprocessor Programming
105
Remove: searching
while (curr.key <= key) {
if (item == curr.item) {
pred.next = curr.next;
return true;
}
pred.unlock();
pred = curr;
curr = curr.next;
curr.lock();
}
return false;
Only one node locked!
Art of Multiprocessor Programming
106
Remove: searching
while (curr.key <= key) {
if (item == curr.item) {
pred.next = curr.next;
return true;
}
pred.unlock();
pred = curr;
curr = curr.next;
curr.lock();
}
return false;
demote current
Art of Multiprocessor Programming
107
Remove: searching
while (curr.key <= key) {
if (item == curr.item) {
pred.next = curr.next;
return true;
}
pred.unlock();
pred = currNode;
curr = curr.next;
curr.lock();
}
return false;
Find and lock new current
Art of Multiprocessor Programming
108
Remove: searching
while (curr.key <= key) {
if (item == curr.item) {
pred.next = curr.next;
return true;
}
pred.unlock();
pred = currNode;
curr = curr.next;
curr.lock();
}
return false;
Lock invariant restored
Art of Multiprocessor Programming
109
Remove: searching
while (curr.key <= key) {
if (item == curr.item) {
pred.next = curr.next;
return true;
}
pred.unlock();
pred = curr;
curr = curr.next;
curr.lock();
}
return false;
Otherwise, not present
Art of Multiprocessor Programming
110
Why does this work?
Art of Multiprocessor Programming
111
Adding Nodes
Art of Multiprocessor Programming
112
Rep Invariant
Art of Multiprocessor Programming
113
Drawbacks
Art of Multiprocessor Programming
114
Optimistic Synchronization
Art of Multiprocessor Programming
115
Optimistic: Traverse without Locking
b
d
e
a
add(c)
Aha!
Art of Multiprocessor Programming
116
Optimistic: Lock and Load
b
d
e
a
add(c)
Art of Multiprocessor Programming
117
Optimistic: Lock and Load
b
d
e
a
add(c)
c
Art of Multiprocessor Programming
118
What could go wrong?
b
d
e
a
add(c)
Aha!
Art of Multiprocessor Programming
119
What could go wrong?
b
d
e
a
add(c)
Art of Multiprocessor Programming
120
What could go wrong?
b
d
e
a
remove(b)
Art of Multiprocessor Programming
121
What could go wrong?
b
d
e
a
remove(b)
Art of Multiprocessor Programming
122
What could go wrong?
b
d
e
a
add(c)
Art of Multiprocessor Programming
123
What could go wrong?
b
d
e
a
add(c)
c
Art of Multiprocessor Programming
124
What could go wrong?
d
e
a
add(c)
Uh-oh
Art of Multiprocessor Programming
125
Validate – Part 1
b
d
e
a
add(c)
Yes, b still reachable from head
Art of Multiprocessor Programming
126
What Else Could Go Wrong?
b
d
e
a
add(c)
Aha!
Art of Multiprocessor Programming
127
What Else Coould Go Wrong?
b
d
e
a
add(c)
add(b’)
Art of Multiprocessor Programming
128
What Else Coould Go Wrong?
b
d
e
a
add(c)
add(b’)
b’
Art of Multiprocessor Programming
129
What Else Could Go Wrong?
b
d
e
a
add(c)
b’
Art of Multiprocessor Programming
130
What Else Could Go Wrong?
b
d
e
a
add(c)
c
Art of Multiprocessor Programming
131
Validate Part 2�(while holding locks)
b
d
e
a
add(c)
Yes, b still points to d
Art of Multiprocessor Programming
132
Optimistic: Linearization Point
b
d
e
a
add(c)
c
Art of Multiprocessor Programming
133
Invariants
Art of Multiprocessor Programming
134
Correctness
Art of Multiprocessor Programming
135
Unsuccessful Remove
a
b
d
e
remove(c)
Aha!
Art of Multiprocessor Programming
136
Validate (1)
a
b
d
e
Yes, b still reachable from head
remove(c)
Art of Multiprocessor Programming
137
Validate (2)
a
b
d
e
remove(c)
Yes, b still points to d
Art of Multiprocessor Programming
138
OK Computer
a
b
d
e
remove(c)
return false
Art of Multiprocessor Programming
139
Correctness
Art of Multiprocessor Programming
140
Validation
private boolean
validate(Node pred,
Node curr) {
Node node = head;
while (node.key <= pred.key) {
if (node == pred)
return pred.next == curr;
node = node.next;
}
return false;
}
Art of Multiprocessor Programming
141
private boolean
validate(Node pred,
Node curr) {
Node node = head;
while (node.key <= pred.key) {
if (node == pred)
return pred.next == curr;
node = node.next;
}
return false;
}
Validation
Predecessor & current nodes
Art of Multiprocessor Programming
142
private boolean
validate(Node pred,
Node curr) {
Node node = head;
while (node.key <= pred.key) {
if (node == pred)
return pred.next == curr;
node = node.next;
}
return false;
}
Validation
Begin at the beginning
Art of Multiprocessor Programming
143
private boolean
validate(Node pred,
Node curr) {
Node node = head;
while (node.key <= pred.key) {
if (node == pred)
return pred.next == curr;
node = node.next;
}
return false;
}
Validation
Search range of keys
Art of Multiprocessor Programming
144
private boolean
validate(Node pred,
Node curr) {
Node node = head;
while (node.key <= pred.key) {
if (node == pred)
return pred.next == curr;
node = node.next;
}
return false;
}
Validation
Predecessor reachable
Art of Multiprocessor Programming
145
private boolean
validate(Node pred,
Node curr) {
Node node = head;
while (node.key <= pred.key) {
if (node == pred)
return pred.next == curr;
node = node.next;
}
return false;
}
Validation
Is current node next?
Art of Multiprocessor Programming
146
private boolean
validate(Node pred,
Node curr) {
Node node = head;
while (node.key <= pred.key) {
if (node == pred)
return pred.next == curr;
node = node.next;
}
return false;
}
Validation
Otherwise move on
Art of Multiprocessor Programming
147
private boolean
validate(Node pred,
Node curr) {
Node node = head;
while (node.key <= pred.key) {
if (node == pred)
return pred.next == curr;
node = node.next;
}
return false;
}
Validation
Predecessor not reachable
Art of Multiprocessor Programming
148
Remove: searching
public boolean remove(Item item) {
int key = item.hashCode();
retry: while (true) {
Node pred = this.head;
Node curr = pred.next;
while (curr.key <= key) {
if (item == curr.item)
break;
pred = curr;
curr = curr.next;
} …
Art of Multiprocessor Programming
149
public boolean remove(Item item) {
int key = item.hashCode();
retry: while (true) {
Node pred = this.head;
Node curr = pred.next;
while (curr.key <= key) {
if (item == curr.item)
break;
pred = curr;
curr = curr.next;
} …
Remove: searching
Search key
Art of Multiprocessor Programming
150
public boolean remove(Item item) {
int key = item.hashCode();
retry: while (true) {
Node pred = this.head;
Node curr = pred.next;
while (curr.key <= key) {
if (item == curr.item)
break;
pred = curr;
curr = curr.next;
} …
Remove: searching
Retry on synchronization conflict
Art of Multiprocessor Programming
151
public boolean remove(Item item) {
int key = item.hashCode();
retry: while (true) {
Node pred = this.head;
Node curr = pred.next;
while (curr.key <= key) {
if (item == curr.item)
break;
pred = curr;
curr = curr.next;
} …
Remove: searching
Examine predecessor and current nodes
Art of Multiprocessor Programming
152
public boolean remove(Item item) {
int key = item.hashCode();
retry: while (true) {
Node pred = this.head;
Node curr = pred.next;
while (curr.key <= key) {
if (item == curr.item)
break;
pred = curr;
curr = curr.next;
} …
Remove: searching
Search by key
Art of Multiprocessor Programming
153
public boolean remove(Item item) {
int key = item.hashCode();
retry: while (true) {
Node pred = this.head;
Node curr = pred.next;
while (curr.key <= key) {
if (item == curr.item)
break;
pred = curr;
curr = curr.next;
} …
Remove: searching
Stop if we find item
Art of Multiprocessor Programming
154
public boolean remove(Item item) {
int key = item.hashCode();
retry: while (true) {
Node pred = this.head;
Node curr = pred.next;
while (curr.key <= key) {
if (item == curr.item)
break;
pred = curr;
curr = curr.next;
} …
Remove: searching
Move along
Art of Multiprocessor Programming
155
On Exit from Loop
Art of Multiprocessor Programming
156
Remove Method
try {
pred.lock(); curr.lock();
if (validate(pred,curr) {
if (curr.item == item) {
pred.next = curr.next;
return true;
} else {
return false;
}}} finally {
pred.unlock();
curr.unlock();
}}}
Art of Multiprocessor Programming
157
try {
pred.lock(); curr.lock();
if (validate(pred,curr) {
if (curr.item == item) {
pred.next = curr.next;
return true;
} else {
return false;
}}} finally {
pred.unlock();
curr.unlock();
}}}
Remove Method
Always unlock
Art of Multiprocessor Programming
158
try {
pred.lock(); curr.lock();
if (validate(pred,curr) {
if (curr.item == item) {
pred.next = curr.next;
return true;
} else {
return false;
}}} finally {
pred.unlock();
curr.unlock();
}}}
Remove Method
Lock both nodes
Art of Multiprocessor Programming
159
try {
pred.lock(); curr.lock();
if (validate(pred,curr) {
if (curr.item == item) {
pred.next = curr.next;
return true;
} else {
return false;
}}} finally {
pred.unlock();
curr.unlock();
}}}
Remove Method
Check for synchronization conflicts
Art of Multiprocessor Programming
160
try {
pred.lock(); curr.lock();
if (validate(pred,curr) {
if (curr.item == item) {
pred.next = curr.next;
return true;
} else {
return false;
}}} finally {
pred.unlock();
curr.unlock();
}}}
Remove Method
target found, remove node
Art of Multiprocessor Programming
161
try {
pred.lock(); curr.lock();
if (validate(pred,curr) {
if (curr.item == item) {
pred.next = curr.next;
return true;
} else {
return false;
}}} finally {
pred.unlock();
curr.unlock();
}}}
Remove Method
target not found
Art of Multiprocessor Programming
162
Optimistic List
Art of Multiprocessor Programming
163
So Far, So Good
Art of Multiprocessor Programming
164
Evaluation
is less than
Art of Multiprocessor Programming
165
Lazy List
Art of Multiprocessor Programming
166
Lazy List
Art of Multiprocessor Programming
167
Lazy Removal
a
a
b
c
d
c
Art of Multiprocessor Programming
168
Lazy Removal
a
a
b
d
Present in list
c
Art of Multiprocessor Programming
169
Lazy Removal
a
a
b
d
Logically deleted
Art of Multiprocessor Programming
170
Lazy Removal
a
a
b
c
d
Physically deleted
Art of Multiprocessor Programming
171
Lazy Removal
a
a
b
d
Physically deleted
Art of Multiprocessor Programming
172
Lazy List
Art of Multiprocessor Programming
173
Validation
Art of Multiprocessor Programming
174
Business as Usual
a
b
c
Art of Multiprocessor Programming
175
Business as Usual
a
b
c
Art of Multiprocessor Programming
176
Business as Usual
a
b
c
Art of Multiprocessor Programming
177
Business as Usual
a
b
c
remove(b)
Art of Multiprocessor Programming
178
Business as Usual
a
b
c
a not marked
Art of Multiprocessor Programming
179
Business as Usual
a
b
c
a still points to b
Art of Multiprocessor Programming
180
Business as Usual
a
b
c
Logical delete
Art of Multiprocessor Programming
181
Business as Usual
a
b
c
physical delete
Art of Multiprocessor Programming
182
Business as Usual
a
b
c
Art of Multiprocessor Programming
183
Validation
private boolean
validate(Node pred, Node curr) {
return
!pred.marked &&
!curr.marked &&
pred.next == curr);
}
Art of Multiprocessor Programming
184
private boolean
validate(Node pred, Node curr) {
return
!pred.marked &&
!curr.marked &&
pred.next == curr);
}
List Validate Method
Predecessor not
Logically removed
Art of Multiprocessor Programming
185
private boolean
validate(Node pred, Node curr) {
return
!pred.marked &&
!curr.marked &&
pred.next == curr);
}
List Validate Method
Current not
Logically removed
Art of Multiprocessor Programming
186
private boolean
validate(Node pred, Node curr) {
return
!pred.marked &&
!curr.marked &&
pred.next == curr);
}
List Validate Method
Predecessor still
Points to current
Art of Multiprocessor Programming
187
Remove
try {
pred.lock(); curr.lock();
if (validate(pred,curr) {
if (curr.key == key) {
curr.marked = true;
pred.next = curr.next;
return true;
} else {
return false;
}}} finally {
pred.unlock();
curr.unlock();
}}}
Art of Multiprocessor Programming
188
Remove
try {
pred.lock(); curr.lock();
if (validate(pred,curr) {
if (curr.key == key) {
curr.marked = true;
pred.next = curr.next;
return true;
} else {
return false;
}}} finally {
pred.unlock();
curr.unlock();
}}}
Validate as before
Art of Multiprocessor Programming
189
Remove
try {
pred.lock(); curr.lock();
if (validate(pred,curr) {
if (curr.key == key) {
curr.marked = true;
pred.next = curr.next;
return true;
} else {
return false;
}}} finally {
pred.unlock();
curr.unlock();
}}}
Key found
Art of Multiprocessor Programming
190
Remove
try {
pred.lock(); curr.lock();
if (validate(pred,curr) {
if (curr.key == key) {
curr.marked = true;
pred.next = curr.next;
return true;
} else {
return false;
}}} finally {
pred.unlock();
curr.unlock();
}}}
Logical remove
Art of Multiprocessor Programming
191
Remove
try {
pred.lock(); curr.lock();
if (validate(pred,curr) {
if (curr.key == key) {
curr.marked = true;
pred.next = curr.next;
return true;
} else {
return false;
}}} finally {
pred.unlock();
curr.unlock();
}}}
physical remove
Art of Multiprocessor Programming
192
Contains
public boolean contains(Item item) {
int key = item.hashCode();
Node curr = this.head;
while (curr.key < key) {
curr = curr.next;
}
return curr.key == key && !curr.marked;
}
Art of Multiprocessor Programming
193
Contains
public boolean contains(Item item) {
int key = item.hashCode();
Node curr = this.head;
while (curr.key < key) {
curr = curr.next;
}
return curr.key == key && !curr.marked;
}
Start at the head
Art of Multiprocessor Programming
194
Contains
public boolean contains(Item item) {
int key = item.hashCode();
Node curr = this.head;
while (curr.key < key) {
curr = curr.next;
}
return curr.key == key && !curr.marked;
}
Search key range
Art of Multiprocessor Programming
195
Contains
public boolean contains(Item item) {
int key = item.hashCode();
Node curr = this.head;
while (curr.key < key) {
curr = curr.next;
}
return curr.key == key && !curr.marked;
}
Traverse without locking
(nodes may have been removed)
Art of Multiprocessor Programming
196
Contains
public boolean contains(Item item) {
int key = item.hashCode();
Node curr = this.head;
while (curr.key < key) {
curr = curr.next;
}
return curr.key == key && !curr.marked;
}
Present and undeleted?
Art of Multiprocessor Programming
197
Summary: Wait-free Contains
a
0
0
0
a
b
c
0
e
1
d
Use Mark bit + list ordering
Art of Multiprocessor Programming
198
Lazy List
a
0
0
0
a
b
c
0
e
1
d
Lazy add() and remove() + Wait-free contains()
Art of Multiprocessor Programming
199
Evaluation
Art of Multiprocessor Programming
200
Traffic Jam
Art of Multiprocessor Programming
201
Reminder: Lock-Free Data Structures
Art of Multiprocessor Programming
202
Lock-free Lists
Art of Multiprocessor Programming
203
a
0
0
0
a
b
c
0
e
1
c
Logical Removal
Physical Removal
Use CAS to verify pointer
is correct
Not enough!
Lock-free Lists
Art of Multiprocessor Programming
204
Problem…
a
0
0
0
a
b
c
0
e
1
c
Logical Removal
Physical Removal
0
d
Node added
Art of Multiprocessor Programming
205
The Solution: Combine Bit and Pointer
a
0
0
0
a
b
c
0
e
1
c
Logical Removal =
Set Mark Bit
Physical
Removal
CAS
0
d
Mark-Bit and Pointer
are CASed together
(AtomicMarkableReference)
Fail CAS: Node not
added after logical
Removal
Art of Multiprocessor Programming
206
Solution
Art of Multiprocessor Programming
207
Marking a Node
address
F
mark bit
Reference
Art of Multiprocessor Programming
208
Extracting Reference & Mark
Public Object get(boolean[] marked);
Art of Multiprocessor Programming
209
Extracting Reference & Mark
Public Object get(boolean[] marked);
Returns reference
Returns mark at array index 0!
Art of Multiprocessor Programming
210
Extracting Reference Only
public boolean isMarked();
Value of mark
Art of Multiprocessor Programming
211
Changing State
Public boolean compareAndSet(
Object expectedRef,
Object updateRef,
boolean expectedMark,
boolean updateMark);
Art of Multiprocessor Programming
212
Changing State
Public boolean compareAndSet(
Object expectedRef,
Object updateRef,
boolean expectedMark,
boolean updateMark);
If this is the current reference …
And this is the current mark …
Art of Multiprocessor Programming
213
Changing State
Public boolean compareAndSet(
Object expectedRef,
Object updateRef,
boolean expectedMark,
boolean updateMark);
…then change to this new reference …
… and this new mark
Art of Multiprocessor Programming
214
Changing State
public boolean attemptMark(
Object expectedRef,
boolean updateMark);
Art of Multiprocessor Programming
215
Changing State
public boolean attemptMark(
Object expectedRef,
boolean updateMark);
If this is the current reference …
Art of Multiprocessor Programming
216
Changing State
public boolean attemptMark(
Object expectedRef,
boolean updateMark);
.. then change to this new mark.
b
CAS
Art of Multiprocessor Programming
217
Removing a Node
a
c
d
remove c
Art of Multiprocessor Programming
218
Removing a Node
a
b
d
remove b
remove c
c
failed
CAS
CAS
Art of Multiprocessor Programming
219
Removing a Node
a
b
d
remove b
remove c
c
Art of Multiprocessor Programming
220
Removing a Node
a
d
remove b
remove c
Art of Multiprocessor Programming
221
Traversing the List
Art of Multiprocessor Programming
222
Lock-Free Traversal�(only Add and Remove)
a
b
c
d
CAS
Uh-oh
pred
curr
pred
curr
Art of Multiprocessor Programming
223
The Window Class
class Window {
public Node pred;
public Node curr;
Window(Node pred, Node curr) {
this.pred = pred; this.curr = curr;
}
}
Art of Multiprocessor Programming
224
The Window Class
class Window {
public Node pred;
public Node curr;
Window(Node pred, Node curr) {
this.pred = pred; this.curr = curr;
}
}
A container for pred and current values
Art of Multiprocessor Programming
225
Using the Find Method
Window window = find(head, key);
Node pred = window.pred;
curr = window.curr;
Art of Multiprocessor Programming
226
Using the Find Method
Window window = find(head, key);
Node pred = window.pred;
curr = window.curr;
Find returns window
Art of Multiprocessor Programming
227
Using the Find Method
Window window = find(head, key);
Node pred = window.pred;
curr = window.curr;
Extract pred and curr
Art of Multiprocessor Programming© Herlihy-Shavit 2007
228
The Find Method
Window window = find(item);
At some instant,
pred
curr
succ
item
or …
Art of Multiprocessor Programming© Herlihy-Shavit 2007
229
The Find Method
Window window = find(item);
At some instant,
pred
curr= null
succ
item
not in list
Art of Multiprocessor Programming
230
Remove
public boolean remove(T item) {
Boolean snip;
while (true) {
Window window = find(head, key);
Node pred = window.pred, curr = window.curr;
if (curr.key != key) {
return false;
} else {
Node succ = curr.next.getReference();
snip = curr.next.compareAndSet(succ, succ, false true);
if (!snip) continue;
pred.next.compareAndSet(curr, succ, false, false);
return true;
}}}
Art of Multiprocessor Programming
231
Remove
public boolean remove(T item) {
Boolean snip;
while (true) {
Window window = find(head, key);
Node pred = window.pred, curr = window.curr;
if (curr.key != key) {
return false;
} else {
Node succ = curr.next.getReference();
snip = curr.next.compareAndSet (succ, succ, false, true);
if (!snip) continue;
pred.next.compareAndSet(curr, succ, false, false);
return true;
}}}
Keep trying
Art of Multiprocessor Programming
232
Remove
public boolean remove(T item) {
Boolean snip;
while (true) {
Window window = find(head, key);
Node pred = window.pred, curr = window.curr;
if (curr.key != key) {
return false;
} else {
Node succ = curr.next.getReference();
snip = curr.next.compareAndSet (succ, succ, false, true);
if (!snip) continue;
pred.next.compareAndSet(curr, succ, false, false);
return true;
}}}
Find neighbors
Art of Multiprocessor Programming
233
Remove
public boolean remove(T item) {
Boolean snip;
while (true) {
Window window = find(head, key);
Node pred = window.pred, curr = window.curr;
if (curr.key != key) {
return false;
} else {
Node succ = curr.next.getReference();
snip = curr.next.compareAndSet(succ, succ, false, true);
if (!snip) continue;
pred.next.compareAndSet(curr, succ, false, false);
return true;
}}}
She’s not there …
Art of Multiprocessor Programming
234
Remove
public boolean remove(T item) {
Boolean snip;
while (true) {
Window window = find(head, key);
Node pred = window.pred, curr = window.curr;
if (curr.key != key) {
return false;
} else {
Node succ = curr.next.getReference();
snip = curr.next.compareAndSet(succ, succ, false, true);
if (!snip) continue;
pred.next.compareAndSet(curr, succ, false, false);
return true;
}}}
Try to mark node as deleted
Art of Multiprocessor Programming
235
Remove
public boolean remove(T item) {
Boolean snip;
while (true) {
Window window = find(head, key);
Node pred = window.pred, curr = window.curr;
if (curr.key != key) {
return false;
} else {
Node succ = curr.next.getReference();
snip = curr.next.compareAndSet(succ, succ, false, true);
if (!snip) continue;
pred.next.compareAndSet(curr, succ, false, false);
return true;
}}}
If it doesn’t work, just retry, if it does, job essentially done
Art of Multiprocessor Programming
236
Remove
public boolean remove(T item) {
Boolean snip;
while (true) {
Window window = find(head, key);
Node pred = window.pred, curr = window.curr;
if (curr.key != key) {
return false;
} else {
Node succ = curr.next.getReference();
snip = curr.next.compareAndSet(succ, succ, false, true);
if (!snip) continue;
pred.next.compareAndSet(curr, succ, false, false);
return true;
}}}
Try to advance reference
(if we don’t succeed, someone else did or will).
a
Art of Multiprocessor Programming
237
Add
public boolean add(T item) {
boolean splice;
while (true) {
Window window = find(head, key);
Node pred = window.pred, curr = window.curr;
if (curr.key == key) {
return false;
} else {
Node node = new Node(item);
node.next = new AtomicMarkableRef(curr, false);
if (pred.next.compareAndSet(curr, node, false, false)) {return true;}
}}}
Art of Multiprocessor Programming
238
Add
public boolean add(T item) {
boolean splice;
while (true) {
Window window = find(head, key);
Node pred = window.pred, curr = window.curr;
if (curr.key == key) {
return false;
} else {
Node node = new Node(item);
node.next = new AtomicMarkableRef(curr, false);
if (pred.next.compareAndSet(curr, node, false, false)) {return true;}
}}}
Item already there.
Art of Multiprocessor Programming
239
Add
public boolean add(T item) {
boolean splice;
while (true) {
Window window = find(head, key);
Node pred = window.pred, curr = window.curr;
if (curr.key == key) {
return false;
} else {
Node node = new Node(item);
node.next = new AtomicMarkableRef(curr, false);
if (pred.next.compareAndSet(curr, node, false, false)) {return true;}
}}}
create new node
Art of Multiprocessor Programming
240
Add
public boolean add(T item) {
boolean splice;
while (true) {
Window window = find(head, key);
Node pred = window.pred, curr = window.curr;
if (curr.key == key) {
return false;
} else {
Node node = new Node(item);
node.next = new AtomicMarkableRef(curr, false);
if (pred.next.compareAndSet(curr, node, false, false)) {return true;}
}}}
Install new node, else retry loop
Art of Multiprocessor Programming
241
Wait-free Contains
public boolean contains(T item) {
boolean marked;
int key = item.hashCode();
Node curr = this.head;
while (curr.key < key)
curr = curr.next;
Node succ = curr.next.get(marked);
return (curr.key == key && !marked[0])
}
Art of Multiprocessor Programming
242
Wait-free Contains
public boolean contains(T item) {
boolean marked;
int key = item.hashCode();
Node curr = this.head;
while (curr.key < key)
curr = curr.next;
Node succ = curr.next.get(marked);
return (curr.key == key && !marked[0])
}
Only diff is that we get and check marked
Art of Multiprocessor Programming
243
Lock-free Find
public Window find(Node head, int key) {
Node pred = null, curr = null, succ = null;
boolean[] marked = {false}; boolean snip;
retry: while (true) {
pred = head;
curr = pred.next.getReference();
while (true) {
succ = curr.next.get(marked);
while (marked[0]) {
…
}
if (curr.key >= key)
return new Window(pred, curr);
pred = curr;
curr = succ;
}
}}
Art of Multiprocessor Programming
244
Lock-free Find
public Window find(Node head, int key) {
Node pred = null, curr = null, succ = null;
boolean[] marked = {false}; boolean snip;
retry: while (true) {
pred = head;
curr = pred.next.getReference();
while (true) {
succ = curr.next.get(marked);
while (marked[0]) {
…
}
if (curr.key >= key)
return new Window(pred, curr);
pred = curr;
curr = succ;
}
}}
If list changes while traversed, start over
Art of Multiprocessor Programming
245
public Window find(Node head, int key) {
Node pred = null, curr = null, succ = null;
boolean[] marked = {false}; boolean snip;
retry: while (true) {
pred = head;
curr = pred.next.getReference();
while (true) {
succ = curr.next.get(marked);
while (marked[0]) {
…
}
if (curr.key >= key)
return new Window(pred, curr);
pred = curr;
curr = succ;
}
}}
Lock-free Find
Start looking from head
Art of Multiprocessor Programming
246
public Window find(Node head, int key) {
Node pred = null, curr = null, succ = null;
boolean[] marked = {false}; boolean snip;
retry: while (true) {
pred = head;
curr = pred.next.getReference();
while (true) {
succ = curr.next.get(marked);
while (marked[0]) {
…
}
if (curr.key >= key)
return new Window(pred, curr);
pred = curr;
curr = succ;
}
}}
Lock-free Find
Move down the list
Art of Multiprocessor Programming
247
public Window find(Node head, int key) {
Node pred = null, curr = null, succ = null;
boolean[] marked = {false}; boolean snip;
retry: while (true) {
pred = head;
curr = pred.next.getReference();
while (true) {
succ = curr.next.get(marked);
while (marked[0]) {
…
}
if (curr.key >= key)
return new Window(pred, curr);
pred = curr;
curr = succ;
}
}}
Lock-free Find
Get ref to successor and current deleted bit
Art of Multiprocessor Programming
248
public Window find(Node head, int key) {
Node pred = null, curr = null, succ = null;
boolean[] marked = {false}; boolean snip;
retry: while (true) {
pred = head;
curr = pred.next.getReference();
while (true) {
succ = curr.next.get(marked);
while (marked[0]) {
…
}
if (curr.key >= key)
return new Window(pred, curr);
pred = curr;
curr = succ;
}
}}
Lock-free Find
Try to remove deleted nodes in path…code details soon
Art of Multiprocessor Programming
249
public Window find(Node head, int key) {
Node pred = null, curr = null, succ = null;
boolean[] marked = {false}; boolean snip;
retry: while (true) {
pred = head;
curr = pred.next.getReference();
while (true) {
succ = curr.next.get(marked);
while (marked[0]) {
…
}
if (curr.key >= key)
return new Window(pred, curr);
pred = curr;
curr = succ;
}
}}
Lock-free Find
If curr key that is greater or equal, return pred and curr
Art of Multiprocessor Programming
250
public Window find(Node head, int key) {
Node pred = null, curr = null, succ = null;
boolean[] marked = {false}; boolean snip;
retry: while (true) {
pred = head;
curr = pred.next.getReference();
while (true) {
succ = curr.next.get(marked);
while (marked[0]) {
…
}
if (curr.key >= key)
return new Window(pred, curr);
pred = curr;
curr = succ;
}
}}
Lock-free Find
Otherwise advance window and loop again
Art of Multiprocessor Programming
251
Lock-free Find
retry: while (true) {
…
while (marked[0]) {
snip = pred.next.compareAndSet(curr,
succ, false, false);
if (!snip) continue retry;
curr = succ;
succ = curr.next.get(marked);
}
…
Art of Multiprocessor Programming
252
Lock-free Find
retry: while (true) {
…
while (marked[0]) {
snip = pred.next.compareAndSet(curr,
succ, false, false);
if (!snip) continue retry;
curr = succ;
succ = curr.next.get(marked);
}
…
Try to snip out node
Art of Multiprocessor Programming
253
Lock-free Find
retry: while (true) {
…
while (marked[0]) {
snip = pred.next.compareAndSet(curr, succ, false, false);
if (!snip) continue retry;
curr = succ;
succ = curr.next.get(marked);
}
…
if predecessor’s next field changed must retry whole traversal
Art of Multiprocessor Programming
254
Lock-free Find
retry: while (true) {
…
while (marked[0]) {
snip = pred.next.compareAndSet(curr,
succ, false, false);
if (!snip) continue retry;
curr = succ;
succ = curr.next.get(marked);
}
…
Otherwise move on to check if next node deleted
Art of Multiprocessor Programming
255
Performance
On 16 node shared memory machine
Benchmark throughput of Java List-based Set
algs. Vary % of Contains() method Calls.
Art of Multiprocessor Programming
256
High Contains Ratio
Lock-free
Lazy list
Coarse Grained
Fine Lock-coupling
Art of Multiprocessor Programming
257
Low Contains Ratio
Lock-free
Lazy list
Coarse Grained
Fine Lock-coupling
Art of Multiprocessor Programming
258
As Contains Ratio Increases
Lock-free
Lazy list
Coarse Grained
Fine Lock-coupling
% Contains()
Art of Multiprocessor Programming
259
Summary
Art of Multiprocessor Programming
260
“To Lock or Not to Lock”
Art of Multiprocessor Programming
261
�This work is licensed under a Creative Commons Attribution-ShareAlike 2.5 License.