You are on page 1of 256

Linked Lists: Locking, Lock-

Free, and Beyond …

Companion slides for


The Art of Multiprocessor
Programming
by Maurice Herlihy & Nir Shavit
Last Lecture: Spin-Locks

.. spin
CS

critical Resets lock


lock section upon exit

Art of Multiprocessor Programming 2


Today: Concurrent Objects

• Adding threads should not lower


throughput
– Contention effects
– Mostly fixed by Queue locks
• Should increase throughput
– Not possible if inherently sequential
– Surprising things are parallelizable

Art of Multiprocessor Programming 3


Coarse-Grained
Synchronization
• Each method locks the object
– Avoid contention using queue locks
– Easy to reason about
• In simple cases
– Standard Java model
• Synchronized blocks and methods
• So, are we done?

Art of Multiprocessor Programming 4


Coarse-Grained
Synchronization
• Sequential bottleneck
– Threads “stand in line”
• Adding more threads
– Does not improve throughput
– Struggle to keep it from getting worse
• So why even use a multiprocessor?
– Well, some apps inherently parallel …

Art of Multiprocessor Programming 5


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 6


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 7


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 8


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 9


Fourth:
Lock-Free Synchronization
• Don’t use locks at all
– Use compareAndSet() & relatives …
• Advantages
– No Scheduler Assumptions/Support
• Disadvantages
– Complex
– Sometimes high overhead

Art of Multiprocessor Programming 10


Linked List
• Illustrate these patterns …
• Using a list-based Set
– Common application
– Building block for other apps

Art of Multiprocessor Programming 11


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 12


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 13


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 14


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 15


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 16


List Node
public class Node {
public T item;
public int key;
public Node next;
}

Art of Multiprocessor Programming 17


List Node
public class Node {
public T item;
public int key;
public Node next;
}

item of interest

Art of Multiprocessor Programming 18


List Node
public class Node {
public T item;
public int key;
public Node next;
}

Usually hash code

Art of Multiprocessor Programming 19


List Node
public class Node {
public T item;
public int key;
public Node next;
}

Reference to next node

Art of Multiprocessor Programming 20


The List-Based Set

-∞ a b c

+∞

Sorted with Sentinel nodes


(min & max possible keys)
Art of Multiprocessor Programming 21
Reasoning about Concurrent
Objects
• Invariant
– Property that always holds
• Established because
– True when object is created
– Truth preserved by each method
• Each step of each method

Art of Multiprocessor Programming 22


Specifically …
• Invariants preserved by
– add()
– remove()
– contains()
• Most steps are trivial
– Usually one step tricky
– Often linearization point

Art of Multiprocessor Programming 23


Interference
• Invariants make sense only if
– methods considered
– are the only modifiers
• Language encapsulation helps
– List nodes not visible outside class

Art of Multiprocessor Programming 24


Interference
• Freedom from interference needed
even for removed nodes
– Some algorithms traverse removed
nodes
– Careful with malloc() & free()!
• Garbage-collection helps here

Art of Multiprocessor Programming 25


Abstract Data Types
• Concrete representation

a b

• Abstract Type
– {a, b}

Art of Multiprocessor Programming 26


Abstract Data Types
• Meaning of rep given by abstraction
map

– S( a b ) = {a,b}

Art of Multiprocessor Programming 27


Rep Invariant
• Which concrete values meaningful?
– Sorted?
– Duplicates?
• Rep invariant
– Characterizes legal concrete reps
– Preserved by methods
– Relied on by methods

Art of Multiprocessor Programming 28


Blame Game
• Rep invariant is a contract
• Suppose
– add() leaves behind 2 copies of x
– remove() removes only 1
• Which one is incorrect?

Art of Multiprocessor Programming 29


Blame Game
• Suppose
– add() leaves behind 2 copies of x
– remove() removes only 1
• Which one is incorrect?
– If rep invariant says no duplicates
• add() is incorrect
– Otherwise
• remove() is incorrect
Art of Multiprocessor Programming 30
Rep Invariant (partly)
• Sentinel nodes
– tail reachable from head
• Sorted
• No duplicates

Art of Multiprocessor Programming 31


Abstraction Map
• S(head) =
– { x | there exists a such that
• a reachable from head and
• a.item = x
–}

Art of Multiprocessor Programming 32


Sequential List Based Set
Add()
a c d

Remove()
a b c

Art of Multiprocessor Programming 33


Sequential List Based Set
Add()
a c d

b
Remove()
a b c

Art of Multiprocessor Programming 34


Course Grained Locking

a b d

Art of Multiprocessor Programming 35


Course Grained Locking

a b d

Art of Multiprocessor Programming 36


Course Grained Locking

a b d
honk!

c
honk!

Simple but hotspot + bottleneck

Art of Multiprocessor Programming 37


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 38


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

Art of Multiprocessor Programming 39


Hand-over-Hand locking

a b c

Art of Multiprocessor Programming 40


Hand-over-Hand locking

a b c

Art of Multiprocessor Programming 41


Hand-over-Hand locking

a b c

Art of Multiprocessor Programming 42


Hand-over-Hand locking

a b c

Art of Multiprocessor Programming 43


Hand-over-Hand locking

a b c

Art of Multiprocessor Programming 44


Removing a Node

a b c d

remove(b)

Art of Multiprocessor Programming 45


Removing a Node

a b c d

remove(b)

Art of Multiprocessor Programming 46


Removing a Node

a b c d

remove(b)

Art of Multiprocessor Programming 47


Removing a Node

a b c d

remove(b)

Art of Multiprocessor Programming 48


Removing a Node

a b c d

remove(b)

Art of Multiprocessor Programming 49


Removing a Node

a c d

Why do we need
remove(b)
to always hold 2
locks?

Art of Multiprocessor Programming 50


Concurrent Removes

a b c d

remove(c)
remove(b)

Art of Multiprocessor Programming 51


Concurrent Removes

a b c d

remove(c)
remove(b)

Art of Multiprocessor Programming 52


Concurrent Removes

a b c d

remove(c)
remove(b)

Art of Multiprocessor Programming 53


Concurrent Removes

a b c d

remove(c)
remove(b)

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(c)
remove(b)

Art of Multiprocessor Programming 56


Concurrent Removes

a b c d

remove(c)
remove(b)

Art of Multiprocessor Programming 57


Concurrent Removes

a b c d

remove(c)
remove(b)

Art of Multiprocessor Programming 58


Uh, Oh

a c d

remove(c)
remove(b)

Art of Multiprocessor Programming 59


Uh, Oh
Bad news, C not removed

a c d

remove(c)
remove(b)

Art of Multiprocessor Programming 60


Problem
• To delete node c
– Swing node b’s next field to d
a b c
• Problem is,
– Someone deleting b concurrently could
direct a pointer
a b c
to c

Art of Multiprocessor Programming 61


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 62


Hand-Over-Hand Again

a b c d

remove(b)

Art of Multiprocessor Programming 63


Hand-Over-Hand Again

a b c d

remove(b)

Art of Multiprocessor Programming 64


Hand-Over-Hand Again

a b c d

remove(b)

Art of Multiprocessor Programming 65


Hand-Over-Hand Again

a b c d

Found
remove(b) it!

Art of Multiprocessor Programming 66


Hand-Over-Hand Again

a b c d

Found
remove(b) it!

Art of Multiprocessor Programming 67


Hand-Over-Hand Again

a c d

remove(b)

Art of Multiprocessor Programming 68


Removing a Node

a b c d

remove(c)
remove(b)

Art of Multiprocessor Programming 69


Removing a Node

a b c d

remove(c)
remove(b)

Art of Multiprocessor Programming 70


Removing a Node

a b c d

remove(c)
remove(b)

Art of Multiprocessor Programming 71


Removing a Node

a b c d

remove(c)
remove(b)

Art of Multiprocessor Programming 72


Removing a Node

a b c d

remove(c)
remove(b)

Art of Multiprocessor Programming 73


Removing a Node

a b c d

remove(c)
remove(b)

Art of Multiprocessor Programming 74


Removing a Node

a b c d

remove(c)
remove(b)

Art of Multiprocessor Programming 75


Removing a Node

a b c d

remove(c)
remove(b)

Art of Multiprocessor Programming 76


Removing a Node

a b c d

remove(c)
Must
acquire
Lock of b

Art of Multiprocessor Programming 77


Removing a Node

a b c d

remove(c)
Cannot
acquire
lock of b

Art of Multiprocessor Programming 78


Removing a Node

a b c d

remove(c)
Wait!

Art of Multiprocessor Programming 79


Removing a Node

a b d

Proceed
to
remove(b)

Art of Multiprocessor Programming 80


Removing a Node

a b d

remove(b)

Art of Multiprocessor Programming 81


Removing a Node

a b d

remove(b)

Art of Multiprocessor Programming 82


Removing a Node

a d

remove(b)

Art of Multiprocessor Programming 83


Removing a Node

a d

Art of Multiprocessor Programming 84


Remove method
public boolean remove(Item item) {
int key = item.hashCode();
Node pred, curr;
try {

} finally {
curr.unlock();
pred.unlock();
}}

Art of Multiprocessor Programming 85


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 86
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 87
Remove method
public boolean remove(Item item) {
int key = item.hashCode();
Node pred, curr;
try {
… Make sure
} finally { locks released
curr.unlock();
pred.unlock();
}}

Art of Multiprocessor Programming 88


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 89


Remove method
try {
pred = this.head;
pred.lock();
curr = pred.next;
curr.lock();

} finally { … }

Art of Multiprocessor Programming 90


Remove method
lock pred == head
try {
pred = this.head;
pred.lock();
curr = pred.next;
curr.lock();

} finally { … }

Art of Multiprocessor Programming 91


Remove method
try { Lock current
pred = this.head;
pred.lock();
curr = pred.next;
curr.lock();

} finally { … }

Art of Multiprocessor Programming 92


Remove method
try {
pred = this.head;
pred.lock(); Traversing list
curr = pred.next;
curr.lock();

} finally { … }

Art of Multiprocessor Programming 93


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 94
Remove: searching
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 95
Remove: searching
while (curr.key <= key) {
if (item == curr.item) {
pred.next = curr.next;
return true;
}
At start of each loop: curr
pred.unlock();
pred = curr; and pred locked
curr = curr.next;
curr.lock();
}
return false;
Art of Multiprocessor Programming 96
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();
}
If item false;
return found, remove node
Art of Multiprocessor Programming 97
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();
}
If nodefalse;
return found, remove it
Art of Multiprocessor Programming 98
Remove: searching
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 99
Remove: searching
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 100
Remove: searching
while (curr.key <= key) {
demote current
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 101
Remove: searching
while (curr.key <= key) {
ifFind and==
(item lock new current
curr.item) {
pred.next = curr.next;
return true;
}
pred.unlock();
pred = currNode;
curr = curr.next;
curr.lock();
}
return false;
Art of Multiprocessor Programming 102
Remove: searching
while (curr.key <= key) {
Lock invariant
if (item restored {
== curr.item)
pred.next = curr.next;
return true;
}
pred.unlock();
pred = currNode;
curr = curr.next;
curr.lock();
}
return false;
Art of Multiprocessor Programming 103
Remove: searching
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 104
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;
curr.lock(); •pred reachable from head
} •curr is pred.next
return false; •So curr.item is in the set
Art of Multiprocessor Programming 106
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;
curr.lock();
} Linearization point if
return false; item is present
Art of Multiprocessor Programming 107
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;
curr.lock();
} Node locked, so no other
return false; thread can remove it ….
Art of Multiprocessor Programming 108
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; Item not present
curr.lock();
}
return false;
Art of Multiprocessor Programming 109
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
} •pred.key < key
return false; •key < curr.key
Art of Multiprocessor Programming 110
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 111
Adding Nodes
• To add node e
– Must lock predecessor
– Must lock successor
• Neither can be deleted
– (Is successor lock actually required?)

Art of Multiprocessor Programming 112


Same Abstraction Map
• S(head) =
– { x | there exists a such that
• a reachable from head and
• a.item = x
–}

Art of Multiprocessor Programming 113


Rep Invariant
• Easy to check that
– tail always reachable from head
– Nodes sorted, no duplicates

Art of Multiprocessor Programming 114


Drawbacks
• Better than coarse-grained lock
– Threads can traverse in parallel
• Still not ideal
– Long chain of acquire/release
– Inefficient

Art of Multiprocessor Programming 115


Optimistic Synchronization
• Find nodes without locking
• Lock nodes
• Check that everything is OK

Art of Multiprocessor Programming 116


Optimistic: Traverse without
Locking

a b d e

add(c) Aha!

Art of Multiprocessor Programming 117


Optimistic: Lock and Load

a b d e

add(c)

Art of Multiprocessor Programming 118


What could go wrong?

a b d e

remove(b
)
add(c) Aha!

Art of Multiprocessor Programming 119


Validate – Part 1
(while holding locks)

a b d e

Yes, b
add(c) still
reachable
from head

Art of Multiprocessor Programming 120


What Else Can Go Wrong?

a b d e

add(c)

Art of Multiprocessor Programming 121


What Else Can Go Wrong?
b’

a b d e

add(b’)
add(c)

Art of Multiprocessor Programming 122


What Else Can Go Wrong?
b’

a b d e

add(c) Aha!

Art of Multiprocessor Programming 123


Validate Part 2
(while holding locks)

a b d e

add(c) Yes, b
still points
to d

Art of Multiprocessor Programming 124


Optimistic: Linearization Point

a b d e

c
add(c)

Art of Multiprocessor Programming 125


Same Abstraction Map
• S(head) =
– { x | there exists a such that
• a reachable from head and
• a.item = x
–}

Art of Multiprocessor Programming 126


Invariants
• Careful: we may traverse deleted
nodes
• But we establish properties by
– Validation
– After we lock target nodes

Art of Multiprocessor Programming 127


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 delete and return true

Art of Multiprocessor Programming 128


Unsuccessful Remove

a b d e

Aha!
remove(c)

Art of Multiprocessor Programming 129


Validate (1)

a b d e

Yes, b still
remove(c) reachable
from head

Art of Multiprocessor Programming 130


Validate (2)

a b d e

remove(c) Yes, b still


points to d

Art of Multiprocessor Programming 131


OK Computer

a b d e

remove(c) return false

Art of Multiprocessor Programming 132


Correctness
• If
– Nodes b and d both locked
– Node b still accessible
– Node d still successor to b
• Then
– Neither will be deleted
– No thread can add c after b
– OK to return false

Art of Multiprocessor Programming 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;
}

Art of Multiprocessor Programming 134


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 135


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; Begin at the
} beginning
Art of Multiprocessor Programming 136
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; Search range of keys
}

Art of Multiprocessor Programming 137


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 138


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 139


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 140


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 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;
} …

Art of Multiprocessor Programming 142


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 143
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 144


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 145


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 146


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 147


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 148


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 149


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 150
Remove Method
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 151
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 { Lock both nodes
pred.unlock();
curr.unlock();
}}}
Art of Multiprocessor Programming 152
Remove Method
try {
pred.lock(); curr.lock();
if (validate(pred,curr) {
if (curr.item == item) {
pred.next = curr.next;
return true;
} else { Check for synchronization
return false; conflicts
}}} finally {
pred.unlock();
curr.unlock();
}}}
Art of Multiprocessor Programming 153
Remove Method
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 {
pred.unlock();
remove node
curr.unlock();
}}}
Art of Multiprocessor Programming 154
Remove Method
try {
pred.lock(); curr.lock();
if (validate(pred,curr) {
if (curr.item == item) {
pred.next = curr.next;
return true;
} else { target not found
return false;
}}} finally {
pred.unlock();
curr.unlock();
}}}
Art of Multiprocessor Programming 155
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 156


So Far, So Good
• Much less lock acquisition/release
– Performance
– Concurrency
• Problems
– Need to traverse list twice
– contains() method acquires locks

Art of Multiprocessor Programming 157


Evaluation
• Optimistic is effective if
– cost of scanning twice without locks
is less than
– cost of scanning once with locks
• Drawback
– contains() acquires locks
– 90% of calls in many apps

Art of Multiprocessor Programming 158


Lazy List
• Like optimistic, except
– Scan once
– contains(x) never locks …
• Key insight
– Removing nodes causes trouble
– Do it “lazily”

Art of Multiprocessor Programming 159


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 160


Lazy Removal

a b c d

Art of Multiprocessor Programming 161


Lazy Removal

a b c d

Present in list

Art of Multiprocessor Programming 162


Lazy Removal

a b c d

Logically deleted

Art of Multiprocessor Programming 163


Lazy Removal

a b c d

Physically deleted

Art of Multiprocessor Programming 164


Lazy Removal

a b d

Physically deleted

Art of Multiprocessor Programming 165


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 166


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 167


Business as Usual

a b c

Art of Multiprocessor Programming 168


Business as Usual

a b c

Art of Multiprocessor Programming 169


Business as Usual

a b c

Art of Multiprocessor Programming 170


Business as Usual

a b c

remove(b)

Art of Multiprocessor Programming 171


Business as Usual

a b c

a not
marked

Art of Multiprocessor Programming 172


Business as Usual

a b c

a still
points
to b

Art of Multiprocessor Programming 173


Business as Usual

a b c

Logical
delete

Art of Multiprocessor Programming 174


Business as Usual

a b c

physical
delete

Art of Multiprocessor Programming 175


Business as Usual

a b c

Art of Multiprocessor Programming 176


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 177


Invariant
• If not marked then item in the set
• and reachable from head
• and if not yet traversed it is
reachable from pred

Art of Multiprocessor Programming 178


Validation
private boolean
validate(Node pred, Node curr) {
return
!pred.marked &&
!curr.marked &&
pred.next == curr);
}

Art of Multiprocessor Programming 179


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 180
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 181


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 182
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 183
Remove
try {
pred.lock(); curr.lock();
if (validate(pred,curr) {
if (curr.key == key) {
curr.marked = true;
pred.next = curr.next;
return true;
} else { Validate as before
return false;
}}} finally {
pred.unlock();
curr.unlock();
}}} Art of Multiprocessor Programming 184
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 { Key found
pred.unlock();
curr.unlock();
}}} Art of Multiprocessor Programming 185
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 186
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 187
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 188


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 189


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 190
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 191
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 192
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 193


Lazy List

a 0 b 0 dc 1
0 e 0

Lazy add() and remove() + Wait-free contains()

Art of Multiprocessor Programming 194


Evaluation
• Good:
– contains() doesn’t lock
– In fact, its wait-free!
– Good because typically high % contains()
– Uncontended calls don’t re-traverse
• Bad
– Contended add() and remove() calls do
re-traverse
– Traffic jam if one thread delays
Art of Multiprocessor Programming 195
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 …
– Everyone else using that lock is stuck!
– Need to trust the scheduler….
Art of Multiprocessor Programming 196
Reminder: Lock-Free Data
Structures
• No matter what …
– Guarantees minimal progress in any
execution
– i.e. Some thread will always complete a
method call
– Even if others halt at malicious times
– Implies that implementation can’t use
locks

Art of Multiprocessor Programming 197


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 198


Remove Using CAS
Logical Removal =
Set Mark Bit

a 0 b 0 cc 1
0 e 0

Use CAS to verify pointer Physical


is correct Removal
CAS pointer
Not enough!

Art of Multiprocessor Programming 199


Problem…
Logical Removal =
Set Mark Bit

a 0 b 0 cc 1
0 e 0

Problem: d 0
Physical
d not added to list… Removal Node added
Must Prevent CAS Before
manipulation of Physical
Removal CAS
removed node’s pointer
Art of Multiprocessor Programming 200
The Solution: Combine Bit and
Pointer
Logical Removal =
Set Mark Bit

a 0 b 0 cc 1
0 e 0

d 0
Physical
Mark-Bit and Pointer Removal Fail CAS: Node not
CAS added after logical
are CASed together Removal
(AtomicMarkableReference)

Art of Multiprocessor Programming 201


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 202


Marking a Node
• AtomicMarkableReference class
– Java.util.concurrent.atomic package

Reference
address F

mark bit
Art of Multiprocessor Programming 203
Extracting Reference & Mark

Public Object get(boolean[] marked);

Art of Multiprocessor Programming 204


Extracting Reference &
Mark
Public Object get(boolean[] marked);

Returns mark at
Returns
array index 0!
reference

Art of Multiprocessor Programming 205


Extracting Reference Only
public boolean isMarked();

Value of
mark

Art of Multiprocessor Programming 206


Changing State

Public boolean compareAndSet(


Object expectedRef,
Object updateRef,
boolean expectedMark,
boolean updateMark);

Art of Multiprocessor Programming 207


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 208
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 209
Changing State

public boolean attemptMark(


Object expectedRef,
boolean updateMark);

Art of Multiprocessor Programming 210


Changing State

public boolean attemptMark(


Object expectedRef,
boolean updateMark);

If this is the current


reference …

Art of Multiprocessor Programming 211


Changing State

public boolean attemptMark(


Object expectedRef,
boolean updateMark);

.. then change to
this new mark.

Art of Multiprocessor Programming 212


Removing a Node

a bCAS c d

remov
e c

Art of Multiprocessor Programming 213


Removing a Node
failed

a CAS bCAS c d

remov
e c
remov
e b

Art of Multiprocessor Programming 214


Removing a Node

a b c d

remov
e c
remov
e b

Art of Multiprocessor Programming 215


Removing a Node

a d

remov
e c
remov
e b

Art of Multiprocessor Programming 216


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 217


Lock-Free Traversal
(only Add and Remove)
pred curr
pred curr
CAS
a b c d

Uh-oh

Art of Multiprocessor Programming 218


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 219


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 220
Using the Find Method
Window window = find(head, key);
Node pred = window.pred;
curr = window.curr;

Art of Multiprocessor Programming 221


Using the Find Method
Window window = find(head, key);
Node pred = window.pred;
curr = window.curr;

Find returns window

Art of Multiprocessor Programming 222


Using the Find Method
Window window = find(head, key);
Node pred = window.pred;
curr = window.curr;

Extract pred and curr

Art of Multiprocessor Programming 223


The Find Method
Window window = find(item);

item
At some instant, or …

pred curr succ

Art of Multiprocessor Programming© 224


Herlihy-Shavit 2007
The Find Method
Window window = find(item);

item not in list


At some instant,

curr= null
pred succ

Art of Multiprocessor Programming© 225


Herlihy-Shavit 2007
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 226
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 227
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 228
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;
}}}
She’s not there …
Art of Multiprocessor Programming 229
Remove
public boolean remove(T item) {
Boolean Try
snip;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 230
Remove
public boolean remove(T item) {
If it snip;
Boolean doesn’t
work, just retry,
while (true) {
Window window = find(head, key);
if itpred
Node does, job
= window.pred, curr = window.curr;
essentially
if (curr.keydone
!= 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 231
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
Node don’t
succ succeed, someone else did or will).
= 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 232
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 233


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 234


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 235
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 236


Wait-free Contains
public boolean contains(Tt 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 237


Wait-free Contains
public boolean contains(T item) {
boolean marked; Only diff is that we
get and check
int key = item.hashCode();
Node curr = this.head; marked
while (curr.key < key)
curr = curr.next;
Node succ = curr.next.get(marked);
return (curr.key == key && !marked[0])
}

Art of Multiprocessor Programming 238


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 239
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) { If list changes
succ = curr.next.get(marked); while
while (marked[0]) { traversed,
… start over
}
if (curr.key >= key)
Lock-Free
because we
return new Window(pred, curr);
pred = curr; start over only
}
curr = succ;
if someone else
}} makes progress
Art of Multiprocessor Programming 240
Lock-free Find
public Window find(Node head, int key) {
Start
Node pred = null, curr looking from= head
= 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 241
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 242
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 = Get
succ; ref to successor and
current deleted bit
}
}}
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;
Try to remove deleted nodes in
curr = succ;
}
}} path…code details soon
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();
Ifwhile
curr(true)
key that
{ is greater or
equal,
succ = return pred and curr
curr.next.get(marked);
while (marked[0]) {

}
if (curr.key >= key)
return new Window(pred, curr);
pred = curr;
curr = succ;
}
}}
Art of Multiprocessor Programming 245
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) {
Otherwise advance{ window and
succ = curr.next.get(marked);
while (marked[0])
… loop again
}
if (curr.key >= key)
return new Window(pred, curr);
pred = curr;
curr = succ;
}
}}
Art of Multiprocessor Programming 246
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 247


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 248


Lock-free Find
if predecessor’s next field
changed must retry whole
retry: while (true) { traversal

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 249


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 250


Performance

On 16 node shared memory machine


Benchmark throughput of Java List-based Set
algs. Vary % of Contains() method Calls.

Art of Multiprocessor Programming 251


High Contains Ratio

Lock-free
Lazy list

Course Grained
Fine Lock-coupling

Art of Multiprocessor Programming 252


Low Contains Ratio

Lock-free

Lazy list

Course Grained
Fine Lock-coupling

Art of Multiprocessor Programming 253


As Contains Ratio Increases

Lock-free
Lazy list

Course Grained
Fine Lock-coupling

% Contains()

Art of Multiprocessor Programming 254


Summary
• Coarse-grained locking
• Fine-grained locking
• Optimistic synchronization
• Lock-free synchronization

Art of Multiprocessor Programming 255


“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()
– Remember: Blocking/non-blocking is a
property of a method

Art of Multiprocessor Programming 256


This work is licensed under a Creative Commons Attribution-
ShareAlike 2.5 License.
• You are free:
– to Share — to copy, distribute and transmit the work
– to Remix — to adapt the work
• Under the following conditions:
– Attribution. You must attribute the work to “The Art of
Multiprocessor Programming” (but not in any way that suggests that
the authors endorse you or your use of the work).
– Share Alike. If you alter, transform, or build upon this work, you
may distribute the resulting work only under the same, similar or a
compatible license.
• For any reuse or distribution, you must make clear to others the
license terms of this work. The best way to do this is with a link
to
– http://creativecommons.org/licenses/by-sa/3.0/.
• Any of the above conditions can be waived if you get permission
from the copyright holder.
• Nothing in this license impairs or restricts the author's moral
rights.

Art of Multiprocessor Programming 257

You might also like