linked lists: locking, lock- free, and beyond · lock and load • to move to successor entry for e...
Post on 24-Jan-2021
17 Views
Preview:
TRANSCRIPT
Linked Lists: Locking, Lock-Free, and Beyond …
Maurice HerlihyCS176
Fall 2003
© 2003 Herlihy & Shavit 2
Linked Lists
• We can make effective spin locks– Correct– Work well under contention
• Are we done with concurrent data structures?
© 2003 Herlihy & Shavit 3
Not Over Yet
• Contention– Solved by MCS or CLH locks
• Sequential Bottleneck– No “bag of tricks”
• Linked Lists– Simple data structure– Good testbed
© 2003 Herlihy & Shavit 4
Set Interface
• Unordered collection of objects• No duplicates• Methods
– Add a new object– Remove an object– Test if object is present
© 2003 Herlihy & Shavit 5
List-Based Setspublic interface Set {public boolean add(Object x);public boolean remove(Object x);public boolean contains(Object x);
}
© 2003 Herlihy & Shavit 6
List Entrypublic class Entry {public Object object;public int key;public Entry next;
}
© 2003 Herlihy & Shavit 7
List Entrypublic class Entry {public Object object;public int key;public Entry next;
}
Object of interest
© 2003 Herlihy & Shavit 8
List Entrypublic class Entry {public Object object;public int key;public Entry next;
}
Sort by key value(usually hash code)
© 2003 Herlihy & Shavit 9
List Entrypublic class Entry {public Object object;public int key;public Entry next;
}
Sorting makes iteasy to detect absence
© 2003 Herlihy & Shavit 10
List Entrypublic class Entry {public Object object;public int key;public Entry next;
}
Reference to next entry
© 2003 Herlihy & Shavit 11
List-Based Set
a b c
Sentinel node never deleted(minimum possible key)
© 2003 Herlihy & Shavit 12
Adding an Entry
a b c
b
© 2003 Herlihy & Shavit 13
Removing an Entry
a b c
© 2003 Herlihy & Shavit 14
Nu, What About Concurrency?
• Our bag of tricks– Coarse-grained locks– Fine-grained locks– Optimistic synchronization– Lock-free synchronization
© 2003 Herlihy & Shavit 15
Coarse-Grained Locking
• Easy, same as synchronized methods– “One lock to rule them all …”
• Simple, clearly correct– Deserves respect!
• Works poorly with contention– Queue locks help– But bottleneck still an issue
© 2003 Herlihy & Shavit 16
Fine-grained Locking
• Requires careful thought– “Do not meddle in the affairs of wizards,
for they are subtle and quick to anger”• Split object into pieces
– Each piece has own lock– Methods that work on disjoint pieces
need not exclude each other
© 2003 Herlihy & Shavit 17
Optimistic Synchronization
• Requires very careful thought– “Do not meddle in the affairs of dragons,
for you are crunchy and taste good with ketchup."
• Try it without synchronization– If you win, you win– If not, try it again with synchronization
© 2003 Herlihy & Shavit 18
Lock-Free Synchronization
• Dump locking altogether …– “You take the red pill and you stay in
Wonderland and I show you how deep the rabbit-hole goes”
• No locks, just native atomic methods– Usually compareAndSet
© 2003 Herlihy & Shavit 19
Hand-over-Hand locking
a b c
© 2003 Herlihy & Shavit 20
Removing an Entry
a b c d
remove b
© 2003 Herlihy & Shavit 21
Removing an Entry
a b c d
remove b
remove c
© 2003 Herlihy & Shavit 22
Uh-oh
a b c d
remove b
remove c
© 2003 Herlihy & Shavit 23
Problem
• To delete entry b– Swing entry a’s next field to c
• Problem is,– Someone could delete c concurrently
© 2003 Herlihy & Shavit 24
Insight
• If an entry is locked– No one can delete entry’s successor
• If a thread locks– Entry to be deleted– And its predecessor– Then it works
© 2003 Herlihy & Shavit 25
Hand-Over-Hand Again
a b c d
remove b
Found it!
© 2003 Herlihy & Shavit 26
Removing an Entry
a b c d
remove b
remove c
© 2003 Herlihy & Shavit 27
Removing an Entry
a b c d
remove b
remove c
© 2003 Herlihy & Shavit 28
Remove method
public boolean remove(Object object) {int key = object.hashCode();Entry predEntry, currEntry;try {
…} finally {currEntry.unlock();predEntry.unlock();
}}
© 2003 Herlihy & Shavit 29
Remove method
public boolean remove(Object object) {int key = object.hashCode();Entry prevEntry, currEntry;try {
…} finally {currEntry.unlock();prevEntry.unlock();
}}
Key used to order entry
© 2003 Herlihy & Shavit 30
Remove method
public boolean remove(Object object) {int key = object.hashCode();Entry predEntry, currEntry;try {
…} finally {currEntry.unlock();prevEntry.unlock();
}}
Predecessor and current entries
© 2003 Herlihy & Shavit 31
Remove method
public boolean remove(Object object) {int key = object.hashCode();Entry prevEntry, currEntry;try {
…} finally {currEntry.unlock();predEntry.unlock();
}}
Make sure locks released
© 2003 Herlihy & Shavit 32
Remove method
public boolean remove(Object object) {int key = object.hashCode();Entry prevEntry, currEntry;try {
…} finally {currEntry.unlock();prevEntry.unlock();
}}
Everything else
© 2003 Herlihy & Shavit 33
Remove method
try {predEntry = this.head;predEntry.lock();currEntry = predEntry.next;currEntry.lock();…
} finally { … }
© 2003 Herlihy & Shavit 34
Remove method
try {predEntry = this.head;predEntry.lock();currEntry = predEntry.next;currEntry.lock();…
} finally { … }
lock previous
© 2003 Herlihy & Shavit 35
Remove method
try {prevEntry = this.head;prevEntry.lock();currEntry = predEntry.next;currEntry.lock();…
} finally { … }
Lock current
© 2003 Herlihy & Shavit 36
Remove method
try {prevEntry = this.head;prevEntry.lock();currEntry = prevEntry.next;currEntry.lock();…
} finally { … }
Traversing list
© 2003 Herlihy & Shavit 37
Remove: searching
while (currEntry.key <= key) {if (object == currEntry.object) {predEntry.next = currEntry.next;return true;
}predEntry.unlock();predEntry = currEntry;currEntry = currEntry.next;currEntry.lock();
}return false;
© 2003 Herlihy & Shavit 38
Remove: searching
while (currEntry.key <= key) {if (object == currEntry.object) {prevEntry.next = currEntry.next;return true;
}prevEntry.unlock();prevEntry = currEntry;currEntry = currEntry.next;currEntry.lock();
}return false;
Search key range
© 2003 Herlihy & Shavit 39
Remove: searching
while (currEntry.key <= key) {if (object == currEntry.object) {prevEntry.next = currEntry.next;return true;
}prevEntry.unlock();prevEntry = currEntry;currEntry = currEntry.next;currEntry.lock();
}return false;
At start of each loop: currEntry and predEntry
locked
© 2003 Herlihy & Shavit 40
Remove: searching
while (currEntry.key <= key) {if (object == currEntry.object) {predEntry.next = currEntry.next;return true;
}prevEntry.unlock();prevEntry = currEntry;currEntry = currEntry.next;currEntry.lock();
}return false;If entry found, remove it
© 2003 Herlihy & Shavit 41
Remove: searching
while (currEntry.key <= key) {if (object == currEntry.object) {predEntry.next = currEntry.next;return true;
}prevEntry.unlock();prevEntry = currEntry;currEntry = currEntry.next;currEntry.lock();
}return false;If entry found, remove it
© 2003 Herlihy & Shavit 42
Remove: searching
while (currEntry.key <= key) {if (object == currEntry.object) {prevEntry.next = currEntry.next;return true;
}predEntry.unlock();prevEntry = currEntry;currEntry = currEntry.next;currEntry.lock();
}return false;
Unlock predecessor
© 2003 Herlihy & Shavit 43
Remove: searching
while (currEntry.key <= key) {if (object == currEntry.object) {prevEntry.next = currEntry.next;return true;
}prevEntry.unlock();prevEntry = currEntry;currEntry = currEntry.next;currEntry.lock();
}return false;
Only one entry locked!
© 2003 Herlihy & Shavit 44
Remove: searching
while (currEntry.key <= key) {if (object == currEntry.object) {prevEntry.next = currEntry.next;return true;
}prevEntry.unlock();predEntry = currEntry;currEntry = currEntry.next;currEntry.lock();
}return false;
demote current
© 2003 Herlihy & Shavit 45
Remove: searching
while (currEntry.key <= key) {if (object == currEntry.object) {prevEntry.next = currEntry.next;return true;
}prevEntry.unlock();prevEntry = currEntry;currEntry = currEntry.next;currEntry.lock();
}return false;
Find and lock new current
© 2003 Herlihy & Shavit 46
Remove: searching
while (currEntry.key <= key) {if (object == currEntry.object) {prevEntry.next = currEntry.next;return true;
}prevEntry.unlock();prevEntry = currEntry;currEntry = currEntry.next;currEntry.lock();
}return false;
Lock invariant restored
© 2003 Herlihy & Shavit 47
Remove: searching
while (currEntry.key <= key) {if (object == currEntry.object) {prevEntry.next = currEntry.next;return true;
}prevEntry.unlock();prevEntry = currEntry;currEntry = currEntry.next;currEntry.lock();
}return false;
Otherwise, error 404, dude!
© 2003 Herlihy & Shavit 48
Why does this work?
• To remove entry e– Must lock e– Must lock e’s predecessor
• Therefore, if you lock an entry– It can’t be removed– And neither can its successor
© 2003 Herlihy & Shavit 49
Lock and Load
• To move to successor entry for e– Lock e– Lock e.next– Unlock e
• While traversing– e cannot be removed– e.next cannot be removed
Don’t release e
Until next entry idenified and locked
© 2003 Herlihy & Shavit 50
Adding Entries
• To add entry e– Must lock predecessor– Must lock successor
• Neither can be deleted– (Is successor lock actually required?)
© 2003 Herlihy & Shavit 51
Drawbacks
• Better than coarse-grained lock– Threads can traverse in parallel
• Still not ideal– Long chain of acquire/release– Inefficient
© 2003 Herlihy & Shavit 52
Optimistic Synchronization
• Find entries without locking• Lock entries• Check that everything is OK
© 2003 Herlihy & Shavit 53
Removing an Entry
a b c d
remove c
return true
© 2003 Herlihy & Shavit 54
What Can Go Wrong?
a b c d
remove c
remove b
© 2003 Herlihy & Shavit 55
Check that Entry is Still Accessible
a b c d
remove c
© 2003 Herlihy & Shavit 56
What Can Go Wrong?
a b c d
remove c
Add b’
b’
© 2003 Herlihy & Shavit 57
What Can Go Wrong?
a b c d
remove c
b’
© 2003 Herlihy & Shavit 58
Check that Entries Still Adjacent
a b c d
remove c
© 2003 Herlihy & Shavit 59
Correctness
• If– Entry b and Entry c both locked– Entry b still accessible– Entry c still successor to b
• Then– Neither will be deleted– OK to delete and return true
© 2003 Herlihy & Shavit 60
Removing an Absent Entry
a b d e
remove c
return false
© 2003 Herlihy & Shavit 61
Correctness
• If– Entry b and Entry d both locked– Entry b still accessible– Entry d still successor to b
• Then– Neither will be deleted– No thread can add c after b– OK to return false
© 2003 Herlihy & Shavit 62
Validationprivate booleanvalidate(Entry predEntry,
Entry currEntry) {Entry entry = head;while (entry.key <= predEntry.key) {if (entry == predEntry)return predEntry.next == currEntry;
entry = entry.next;}return false;
}
© 2003 Herlihy & Shavit 63
Validationprivate booleanvalidate(Entry predEntry,
Entry currEntry) {Entry entry = head;while (entry.key <= predEntry.key) {if (entry == predEntry)return predEntry.next == currEntry;
entry = entry.next;}return false;
}
Predecessor & current entries
© 2003 Herlihy & Shavit 64
Validationprivate booleanvalidate(Entry predEntry,
Entry currEntry) {Entry entry = head;while (entry.key <= predEntry.key) {if (entry == predEntry)return predEntry.next == currEntry;
entry = entry.next;}return false;
}Start at the
beginning
© 2003 Herlihy & Shavit 65
Validationprivate booleanvalidate(Entry predEntry,
Entry currEntry) {Entry entry = head;while (entry.key <= predEntry.key) {if (entry == predEntry)return predEntry.next == currEntry;
entry = entry.next;}return false;
}Search range of keys
© 2003 Herlihy & Shavit 66
Validationprivate booleanvalidate(Entry predEntry,
Entry currEntry) {Entry entry = head;while (entry.key <= predEntry.key) {if (entry == predEntry)return predEntry.next == currEntry;entry = entry.next;
}return false;
}Predecessor reachable
© 2003 Herlihy & Shavit 67
Validationprivate booleanvalidate(Entry predEntry,
Entry currEntry) {Entry entry = head;while (entry.key <= predEntry.key) {if (entry == predEntry)return predEntry.next == currEntry;
entry = entry.next;}return false;
}Is current entry next?
© 2003 Herlihy & Shavit 68
Validationprivate booleanvalidate(Entry predEntry,
Entry currEntry) {Entry entry = head;while (entry.key <= predEntry.key) {if (entry == predEntry)return predEntry.next == currEntry;
entry = entry.next;}return false;
}
Otherwise move on
© 2003 Herlihy & Shavit 69
Validationprivate booleanvalidate(Entry predEntry,
Entry currEntry) {Entry entry = head;while (entry.key <= predEntry.key) {if (entry == predEntry)return predEntry.next == currEntry;
entry = entry.next;}return false;
}
Predecessor not reachable
© 2003 Herlihy & Shavit 70
Remove: searchingpublic boolean remove(Object object) {int key = object.hashCode();retry: while (true) {Entry predEntry = this.head;Entry currEntry = predEntry.next;while (currEntry.key <= key) {if (object == currEntry.object)break;
predEntry = currEntry;currEntry = currEntry.next;
} …
© 2003 Herlihy & Shavit 71
Remove: searchingpublic boolean remove(Object object) {int key = object.hashCode();retry: while (true) {Entry prevEntry = this.head;Entry currEntry = prevEntry.next;while (currEntry.key <= key) {if (object == currEntry.object)break;
prevEntry = currEntry;currEntry = currEntry.next;
} … Search key
© 2003 Herlihy & Shavit 72
Remove: searchingpublic boolean remove(Object object) {int key = object.hashCode();retry: while (true) {Entry prevEntry = this.head;Entry currEntry = prevEntry.next;while (currEntry.key <= key) {if (object == currEntry.object)break;
prevEntry = currEntry;currEntry = currEntry.next;
} … Retry on synchronization conflict
© 2003 Herlihy & Shavit 73
Remove: searchingpublic boolean remove(Object object) {int key = object.hashCode();retry: while (true) {Entry predEntry = this.head;Entry currEntry = predEntry.next;while (currEntry.key <= key) {if (object == currEntry.object)break;
prevEntry = currEntry;currEntry = currEntry.next;
} …Examine predecessor and current entries
© 2003 Herlihy & Shavit 74
Remove: searchingpublic boolean remove(Object object) {int key = object.hashCode();retry: while (true) {Entry prevEntry = this.head;Entry currEntry = prevEntry.next;while (currEntry.key <= key) {if (object == currEntry.object)break;
prevEntry = currEntry;currEntry = currEntry.next;
} … Search by key
© 2003 Herlihy & Shavit 75
Remove: searchingpublic boolean remove(Object object) {int key = object.hashCode();retry: while (true) {Entry prevEntry = this.head;Entry currEntry = prevEntry.next;while (currEntry.key <= key) {if (object == currEntry.object)break;
prevEntry = currEntry;currEntry = currEntry.next;
} … Stop if we find object
© 2003 Herlihy & Shavit 76
Remove: searchingpublic boolean remove(Object object) {int key = object.hashCode();retry: while (true) {Entry prevEntry = this.head;Entry currEntry = prevEntry.next;while (currEntry.key <= key) {if (object == currEntry.object)break;
predEntry = currEntry;currEntry = currEntry.next;
} …
Move along
© 2003 Herlihy & Shavit 77
On Exit from Loop
• If object is present– currEntry holds object– predEntry just before currEntry
• If object is absent– currEntry has first higher key– predEntry just before currEntry
• Assuming no synchronization problems
© 2003 Herlihy & Shavit 78
Remove Methodtry {
predEntry.lock(); currEntry.lock();if (validate(predEntry,currEntry) {if (currEntry.object == object) {predEntry.next = currEntry.next;return true;} else {return false;}}} finally {predEntry.unlock();currEntry.unlock();
}}}
© 2003 Herlihy & Shavit 79
Remove Methodtry {
predEntry.lock(); currEntry.lock();if (validate(predEntry,currEntry) {if (currEntry.object == object) {predEntry.next = currEntry.next;return true;} else {return false;}}} finally {predEntry.unlock();currEntry.unlock();
}}}
Always unlock
© 2003 Herlihy & Shavit 80
Remove Methodtry {
predEntry.lock(); currEntry.lock();if (validate(predEntry,currEntry) {if (currEntry.object == object) {predEntry.next = currEntry.next;return true;} else {return false;}}} finally {predEntry.unlock();currEntry.unlock();
}}}
Lock both entries
© 2003 Herlihy & Shavit 81
Remove Methodtry {
predEntry.lock(); currEntry.lock();if (validate(predEntry,currEntry) {if (currEntry.object == object) {predEntry.next = currEntry.next;return true;} else {return false;}}} finally {predEntry.unlock();currEntry.unlock();
}}}
Check for synchronization conflicts
© 2003 Herlihy & Shavit 82
Remove Methodtry {
predEntry.lock(); currEntry.lock();if (validate(predEntry,currEntry) {if (currEntry.object == object) {predEntry.next = currEntry.next;return true;} else {return false;}}} finally {predEntry.unlock();currEntry.unlock();
}}}
Object found, remove entry
© 2003 Herlihy & Shavit 83
Remove Methodtry {
predEntry.lock(); currEntry.lock();if (validate(predEntry,currEntry) {if (currEntry.object == object) {predEntry.next = currEntry.next;return true;} else {return false;}}} finally {predEntry.unlock();currEntry.unlock();
}}}
Object not found
© 2003 Herlihy & Shavit 84
So Far, So Good
• Much less lock acquisition/release– Performance– Concurrency
• Problems– Need to traverse list twice– contains() method acquires locks
• Most common method call
© 2003 Herlihy & Shavit 85
Marked Lists
• Remove Method– Scans list (as before)– Locks predecessor & current (as before)– Marks current entry as removed (new!)– Redirects predecessor’s next (as before)
© 2003 Herlihy & Shavit 86
Marked Lists
• All Methods– Scan list – Do not scan past marked entry– Instead, start over from list head
© 2003 Herlihy & Shavit 87
Business as Usual
a b c d
© 2003 Herlihy & Shavit 88
Interference
a b c d
Remove c
© 2003 Herlihy & Shavit 89
Interference
a b c d
Uh-ohRemove
cmark
© 2003 Herlihy & Shavit 90
Interference
a b c d
markUh-oh
© 2003 Herlihy & Shavit 91
Marking a Node
• AtomicMarkableReference class– Java.util.concurrent.atomic package
address F
mark bit
Reference (31 bits)
© 2003 Herlihy & Shavit 92
Extracting Information
Public Object get(boolean[]);
© 2003 Herlihy & Shavit 93
Extracting Information
Public Object get(boolean[]);
Returns reference
Stores mark at index 0
© 2003 Herlihy & Shavit 94
Extracting Information
public boolean isMarked();
Value of mark
© 2003 Herlihy & Shavit 95
Changing State
Public boolean compareAndSet( Object expectedRef,Object updateRef,boolean expectedMark,boolean updateMark);
© 2003 Herlihy & Shavit 96
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 …
© 2003 Herlihy & Shavit 97
Changing State
Public boolean compareAndSet( Object expectedRef,Object updateRef,boolean expectedMark,boolean updateMark);
…then change to this new reference …
… and this new mark
© 2003 Herlihy & Shavit 98
Changing State
public boolean attemptMark( Object expectedRef,boolean updateMark);
© 2003 Herlihy & Shavit 99
Changing State
public boolean attemptMark(Object expectedRef,boolean updateMark);
If this is the current reference …
© 2003 Herlihy & Shavit 100
Changing State
public boolean attemptMark( Object expectedRef,boolean updateMark);
.. then change to this new mark.
© 2003 Herlihy & Shavit 101
Aside
• We will see that it is often useful to tag pointers with– Boolean values– Integer values
• Sometimes to mark, and sometimes to ensure pointers are unique
© 2003 Herlihy & Shavit 102
List Validate Methodprivate booleanvalidate(Entry predEntry,
Entry currEntry) {return(!predEntry.next.isMarked()) && (!currEntry.next.isMarked()) && (predEntry.next.getReference()== currEntry);
}
© 2003 Herlihy & Shavit 103
List Validate Methodprivate booleanvalidate(Entry predEntry,
Entry currEntry) {return(!predEntry.next.isMarked()) && (!currEntry.next.isMarked()) && (predEntry.next.getReference()== currEntry);
}
Predecessor not removed
© 2003 Herlihy & Shavit 104
List Validate Methodprivate booleanvalidate(Entry predEntry,
Entry currEntry) {return (!predEntry.next.isMarked()) &&(!currEntry.next.isMarked()) && (predEntry.next.getReference()== currEntry);
}
Current not removed
© 2003 Herlihy & Shavit 105
List Validate Methodprivate booleanvalidate(Entry predEntry,
Entry currEntry) {return (!predEntry.next.isMarked()) && (!currEntry.next.isMarked()) &&(predEntry.next.getReference()== currEntry);
}
Next field unchanged
© 2003 Herlihy & Shavit 106
Remove: searchingpublic boolean remove(Object object) {…while (currEntry.key <= key) {
Entry nextEntry = (Entry)currEntry.next.get(mark);
if (mark[0])continue retry;
if (object == currEntry.object)break;
predEntry = currEntry;currEntry = currEntry.next;
} …
© 2003 Herlihy & Shavit 107
Remove: searchingpublic boolean remove(Object object) {…while (currEntry.key <= key) {
Entry nextEntry = (Entry)currEntry.next.get(mark);
if (mark[0])continue retry;
if (object == currEntry.object)break;
predEntry = currEntry;currEntry = currEntry.next;
} …
Atomically read reference & mark
© 2003 Herlihy & Shavit 108
Remove: searchingpublic boolean remove(Object object) {…while (currEntry.key <= key) {
Entry nextEntry = (Entry)currEntry.next.get(mark);
if (mark[0])continue retry;
if (object == currEntry.object)break;
predEntry = currEntry;currEntry = currEntry.next;
} …
Panic if entry removed
© 2003 Herlihy & Shavit 109
Evaluation
• Good:– Contains method doesn’t need to lock– Uncontended calls don’t re-traverse
• Bad– Contended calls do re-traverse– Traffic jam if one thread delays
© 2003 Herlihy & Shavit 110
Traffic Jam
• Any concurrent data structure based on mutual exclusion has a weakness
• If one thread– Enters critical section– And “eats the big muffin” (stops running)
• Cache miss, page fault, descheduled …• Software error, …
– Everyone else using that lock is stuck!
© 2003 Herlihy & Shavit 111
Lock-Free Data Structures
• No matter what …– Some thread will complete method call– Even if others halt at malicious times
• Implies that– You can’t use locks (why?)– Um, that’s why they call it lock-free
© 2003 Herlihy & Shavit 112
Lock-Free ≠Wait-Free
• Wait-free synchronization– Every method call eventually finishes– What everyone really wants
• Lock-free synchronization– Some method call eventually finishes– What we are usually willing to pay for
• Starvation rare in practice …
© 2003 Herlihy & Shavit 113
Lock-Free Lists
• Next logical step• Eliminate locking entirely• Use only compareAndSet()• Invented by Maged Michael, 2003
© 2003 Herlihy & Shavit 114
Adding an Entry
a b c
b
CAS
© 2003 Herlihy & Shavit 115
Removing an Entry
a b cCAS
© 2003 Herlihy & Shavit 116
Removing an Entry
a b c d
remove b
remove c
CAS CAS
© 2003 Herlihy & Shavit 117
Look Familiar?
a b c d
remove b
remove c
© 2003 Herlihy & Shavit 118
Problem
• Method updates entry’s next field• After entry has been removed
© 2003 Herlihy & Shavit 119
Solution
• Use AtomicMarkableReference• Remove in two steps
– Set mark bit in next field– Redirect predecessor’s pointer
• CAS– Fails if mark bit set (entry removed)
© 2003 Herlihy & Shavit 120
Removing an Entry
a b c d
remove c
CAS
© 2003 Herlihy & Shavit 121
Removing an Entry
a b d
remove b
remove c
cCASCAS
failed
© 2003 Herlihy & Shavit 122
Removing an Entry
a b d
remove b
remove c
c
© 2003 Herlihy & Shavit 123
Removing an Entry
a d
remove b
remove c
© 2003 Herlihy & Shavit 124
Traversing the List
• Q: what do you do when you find a “logically” deleted entry in your path?
• A: finish the job.– CAS the predecessor’s next field– Proceed (repeat as needed)
© 2003 Herlihy & Shavit 125
Lock-Free Traversal
a b c dCAS
Uh-oh
© 2003 Herlihy & Shavit 126
The Find Method
pred,curr,next = find(object);
© 2003 Herlihy & Shavit 127
The Find Method
pred,curr,succ = find(object);
At some instant,
pred curr succ
object or …
© 2003 Herlihy & Shavit 128
The Find Method
pred,curr,succ = find(object);
At some instant,
predcurr= null
succ
object not in list
© 2003 Herlihy & Shavit 129
Removepublic boolean remove(Object object) {while (true) {pred,curr,succ = find(object);if (curr == null)return false;
if (!curr.next.attemptMark(succ,true))
continue; pred.next.compareAndSet(curr, succ,
false,false);return true;}}
© 2003 Herlihy & Shavit 130
Removepublic boolean remove(Object object) {while (true) {pred,curr,succ = find(object);if (curr == null)return false;
if (!curr.next.attemptMark(succ,true))
continue; pred.next.compareAndSet(curr, succ,
false,false);return true;}} Keep trying
© 2003 Herlihy & Shavit 131
Removepublic boolean remove(Object object) {while (true) {pred,curr,succ = find(object);if (curr == null)return false;
if (!curr.next.attemptMark(succ,true))
continue; pred.next.compareAndSet(curr, succ,
false,false);return true;}} Find alleged neighbors
© 2003 Herlihy & Shavit 132
Removepublic boolean remove(Object object) {while (true) {pred,curr,succ = find(object);if (curr == null)return false;
if (!curr.next.attemptMark(succ,true))
continue; pred.next.compareAndSet(curr, succ,
false,false);return true;}} She’s not there …
© 2003 Herlihy & Shavit 133
Removepublic boolean remove(Object object) {while (true) {pred,curr,succ = find(object);if (curr == null)return false;
if (!curr.next.attemptMark(succ,true))
continue;pred.next.compareAndSet(curr, succ,
false,false);return true;}}
Try to mark entry as deleted
© 2003 Herlihy & Shavit 134
Removepublic boolean remove(Object object) {while (true) {pred,curr,succ = find(object);if (curr == null)return false;
if (!curr.next.attemptMark(succ,true))
continue; pred.next.compareAndSet(curr, succ,
false,false);return true;}}
If it doesn’t work, just retry
© 2003 Herlihy & Shavit 135
Removepublic boolean remove(Object object) {while (true) {pred,curr,succ = find(object);if (curr == null)return false;
if (!curr.next.attemptMark(succ,true))
continue; pred.next.compareAndSet(curr, succ,
false,false);return true;}}
If it works, our job is (essentially) done
© 2003 Herlihy & Shavit 136
Removepublic boolean remove(Object object) {while (true) {pred,curr,succ = find(object);if (curr == null)return false;
if (!curr.next.attemptMark(succ,true))
continue; pred.next.compareAndSet(curr, succ,
false,false);return true;}}
Try to advance reference(if we don’t succeed, someone else did).
a
© 2003 Herlihy & Shavit 137
Addpublic boolean add(Object object) {while (true) {pred,curr,succ= find(object);if (curr != null)return false;
Entry entry = new Entry(object);entry.next = new AMR(succ,false);if (pred.next.CAS(succ, entry,
false, false))return true;
}}
© 2003 Herlihy & Shavit 138
Addpublic boolean add(Object object) {while (true) {pred,curr,succ= find(object);if (curr != null)return false;
Entry entry = new Entry(object);entry.next = new AMR(succ,false);if (pred.next.CAS(succ, entry,
false, false))return true;
}}
Object already there.
© 2003 Herlihy & Shavit 139
Addpublic boolean add(Object object) {while (true) {pred,curr,succ= find(object);if (curr != null)return false;
Entry entry = new Entry(object);entry.next = new AMR(succ,false);if (pred.next.CAS(succ, entry,
false, false))return true;
}}create new entry
© 2003 Herlihy & Shavit 140
Addpublic boolean add(Object object) {while (true) {pred,curr,succ= find(object);if (curr != null)return false;
Entry entry = new Entry(object);entry.next = new AMR(succ,false);if (pred.next.CAS(succ, entry,
false, false))return true;
}}
Install new entry
© 2003 Herlihy & Shavit 141
Containspublic boolean contains(Object obj){while (true) {prev,curr,succ = find(object);return (curr != null);
}}
© 2003 Herlihy & Shavit 142
Containspublic boolean contains(Object obj){while (true) {prev,curr,succ = find(object);return (curr != null);
}}
Did we find anything?
© 2003 Herlihy & Shavit 143
Findprivate Entry,Entry,Entry
find(Object object) {Entry pred, curr, succ;boolean[] pmark = new boolean[1];boolean[] cmark = new boolean[1];int key = object.hashCode();tryAgain: while (true) {
…}}}
© 2003 Herlihy & Shavit 144
Findprivate Entry,Entry,Entry
find(Object object) {Entry pred, curr, succ;boolean[] pmark = new boolean[1];boolean[] cmark = new boolean[1];int key = object.hashCode();tryAgain: while (true) {
…}}}
The entries we seek
© 2003 Herlihy & Shavit 145
Findprivate Entry,Entry,Entry
find(Object object) {Entry pred, curr, succ;boolean[] pmark = new boolean[1];boolean[] cmark = new boolean[1];int key = object.hashCode();tryAgain: while (true) {
…}}}
Deleted bits for predand curr
© 2003 Herlihy & Shavit 146
Findprivate Entry,Entry,Entry
find(Object object) {Entry pred, curr, succ;boolean[] pmark = new boolean[1];boolean[] cmark = new boolean[1];int key = object.hashCode();tryAgain: while (true) {
…}}}
If list changes while traversed, start over
© 2003 Herlihy & Shavit 147
Findprivate Entry,Entry,Entry
find(Object object) {Entry pred, curr, succ;boolean[] pmark = new boolean[1];boolean[] cmark = new boolean[1];int key = object.hashCode();tryAgain: while (true) {
…}}}
Lock-Free because we start over only if someone else makes progress
© 2003 Herlihy & Shavit 148
FindtryAgain: while (true) {
pred = this.head.getReference();curr = pred.next.get(pmark); while (true) {…
}}} Start with first two entries
© 2003 Herlihy & Shavit 149
FindtryAgain: while (true) {
pred = this.head.getReference();curr = pred.next.get(pmark);while (true) {…
}}}
Move down the list
© 2003 Herlihy & Shavit 150
Find…
while (true) {if (curr == null)return pred, null, succ;succ = curr.next.get(cmark); int ckey = curr.key;if (isChanged(pred.next))continue tryAgain;
}}}
Fell off the end of the list
© 2003 Herlihy & Shavit 151
Find…
while (true) {if (curr == null)return pred, null, succ;succ = curr.next.get(cmark); int ckey = curr.key;if (isChanged(pred.next))continue tryAgain;
}}}
Get ref to successor and current deleted bit
© 2003 Herlihy & Shavit 152
Find…
while (true) {if (curr == null)return pred, null, succ;succ = curr.next.get(cmark); int ckey = curr.key;if (isChanged(pred.next))continue tryAgain;
}}}
Panic if predecessor’s next field changed
© 2003 Herlihy & Shavit 153
Findwhile (true) {…
if (!cmark[0]) {if (curr.object == object)return pred, curr, succ;
else if (ckey <= key) {pred = curr;
} elsereturn prev, null, curr;
} else {…
}}}
If current node is not deleted
© 2003 Herlihy & Shavit 154
Findwhile (true) {…
if (!cmark[0]) {if (curr.object == object)return pred, curr, succ;
else if (ckey <= key) {pred = curr;
} elsereturn prev, null, curr;
} else {…
}}}
Object found
© 2003 Herlihy & Shavit 155
Findwhile (true) {…
if (!cmark[0]) {if (curr.object == object)return pred, curr, succ;
else if (ckey <= key) {pred = curr;
} elsereturn prev, null, curr;
} else {…
}}}Keep looking
© 2003 Herlihy & Shavit 156
Findwhile (true) {…
if (!cmark[0]) {if (curr.object == object)return pred, curr, succ;
else if (ckey <= key) {pred = curr;
} elsereturn prev, null, curr;
} else {…
}}}
Not there, give up
© 2003 Herlihy & Shavit 157
Find…while (true) {…
if (!cmark[0]) {…
} else {if (pred.next.compareAndSet(curr, succ, false, false))continue;
elsecontinue tryAgain;
}
Current entry is logically deleted
© 2003 Herlihy & Shavit 158
Find…while (true) {…
if (!cmark[0]) {…
} else {if (pred.next.compareAndSet(curr, succ, false, false))continue;
elsecontinue tryAgain;
}
Try to redirect predecessor’s next reference
© 2003 Herlihy & Shavit 159
Find…while (true) {…
if (!cmark[0]) {…
} else {if (pred.next.compareAndSet(curr, succ, false, false))continue;
elsecontinue tryAgain;
}
On success, keep going, on failure, start over
© 2003 Herlihy & Shavit 160
Summary
• Coarse-grained locking• Fine-grained locking• Optimistic syncrhronization• Lock-free synchronization
© 2003 Herlihy & Shavit 161
Scratch
a
© 2003 Herlihy & Shavit 162
Scratch
a
© 2003 Herlihy & Shavit 163
Scratch
© 2003 Herlihy & Shavit 164
Scratch
© 2003 Herlihy & Shavit 165
Scratch
© 2003 Herlihy & Shavit 166
Scratch
© 2003 Herlihy & Shavit 167
Removing an Entry
a b cCAS
top related