Linked Lists: Locking, Lock

Download Report

Transcript Linked Lists: Locking, Lock

Linked Lists: Lazy and
Non-Blocking Synchronization
Based on the companion slides for
The Art of Multiprocessor
Programming
(Maurice Herlihy & Nir Shavit)
Lazy List
• Like optimistic, except
– Scan once
– contains(x) never locks …
• Key insight
– Removing nodes causes trouble
– Do it “lazily”
Art of Multiprocessor Programming© Herlihy-Shavit 2007
2
Lazy List
• remove()
– Scans list (as before)
– Locks predecessor & current (as before)
• Logical delete
– Marks current node as removed (new!)
• Physical delete
– Redirects predecessor’s next (as before)
Art of Multiprocessor Programming© Herlihy-Shavit 2007
3
Lazy Removal
a
b
c
Art of Multiprocessor Programming© Herlihy-Shavit 2007
d
4
Lazy Removal
a
b
c
d
Present in list
Art of Multiprocessor Programming© Herlihy-Shavit 2007
5
Lazy Removal
a
b
c
d
Logically deleted
Art of Multiprocessor Programming© Herlihy-Shavit 2007
6
Lazy Removal
a
b
c
d
Physically deleted
Art of Multiprocessor Programming© Herlihy-Shavit 2007
7
Lazy Removal
a
b
d
Physically deleted
Art of Multiprocessor Programming© Herlihy-Shavit 2007
8
Lazy List
• All Methods
– Scan through locked and marked nodes
– Removing a node doesn’t slow down other
method calls …
• Must still lock pred and curr nodes.
Art of Multiprocessor Programming© Herlihy-Shavit 2007
9
Validation
•
•
•
•
No need to rescan list!
Check that pred is not marked
Check that curr is not marked
Check that pred points to curr
Art of Multiprocessor Programming© Herlihy-Shavit 2007
10
Business as Usual
a
b
c
Art of Multiprocessor Programming© Herlihy-Shavit 2007
11
Business as Usual
a
b
c
Art of Multiprocessor Programming© Herlihy-Shavit 2007
12
Business as Usual
a
b
c
Art of Multiprocessor Programming© Herlihy-Shavit 2007
13
Business as Usual
a
b
c
remove(b)
Art of Multiprocessor Programming© Herlihy-Shavit 2007
14
Business as Usual
a
b
c
a not
marked
Art of Multiprocessor Programming© Herlihy-Shavit 2007
15
Business as Usual
a
b
c
a still
points
to b
Art of Multiprocessor Programming© Herlihy-Shavit 2007
16
Business as Usual
a
b
c
Logical
delete
Art of Multiprocessor Programming© Herlihy-Shavit 2007
17
Business as Usual
a
b
c
physical
delete
Art of Multiprocessor Programming© Herlihy-Shavit 2007
18
Business as Usual
a
b
c
Art of Multiprocessor Programming© Herlihy-Shavit 2007
19
Invariant
• If not marked then item in the set
and reachable from head
and if not yet traversed it is
reachable from pred
• Marked elements may be reachable or
not
Art of Multiprocessor Programming© Herlihy-Shavit 2007
20
New Abstraction Map
• S(head) =
– { x | there exists node a such that
• a reachable from head and
• a.item = x and
• a is unmarked
–}
Art of Multiprocessor Programming© Herlihy-Shavit 2007
21
Validation
private boolean
validate(Node pred, Node curr) {
return
!pred.marked &&
!curr.marked &&
pred.next == curr);
}
Art of Multiprocessor Programming© Herlihy-Shavit 2007
22
List Validate Method
private boolean
validate(Node pred, Node curr) {
return
!pred.marked &&
!curr.marked &&
pred.next == curr);
}
Predecessor not
logically removed
Art of Multiprocessor Programming© Herlihy-Shavit 2007
23
List Validate Method
private boolean
validate(Node pred, Node curr) {
return
!pred.marked &&
!curr.marked &&
pred.next == curr);
}
Current not
logically removed
Art of Multiprocessor Programming© Herlihy-Shavit 2007
24
List Validate Method
private boolean
validate(Node pred, Node curr) {
return
!pred.marked &&
!curr.marked &&
pred.next == curr);
}
Predecessor still
points to current
Art of Multiprocessor Programming© Herlihy-Shavit 2007
25
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© Herlihy-Shavit 2007
26
Remove
try {
pred.lock(); curr.lock();
if (validate(pred,curr) {
if (curr.key == key) {
curr.marked = true;
pred.next = curr.next;
return true;
Validate as before
} else
return false;
}} finally {
pred.unlock();
curr.unlock();
} Art of Multiprocessor Programming© Herlihy-Shavit 2007
27
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;
Key found
}} finally {
pred.unlock();
curr.unlock();
} Art of Multiprocessor Programming© Herlihy-Shavit 2007
28
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(); Logical remove
curr.unlock();
} Art of Multiprocessor Programming© Herlihy-Shavit 2007
29
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(); Physical remove
curr.unlock();
} Art of Multiprocessor Programming© Herlihy-Shavit 2007
30
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© Herlihy-Shavit 2007
31
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© Herlihy-Shavit 2007
32
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© Herlihy-Shavit 2007
33
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© Herlihy-Shavit 2007
34
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© Herlihy-Shavit 2007
35
Summary: Wait-free Contains
a 0
b 0
dc 1
0
e 0
Use Mark bit + Fact that List is ordered
1. Not marked  in the set
2. Marked or missing  not in the set
Art of Multiprocessor Programming© Herlihy-Shavit 2007
36
Lazy List: summary
a 0
b 0
dc 1
0
e 0
Wait-free contains()
Blocking add() and remove(): possible starvation
(if validate fails repeatedly)
Art of Multiprocessor Programming© Herlihy-Shavit 2007
37
Linearizability
• Add and remove use locks: linearization points
chosen as usual (successful remove linearized
when mark bit set to 1)
• Contains does not use locks: pay attention to
unsuccessful calls. Earlier of:
• Point where a removed matching node, or a
node with larger key is found
• Point before a new matching node is added
to the list
Art of Multiprocessor Programming© Herlihy-Shavit 2007
38
Evaluation
• Good:
–
–
–
–
contains() doesn’t lock
In fact, it is wait-free!
Good because typically high % contains()
Uncontended calls don’t re-traverse
• Bad
– Contended calls do re-traverse
– Traffic jam if one thread delays
Art of Multiprocessor Programming© Herlihy-Shavit 2007
39
Traffic Jam
• Any concurrent data structure based on
mutual exclusion has a weakness
• If one thread
– Enters critical section
– And “eats the big muffin”
• Cache miss, page fault, descheduled …
• Software error, …
– Everyone else using that lock is stuck!
Art of Multiprocessor Programming© Herlihy-Shavit 2007
40
Reminder: Lock-Free Data
Structures
• No matter what …
– Some thread will complete method call
– Even if others halt at malicious times
– Weaker than wait-free, yet
• Implies that
– You can’t use locks
Art of Multiprocessor Programming© Herlihy-Shavit 2007
41
Lock-free Lists
• Next logical step
• Eliminate locking entirely
• contains() wait-free and add() and
remove() lock-free
• Use only compareAndSet()
• What could go wrong?
Art of Multiprocessor Programming© Herlihy-Shavit 2007
42
Adding a Node
a
c
d
Art of Multiprocessor Programming© Herlihy-Shavit 2007
43
Adding a Node
a
c
d
b
Art of Multiprocessor Programming© Herlihy-Shavit 2007
44
Adding a Node
a CAS
c
d
b
Art of Multiprocessor Programming© Herlihy-Shavit 2007
45
Adding a Node
a
c
d
b
Art of Multiprocessor Programming© Herlihy-Shavit 2007
47
Removing a Node
a
CAS bCAS
c
remov
e b
Art of Multiprocessor Programming© Herlihy-Shavit 2007
d
remov
e c
48
Look Familiar?
Bad news
a
b
c
remov
e b
Art of Multiprocessor Programming© Herlihy-Shavit 2007
d
remov
e c
49
Problem
• Method updates node’s next field
after node has been removed
Art of Multiprocessor Programming© Herlihy-Shavit 2007
50
Solution
• Use AtomicMarkableReference
• Atomically
– Swing reference and
– Update flag
• Remove in two steps
– Set mark bit in next field
– Redirect predecessor’s pointer
Art of Multiprocessor Programming© Herlihy-Shavit 2007
51
Marking a Node
• AtomicMarkableReference class
– Java.util.concurrent.atomic package
Reference
address
F
mark bit
Art of Multiprocessor Programming© Herlihy-Shavit 2007
52
Extracting Reference & Mark
Public Object get(boolean[] marked);
Art of Multiprocessor Programming© Herlihy-Shavit 2007
53
Extracting Reference & Mark
Public Object get(boolean[] marked);
Returns
reference
Returns mark at
array index 0!
Art of Multiprocessor Programming© Herlihy-Shavit 2007
54
Extracting Reference Only
public boolean isMarked();
Value of
mark
Art of Multiprocessor Programming© Herlihy-Shavit 2007
55
Changing State
Public boolean compareAndSet(
Object expectedRef,
Object updateRef,
boolean expectedMark,
boolean updateMark);
Art of Multiprocessor Programming© Herlihy-Shavit 2007
56
Changing State
If this is the current
reference …
Public boolean compareAndSet(
Object expectedRef,
Object updateRef,
boolean expectedMark,
boolean updateMark);
And this is the
current mark …
Art of Multiprocessor Programming© Herlihy-Shavit 2007
57
Changing State
…then change to this
new reference …
Public boolean compareAndSet(
Object expectedRef,
Object updateRef,
boolean expectedMark,
boolean updateMark);
… and this new
mark
Art of Multiprocessor Programming© Herlihy-Shavit 2007
58
Changing State
public boolean attemptMark(
Object expectedRef,
boolean updateMark);
Art of Multiprocessor Programming© Herlihy-Shavit 2007
59
Changing State
public boolean attemptMark(
Object expectedRef,
boolean updateMark);
If this is the current
reference …
Art of Multiprocessor Programming© Herlihy-Shavit 2007
60
Changing State
public boolean attemptMark(
Object expectedRef,
boolean updateMark);
.. then change to
this new mark.
Art of Multiprocessor Programming© Herlihy-Shavit 2007
61
Removing a Node
a
bCAS
c
d
remov
e c
Art of Multiprocessor Programming© Herlihy-Shavit 2007
62
Removing a Node
failed
a
CAS bCAS
c
remov
e b
Art of Multiprocessor Programming© Herlihy-Shavit 2007
d
remov
e c
63
Removing a Node
a
b
c
remov
e b
Art of Multiprocessor Programming© Herlihy-Shavit 2007
d
remov
e c
64
Removing a Node
a
remov
e b
Art of Multiprocessor Programming© Herlihy-Shavit 2007
d
remov
e c
65
Traversing the List
• Q: what do you do when you find a
“logically” deleted node in your path?
• A: finish the job.
– CAS the predecessor’s next field
– Proceed (repeat as needed)
Art of Multiprocessor Programming© Herlihy-Shavit 2007
66
Lock-Free Traversal
a
CAS
b
c
d
Uh-oh
Art of Multiprocessor Programming© Herlihy-Shavit 2007
67
The Window Class
class Window
public Node
public Node
Window(Node
this.pred
}
}
{
pred;
curr;
pred, Node curr) {
= pred; this.curr = curr;
Art of Multiprocessor Programming© Herlihy-Shavit 2007
68
The Window Class
class Window
public Node
public Node
Window(Node
this.pred
}
}
{
pred;
curr;
pred, Node curr) {
= pred; this.curr = curr;
A container for pred
and current values
Art of Multiprocessor Programming© Herlihy-Shavit 2007
69
Using the Find Method
Window window = find(head, key);
Node pred = window.pred;
curr = window.curr;
Art of Multiprocessor Programming© Herlihy-Shavit 2007
70
Using the Find Method
Window window = find(head, key);
Node pred = window.pred;
curr = window.curr;
Find returns window
Art of Multiprocessor Programming© Herlihy-Shavit 2007
71
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
72
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© Herlihy-Shavit 2007
75
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;
If list changes
curr = pred.next.getReference();
while
while (true) {
succ = curr.next.get(marked);traversed,
while (marked[0]) {
start over.
…
Lock-Free
}
because we
if (curr.key >= key)
start over only
return new Window(pred, curr);
pred = curr;
if someone else
curr = succ;
makes progress
}
}}
Art of Multiprocessor Programming© Herlihy-Shavit 2007
76
Lock-free Find
public Window find(Node head, int key) {
Node pred = null,
curr looking
= null, succ
null;
Start
from= head
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© Herlihy-Shavit 2007
77
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) { Move down the list
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© Herlihy-Shavit 2007
78
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;
Get
ref to successor and
}
}}
bit of current node
Art of Multiprocessor Programming© Herlihy-Shavit 2007
mark
79
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;
Try curr
to remove
= succ; marked nodes in
}
}}
path… code details soon
Art of Multiprocessor Programming© Herlihy-Shavit 2007
80
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();
If curr key is greater or equal,
while (true) {
return pred and curr
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© Herlihy-Shavit 2007
81
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);
Otherwise advance window and
while (marked[0]) {
loop again
…
}
if (curr.key >= key)
return new Window(pred, curr);
pred = curr;
curr = succ;
}
}}
Art of Multiprocessor Programming© Herlihy-Shavit 2007
82
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© Herlihy-Shavit 2007
83
Lock-free Find
Try to snip out node
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© Herlihy-Shavit 2007
84
Lock-free Find
If predecessor’s next field
changed must retry whole
traversal
{
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© Herlihy-Shavit 2007
85
Lock-free Find
Otherwise move on to
check if next node deleted
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© Herlihy-Shavit 2007
86
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.attemptMark(succ, true);
if (!snip) continue;
pred.next.compareAndSet(curr, succ, false, false);
return true;
}}}
Art of Multiprocessor Programming© Herlihy-Shavit 2007
87
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.attemptMark(succ, true);
if (!snip) continue;
pred.next.compareAndSet(curr, succ, false, false);
return true;
Keep trying
}}}
Art of Multiprocessor Programming© Herlihy-Shavit 2007
88
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.attemptMark(succ, true);
if (!snip) continue;
pred.next.compareAndSet(curr, succ, false, false);
return true;
}}}
Find neighbors
Art of Multiprocessor Programming© Herlihy-Shavit 2007
89
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.attemptMark(succ, true);
if (!snip) continue;
pred.next.compareAndSet(curr, succ, false, false);
return true;
Key is not there …
}}}
Art of Multiprocessor Programming© Herlihy-Shavit 2007
90
Remove
public boolean remove(T item) {
Boolean snip;
Try to mark node as deleted
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.attemptMark(succ, true);
if (!snip) continue;
pred.next.compareAndSet(curr, succ, false, false);
return true;
}}}
Art of Multiprocessor Programming© Herlihy-Shavit 2007
91
Remove
public boolean remove(T item) {
If it snip;
doesn’t
Boolean
while (true)
{
work,
just retry.
Window window = find(head, key);
If itpred
does,
job
Node
= window.pred,
curr = window.curr;
if (curr.keydone
!= key) {
essentially
return false;
} else {
Node succ = curr.next.getReference();
snip = curr.next.attemptMark(succ, true);
if (!snip) continue;
pred.next.compareAndSet(curr, succ, false, false);
return true;
}}}
Art of Multiprocessor Programming© Herlihy-Shavit 2007
92
Remove
public boolean remove(T item) {
Boolean snip;
while (true) {
a
Window window = find(head, key);
Node pred = window.pred, curr = window.curr;
if (curr.key != key) {
return false;
Try
to advance reference
} else {
(if we
don’t
succeed, someone else did or will).
Node
succ
= curr.next.getReference();
snip = curr.next.attemptMark(succ, true);
if (!snip) continue;
pred.next.compareAndSet(curr, succ, false, false);
return true;
}}}
Art of Multiprocessor Programming© Herlihy-Shavit 2007
93
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© Herlihy-Shavit 2007
94
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© Herlihy-Shavit 2007
95
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© Herlihy-Shavit 2007
96
Add
public boolean add(T item) {
boolean splice;
Install new node,
while (true) {
else retry loop
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© Herlihy-Shavit 2007
97
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© Herlihy-Shavit 2007
98
Wait-free Contains
public boolean contains(T item) {
Only diff is that we
boolean marked;
use method get and
int key = item.hashCode();
Node curr = this.head;
check array marked
while (curr.key < key)
curr = curr.next;
Node succ = curr.next.get(marked);
return (curr.key == key && !marked[0])
}
Art of Multiprocessor Programming© Herlihy-Shavit 2007
99
Summary: Lock-free Removal
Logical Removal =
Set Mark Bit
a 0
b 0
Use CAS to verify pointer
is correct
Not enough!
cc 1
0
e 0
Physical
Removal
CAS pointer
Art of Multiprocessor Programming© Herlihy-Shavit 2007
100
Lock-free Removal
Logical Removal =
Set Mark Bit
a 0
b 0
cc 1
0
Problem:
Physical
d not added to list…
Removal
Must Prevent
CAS
manipulation of
removed node’s pointer
e 0
d 0
Node added
Before
Physical
Removal CAS
Art of Multiprocessor Programming© Herlihy-Shavit 2007
101
Our Solution: Combine Bit and
Pointer
Logical Removal =
Set Mark Bit
a 0
b 0
e 0
cc 1
0
d 0
Mark-Bit and Pointer
are CASed together
Physical
Removal Fail CAS: Node not
added after logical
CAS
Removal
Art of Multiprocessor Programming© Herlihy-Shavit 2007
102
A Lock-free Algorithm
a 0
b 0
cc 1
0
e 0
1. Lock-free add() and remove() physically
remove marked nodes
2. Wait-free find() traverses both marked and
removed nodes
Art of Multiprocessor Programming© Herlihy-Shavit 2007
103
Performance
On 16 node shared memory machine
Benchmark throughput of Java List-based Set
algs. Vary % of Contains() method Calls.
Art of Multiprocessor Programming© Herlihy-Shavit 2007
104
High Contains Ratio
Lock-free
Lazy list
Course Grained
Fine Lock-coupling
Art of Multiprocessor Programming© Herlihy-Shavit 2007
105
Low Contains Ratio
Lock-free
Lazy list
Course Grained
Fine Lock-coupling
Art of Multiprocessor Programming© Herlihy-Shavit 2007
106
As Contains Ratio Increases
Lock-free
Lazy list
Course Grained
Fine Lock-coupling
% Contains()
Art of Multiprocessor Programming© Herlihy-Shavit 2007
107
Summary
•
•
•
•
•
Coarse-grained locking
Fine-grained locking
Optimistic synchronization
Lazy synchronization
Lock-free synchronization
Art of Multiprocessor Programming© Herlihy-Shavit 2007
108
“To Lock or Not to Lock”
• Locking vs. Non-blocking: Extremist views
on both sides
• The answer: nobler to compromise,
combine locking and non-blocking
– Example: Lazy list combines blocking add()
and remove() and a wait-free contains()
– Blocking/non-blocking is a property of a
method
Art of Multiprocessor Programming© Herlihy-Shavit 2007
109
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 2007
110