Linked Lists: Locking, LockFree, and Beyond … Based on the companion slides for The Art of Multiprocessor Programming (Maurice Herlihy & Nir Shavit) Coarse-Grained Synchronization • Each method locks the object – Easy to reason about • In simple cases – Standard Java model • Synchronized blocks and methods • So, are we done? Art of Multiprocessor Programming© Herlihy-Shavit 2007 2 Coarse-Grained Synchronization • Sequential bottleneck – Threads “stand in line” • Adding more threads – Does not improve throughput – Struggle to keep it from getting worse Art of Multiprocessor Programming© Herlihy-Shavit 2007 3 This Lecture • Introduce four “patterns” – Bag of tricks … – Methods that work more than once … • For highly-concurrent objects • Goal: – Concurrent access – More threads, more throughput Art of Multiprocessor Programming© Herlihy-Shavit 2007 4 First: Fine-Grained Synchronization • Instead of using a single lock .. • Split object into – Independently-synchronized components • Methods conflict when they access – The same component … – At the same time Art of Multiprocessor Programming© Herlihy-Shavit 2007 5 Second: Optimistic Synchronization • Search without locking … • If you find it, lock and check … – OK: we are done – Oops: start over • Evaluation – Usually cheaper than locking – Mistakes are expensive Art of Multiprocessor Programming© Herlihy-Shavit 2007 6 Third: Lazy Synchronization • Postpone hard work • Removing components is tricky – Logical removal • Mark component to be deleted – Physical removal • Do what needs to be done Art of Multiprocessor Programming© Herlihy-Shavit 2007 7 Fourth: Lock-Free Synchronization • Don’t use locks at all – Use compareAndSet() & relatives … • Advantages – Robust against asynchrony • Disadvantages – Complex – Sometimes high overhead Art of Multiprocessor Programming© Herlihy-Shavit 2007 8 Linked List • Illustrate these patterns … • Using a list-based Set – Common application – Building block for other apps Art of Multiprocessor Programming© Herlihy-Shavit 2007 9 Set Interface • Unordered collection of items • No duplicates • Methods – add(x) put x in set – remove(x) take x out of set – contains(x) tests if x in set Art of Multiprocessor Programming© Herlihy-Shavit 2007 10 Set Interface public interface Set<T> { public boolean add(T x); public boolean remove(T x); public boolean contains(T x); } Art of Multiprocessor Programming© Herlihy-Shavit 2007 11 Set Interface 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© Herlihy-Shavit 2007 12 Set Interface 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© Herlihy-Shavit 2007 13 Set Interface 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© Herlihy-Shavit 2007 14 List Node public class Node { public T item; public int key; public Node next; } Art of Multiprocessor Programming© Herlihy-Shavit 2007 15 List Node public class Node { public T item; public int key; public Node next; } item of interest Art of Multiprocessor Programming© Herlihy-Shavit 2007 16 List Node public class Node { public T item; public int key; public Node next; } Usually hash code Art of Multiprocessor Programming© Herlihy-Shavit 2007 17 List Node public class Node { public T item; public int key; public Node next; } Reference to next node Art of Multiprocessor Programming© Herlihy-Shavit 2007 18 The List-Based Set -∞ a b c +∞ Sorted with Sentinel nodes (min & max possible keys) Art of Multiprocessor Programming© Herlihy-Shavit 2007 19 Reasoning about Concurrent Objects • Invariant – Property that always holds • Established by – True when object is created – Truth preserved by each method • Each step of each method Art of Multiprocessor Programming© Herlihy-Shavit 2007 20 Specifically … • Invariants preserved by – add() – remove() – contains() • Most steps are trivial – Usually one step tricky – Often linearization point Art of Multiprocessor Programming© Herlihy-Shavit 2007 21 Representation Invariants • Sentinel nodes never added/removed – tail reachable from head • Elements sorted by key (hash value) • No duplicates Art of Multiprocessor Programming© Herlihy-Shavit 2007 22 Interference • Invariants make sense only if methods considered are the only modifiers • Language encapsulation helps – List nodes not visible outside class – This guarantees freedom from interference Art of Multiprocessor Programming© Herlihy-Shavit 2007 23 Abstract Data Types • Concrete representation a b • Abstract Type – {a, b} Art of Multiprocessor Programming© Herlihy-Shavit 2007 24 Abstract Data Types • Meaning of representation given by abstraction map – S( a b ) = {a,b} Art of Multiprocessor Programming© Herlihy-Shavit 2007 25 Abstraction Map • S(head) = { x | there exists a such that a reachable from head and a.item = x } Art of Multiprocessor Programming© Herlihy-Shavit 2007 26 Sequential List Based Set Add() a c d a b c Remove() Art of Multiprocessor Programming© Herlihy-Shavit 2007 27 Sequential List Based Set Add() a c d b c b Remove() a Art of Multiprocessor Programming© Herlihy-Shavit 2007 28 Sequential List Based Set Add() a c d b c b Remove() a Art of Multiprocessor Programming© Herlihy-Shavit 2007 29 Course Grained Locking a b d Art of Multiprocessor Programming© Herlihy-Shavit 2007 30 Course Grained Locking a d b c Art of Multiprocessor Programming© Herlihy-Shavit 2007 31 Course Grained Locking a d b honk! honk! c Simple but hotspot + bottleneck Art of Multiprocessor Programming© Herlihy-Shavit 2007 32 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 Art of Multiprocessor Programming© Herlihy-Shavit 2007 33 Fine-grained Locking • Requires careful thought • Split object into pieces – Each piece has own lock – Methods that work on disjoint pieces need not exclude each other • Allows pipelined list traversal Art of Multiprocessor Programming© Herlihy-Shavit 2007 34 Hand-over-Hand locking a b c Art of Multiprocessor Programming© Herlihy-Shavit 2007 35 Hand-over-Hand locking a b c Art of Multiprocessor Programming© Herlihy-Shavit 2007 36 Hand-over-Hand locking a b c Art of Multiprocessor Programming© Herlihy-Shavit 2007 37 Hand-over-Hand locking a b c Art of Multiprocessor Programming© Herlihy-Shavit 2007 38 Hand-over-Hand locking a b c Art of Multiprocessor Programming© Herlihy-Shavit 2007 39 Removing a Node a b c d remove(b) Art of Multiprocessor Programming© Herlihy-Shavit 2007 40 Removing a Node a b c d remove(b) Art of Multiprocessor Programming© Herlihy-Shavit 2007 41 Removing a Node a b c d remove(b) Art of Multiprocessor Programming© Herlihy-Shavit 2007 42 Removing a Node a b c d remove(b) Art of Multiprocessor Programming© Herlihy-Shavit 2007 43 Removing a Node a b c d remove(b) Art of Multiprocessor Programming© Herlihy-Shavit 2007 44 Removing a Node a c d remove(b) Art of Multiprocessor Programming© Herlihy-Shavit 2007 45 Removing a Node a b c remove(b) Art of Multiprocessor Programming© Herlihy-Shavit 2007 d remove(c) 46 Removing a Node a b c remove(b) Art of Multiprocessor Programming© Herlihy-Shavit 2007 d remove(c) 47 Removing a Node a b c remove(b) Art of Multiprocessor Programming© Herlihy-Shavit 2007 d remove(c) 48 Removing a Node a b c remove(b) Art of Multiprocessor Programming© Herlihy-Shavit 2007 d remove(c) 49 Removing a Node a b c remove(b) Art of Multiprocessor Programming© Herlihy-Shavit 2007 d remove(c) 50 Removing a Node a b c remove(b) Art of Multiprocessor Programming© Herlihy-Shavit 2007 d remove(c) 51 Removing a Node a b c remove(b) Art of Multiprocessor Programming© Herlihy-Shavit 2007 d remove(c) 52 Removing a Node a b c remove(b) Art of Multiprocessor Programming© Herlihy-Shavit 2007 d remove(c) 53 Uh, Oh a c remove(b) Art of Multiprocessor Programming© Herlihy-Shavit 2007 d remove(c) 54 Uh, Oh Bad news a c remove(b) Art of Multiprocessor Programming© Herlihy-Shavit 2007 d remove(c) 55 Problem • To delete node b – Swing node a’s next field to c • Problem is, a b c – Someone could delete c concurrently a b Art of Multiprocessor Programming© Herlihy-Shavit 2007 c 56 Insight • If a node is locked – No one can delete node’s successor • If a thread locks node to be deleted and its predecessor, then it works Art of Multiprocessor Programming© Herlihy-Shavit 2007 57 Hand-Over-Hand Again a b c d remove(b) Art of Multiprocessor Programming© Herlihy-Shavit 2007 58 Hand-Over-Hand Again a b c d remove(b) Art of Multiprocessor Programming© Herlihy-Shavit 2007 59 Hand-Over-Hand Again a b c d remove(b) Art of Multiprocessor Programming© Herlihy-Shavit 2007 60 Hand-Over-Hand Again a b c remove(b) Art of Multiprocessor Programming© Herlihy-Shavit 2007 d Found it! 61 Hand-Over-Hand Again a b c remove(b) Art of Multiprocessor Programming© Herlihy-Shavit 2007 d Found it! 62 Hand-Over-Hand Again a c d remove(b) Art of Multiprocessor Programming© Herlihy-Shavit 2007 63 Removing a Node a b c remove(b) Art of Multiprocessor Programming© Herlihy-Shavit 2007 d remove(c) 64 Removing a Node a b c remove(b) Art of Multiprocessor Programming© Herlihy-Shavit 2007 d remove(c) 65 Removing a Node a b c remove(b) Art of Multiprocessor Programming© Herlihy-Shavit 2007 d remove(c) 66 Removing a Node a b c remove(b) Art of Multiprocessor Programming© Herlihy-Shavit 2007 d remove(c) 67 Removing a Node a b c remove(b) Art of Multiprocessor Programming© Herlihy-Shavit 2007 d remove(c) 68 Removing a Node a b c remove(b) Art of Multiprocessor Programming© Herlihy-Shavit 2007 d remove(c) 69 Removing a Node a b c remove(b) Art of Multiprocessor Programming© Herlihy-Shavit 2007 d remove(c) 70 Removing a Node a b c remove(b) Art of Multiprocessor Programming© Herlihy-Shavit 2007 d remove(c) 71 Removing a Node a b c remove(b) Art of Multiprocessor Programming© Herlihy-Shavit 2007 d remove(c) 72 Removing a Node a b c remove(b) Art of Multiprocessor Programming© Herlihy-Shavit 2007 d remove(c) 73 Removing a Node a b d remove(b) Art of Multiprocessor Programming© Herlihy-Shavit 2007 74 Removing a Node a b d remove(b) Art of Multiprocessor Programming© Herlihy-Shavit 2007 75 Removing a Node a b d remove(b) Art of Multiprocessor Programming© Herlihy-Shavit 2007 76 Removing a Node a remove(b) Art of Multiprocessor Programming© Herlihy-Shavit 2007 d remove(c) 77 Removing a Node a Art of Multiprocessor Programming© Herlihy-Shavit 2007 d 78 Remove method public boolean remove(Item item) { int key = item.hashCode(); Node pred, curr; try { … } finally { curr.unlock(); pred.unlock(); }} Art of Multiprocessor Programming© Herlihy-Shavit 2007 79 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© Herlihy-Shavit 2007 80 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© Herlihy-Shavit 2007 81 Remove method public boolean remove(Item item) { int key = item.hashCode(); Node pred, curr; try { Make sure … locks released } finally { curr.unlock(); pred.unlock(); }} Art of Multiprocessor Programming© Herlihy-Shavit 2007 82 Remove method public boolean remove(Item item) { int key = item.hashCode(); Node pred, curr; try { … } finally { curr.unlock(); Everything else pred.unlock(); }} Art of Multiprocessor Programming© Herlihy-Shavit 2007 83 Remove method try { pred = this.head; pred.lock(); curr = pred.next; curr.lock(); … } finally { … } Art of Multiprocessor Programming© Herlihy-Shavit 2007 84 Remove method Lock pred == head try { pred = this.head; pred.lock(); curr = pred.next; curr.lock(); … } finally { … } Art of Multiprocessor Programming© Herlihy-Shavit 2007 85 Remove method try { pred = this.head; pred.lock(); curr = pred.next; curr.lock(); … } finally { … } Lock current Art of Multiprocessor Programming© Herlihy-Shavit 2007 86 Remove method try { pred = this.head; pred.lock(); curr = pred.next; curr.lock(); … } finally { … } Traversing list Art of Multiprocessor Programming© Herlihy-Shavit 2007 87 Remove: traversing list 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© Herlihy-Shavit 2007 88 Remove: traversing list while (curr.key <= key) { if (item == curr.item) { pred.next = curr.next; return true; } Search key range pred.unlock(); pred = curr; curr = curr.next; curr.lock(); } return false; Art of Multiprocessor Programming© Herlihy-Shavit 2007 89 Remove: traversing list while (curr.key <= key) { if (item == curr.item) { pred.next = curr.next; return true; } At start of each loop: curr pred.unlock(); and pred locked pred = curr; curr = curr.next; curr.lock(); } return false; Art of Multiprocessor Programming© Herlihy-Shavit 2007 90 Remove: traversing list while (curr.key <= key) { if (item == curr.item) { pred.next = curr.next; return true; } pred.unlock(); pred = curr; curr = curr.next; curr.lock(); } return If item false; found, remove node Art of Multiprocessor Programming© Herlihy-Shavit 2007 91 Remove: traversing list Unlock predecessor 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© Herlihy-Shavit 2007 92 Remove: traversing list Only one node locked! 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© Herlihy-Shavit 2007 93 Remove: traversing list while (curr.key <= key) { if (item == curr.item) { demote current pred.next = curr.next; return true; } pred.unlock(); pred = curr; curr = curr.next; curr.lock(); } return false; Art of Multiprocessor Programming© Herlihy-Shavit 2007 94 Remove: traversing list while (curr.key <= key) { if (item == curr.item) Find { and lock pred.next = curr.next; new current return true; } pred.unlock(); pred = currNode; curr = curr.next; curr.lock(); } return false; Art of Multiprocessor Programming© Herlihy-Shavit 2007 95 Remove: traversing list while (curr.key <= key) { if (item == curr.item) { Lock invariant pred.next = curr.next; restored return true; } pred.unlock(); pred = currNode; curr = curr.next; curr.lock(); } return false; Art of Multiprocessor Programming© Herlihy-Shavit 2007 96 Remove: traversing list while (curr.key <= key) { if (item == curr.item) { pred.next = curr.next; return true; Otherwise, not present } pred.unlock(); pred = curr; curr = curr.next; curr.lock(); } return false; Art of Multiprocessor Programming© Herlihy-Shavit 2007 97 Why remove() is linearizable while (curr.key <= key) { if (item == curr.item) { pred.next = curr.next; return true; } pred.unlock(); pred = curr; curr = curr.next; • pred reachable from head curr.lock(); • curr is pred.next } • So curr.item is in the set return false; Art of Multiprocessor Programming© Herlihy-Shavit 2007 98 Why remove() is linearizable while (curr.key <= key) { if (item == curr.item) { pred.next = curr.next; return true; } • pred reachable from head pred.unlock(); • curr is pred.next pred = curr; curr = curr.next;• pred.key < key • key < curr.key curr.lock(); • So item is not in the set } return false; Art of Multiprocessor Programming© Herlihy-Shavit 2007 99 Why remove() is linearizable while (curr.key <= key) { if (item == curr.item) { pred.next = curr.next; return true; } Linearization point pred.unlock(); pred = curr; curr = curr.next; curr.lock(); } return false; Art of Multiprocessor Programming© Herlihy-Shavit 2007 100 Progress • To avoid deadlock, locks must be taken by all methods in the same order • Starvation freedom (if locks are starvation free) Art of Multiprocessor Programming© Herlihy-Shavit 2007 101 Adding Nodes • To add a node: – Traverse list using hand-by-hand locking – Lock predecessor – Lock successor • Neither can be deleted – … and thus item can be inserted inbetween – (Is successor lock actually required?) Art of Multiprocessor Programming© Herlihy-Shavit 2007 102 Same Abstraction Map • S(head) = – { x | there exists a such that • a reachable from head and • a.item = x –} Art of Multiprocessor Programming© Herlihy-Shavit 2007 103 Representation Invariant • Easy to check that – tail always reachable from head – Nodes sorted – No duplicates Art of Multiprocessor Programming© Herlihy-Shavit 2007 104 Drawbacks • Better than coarse-grained lock – Threads can traverse in parallel • Still not ideal – Long chain of acquire/release locks – Inefficient – Threads working at the beginning of the list may block other threads Art of Multiprocessor Programming© Herlihy-Shavit 2007 105 Optimistic Synchronization • Find nodes without locking • Lock nodes • Check that everything is OK Art of Multiprocessor Programming© Herlihy-Shavit 2007 106 Optimistic: Traverse without Locking a add(c) b d e Aha! Art of Multiprocessor Programming© Herlihy-Shavit 2007 107 Optimistic: Lock and Load a b d e add(c) Art of Multiprocessor Programming© Herlihy-Shavit 2007 108 What Can Possibly Go Wrong? a add(c) b d e Aha! Art of Multiprocessor Programming© Herlihy-Shavit 2007 109 What Can Possibly Go Wrong? a b d e add(c) Art of Multiprocessor Programming© Herlihy-Shavit 2007 110 What Can Possibly Go Wrong? a b d e remove(b ) add(c) Art of Multiprocessor Programming© Herlihy-Shavit 2007 111 What Can Possibly Go Wrong? a b d e add(c) Art of Multiprocessor Programming© Herlihy-Shavit 2007 112 What Can Possibly Go Wrong? a add(c) b d e c would be added between b and d, but b is no more reachable (deleted)! Art of Multiprocessor Programming© Herlihy-Shavit 2007 113 Validate (1) a add(c) b d e Yes, b still reachable from head Art of Multiprocessor Programming© Herlihy-Shavit 2007 114 What Else Can Go Wrong? a add(c) b d e Aha! Art of Multiprocessor Programming© Herlihy-Shavit 2007 115 What Else Can Go Wrong? a b d e add(c) Art of Multiprocessor Programming© Herlihy-Shavit 2007 116 What Else Can Go Wrong? b’ a b d e add(b’) add(c) Art of Multiprocessor Programming© Herlihy-Shavit 2007 117 What Else Can Go Wrong? b’ a add(c) b d e add(b’) invalidated by add(c)! Art of Multiprocessor Programming© Herlihy-Shavit 2007 118 Optimistic: Validate(2) a add(c) b d e Yes, b still points to d Art of Multiprocessor Programming© Herlihy-Shavit 2007 119 Optimistic: Linearization Point a b d e c add(c) Art of Multiprocessor Programming© Herlihy-Shavit 2007 120 Same Abstraction Map • S(head) = – { x | there exists a such that • a reachable from head and • a.item = x –} Art of Multiprocessor Programming© Herlihy-Shavit 2007 121 Invariants • Careful: we may traverse deleted nodes • But we establish properties by – Validation – After we lock target nodes Art of Multiprocessor Programming© Herlihy-Shavit 2007 122 Correctness • If – Nodes b and c both locked – Node b still accessible – Node c still successor to b • Then – Neither will be deleted – OK to add and return true Art of Multiprocessor Programming© Herlihy-Shavit 2007 123 Removing a Node a remove(c ) b d e Aha! Art of Multiprocessor Programming© Herlihy-Shavit 2007 124 Validate (1) a remove(c ) b d e Yes, b still reachable from head Art of Multiprocessor Programming© Herlihy-Shavit 2007 125 Validate (2) a remove(c ) b d e Yes, b still points to d Art of Multiprocessor Programming© Herlihy-Shavit 2007 126 OK Computer a remove(c ) b d e return false Art of Multiprocessor Programming© Herlihy-Shavit 2007 127 Correctness • If – Nodes b and d both locked – Node b still accessible – Node d still successor to b • Then – No thread can remove b, d, e – No thread can add a node between b and d or between d and e – OK to remove node or return false Art of Multiprocessor Programming© Herlihy-Shavit 2007 128 Validation private boolean validate(Node pred, Node curry) { Node node = head; while (node.key <= pred.key) { if (node == pred) return pred.next == curr; node = node.next; } return false; } Art of Multiprocessor Programming© Herlihy-Shavit 2007 129 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; Predecessor & } return false;current nodes } Art of Multiprocessor Programming© Herlihy-Shavit 2007 130 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; } Begin at the return false; } beginning Art of Multiprocessor Programming© Herlihy-Shavit 2007 131 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; } Search range of keys return false; } Art of Multiprocessor Programming© Herlihy-Shavit 2007 132 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; Predecessor reachable } Art of Multiprocessor Programming© Herlihy-Shavit 2007 133 Validation private boolean validate(Node pred, Node curry) { Node node = head; while (node.key <= pred.key) { if (node == pred) return pred.next == curr; node = node.next; } return false; Is current node next? } Art of Multiprocessor Programming© Herlihy-Shavit 2007 134 Validation private boolean Otherwise move on 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© Herlihy-Shavit 2007 135 Validation private boolean Predecessor not reachable 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© Herlihy-Shavit 2007 136 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© Herlihy-Shavit 2007 137 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; } … Search key } Art of Multiprocessor Programming© Herlihy-Shavit 2007 138 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; } … Retry on synchronization conflict } Art of Multiprocessor Programming© Herlihy-Shavit 2007 139 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; Examine predecessor and current nodes } … } Art of Multiprocessor Programming© Herlihy-Shavit 2007 140 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; Search by key } … } Art of Multiprocessor Programming© Herlihy-Shavit 2007 141 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; Stop } … if we find item } Art of Multiprocessor Programming© Herlihy-Shavit 2007 142 Remove: searching public boolean remove(Item item) { Move int key = along 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© Herlihy-Shavit 2007 143 On Exit from Loop • If item is present – curr holds item – pred just before curr • If item is absent – curr has first higher key – pred just before curr • Assuming no synchronization problems Art of Multiprocessor Programming© Herlihy-Shavit 2007 144 Remove: details on “…” 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© Herlihy-Shavit 2007 145 Remove: details on “…” try { pred.lock(); curr.lock(); if (validate(pred,curr) { if (curr.item == item) { pred.next = curr.next; return true; } else return false; Always unlock }} finally { pred.unlock(); curr.unlock(); } Art of Multiprocessor Programming© Herlihy-Shavit 2007 146 Remove: details on “…” try { pred.lock(); curr.lock(); if (validate(pred,curr) { if (curr.item == item) { pred.next = curr.next; return true; } else return false; Lock both nodes }} finally { pred.unlock(); curr.unlock(); } Art of Multiprocessor Programming© Herlihy-Shavit 2007 147 Remove: details on “…” try { pred.lock(); curr.lock(); if (validate(pred,curr) { if (curr.item == item) { pred.next = curr.next; return true; Check for synchronization } else return false; conflicts }} finally { pred.unlock(); curr.unlock(); } Art of Multiprocessor Programming© Herlihy-Shavit 2007 148 Remove: details on “…” try { pred.lock(); curr.lock(); if (validate(pred,curr) { if (curr.item == item) { pred.next = curr.next; return true; } else return false; target found, }} finally { remove node pred.unlock(); curr.unlock(); } Art of Multiprocessor Programming© Herlihy-Shavit 2007 149 Remove: details on “…” try { pred.lock(); curr.lock(); if (validate(pred,curr) { if (curr.item == item) { pred.next = curr.next; return true; target not found } else return false; }} finally { pred.unlock(); curr.unlock(); } Art of Multiprocessor Programming© Herlihy-Shavit 2007 150 Optimistic List • Limited hot-spots – Targets of add(), remove(), contains() – No contention on traversals • Moreover – Traversals are wait-free – Food for thought … Art of Multiprocessor Programming© Herlihy-Shavit 2007 151 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 Art of Multiprocessor Programming© Herlihy-Shavit 2007 152 Evaluation • Optimistic is effective if – cost of scanning twice without locks less than – cost of scanning once with locks • Drawback – contains() acquires locks – 90% of calls in many apps Art of Multiprocessor Programming© Herlihy-Shavit 2007 153