# Lecture 33: Nonblocking Linked Lists

## Annoucements

1. Quiz on concurrent linked lists due Today
2. Next leaderboard submission on Monday

Baseline: 58810ms

No others were significantly faster than the baseline.

## Notes on Space Usage

Baseline: 8034ms

1. Sunny Day (511ms)
2. MRC (1580ms)
3. Team 2 (2214ms)

## Previously

1. Coarse locking
• lock the whole data structure for every operation
2. Fine-grained locking
• lock individual nodes to avoid conflicts
3. Optimistic locking
• search without locks, lock on find, then validate
4. Lazy removal
• like optimistic, but with logical removal
• wait-free contains implementation!

## Today

Question. Can we avoid locks entirely?

## Lazy List and Locks

1. Traverse without locking
2. Lock relevant nodes
3. Validate list
4. Perform operation
5. Unlock nodes

## Why Does LazyList Need Locks?

Validataion:

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


Modification (e.g., add):

Node node = new Node(item);
node.next = curr;
pred.next = node; // this is the only step that modifies list!


The issue:

• Validation and modification are separate steps
• Must enforce that nodes are unchanged between validation and mod

## An Idea

If we can

1. combine validation and modification steps
2. perform this operation atomically

then maybe we can avoid locking?

## A Tool

Better living with atomics!

• AtomicMarkableReference<T>
• Stores
1. a reference to a T
2. a boolean marked
• Atomic operations
1. boolean compareAndSet(T expectedRef, T newRef, boolean expectedMark, boolean newMark)
2. T get(boolean[] marked)
3. T getReference()
4. boolean isMarked()

## An Algorithm?

Use AtomicMarkableReference<Node> for Node references

• mark indicates logical removal

For add/remove:

1. Find location
2. Validate and modify
• (first logically remove if remove)
• use compareAndSet to atomically
1. check that predecessor not removed (marked)
2. update next field of predecessor

For contains:

• Just traverse the list!

## NonblockingList Design

See NonblockingList.java

1. For Node class, AtomicMarkableReference<Node> next is marked if this Node is logically removed
• separate logical/physical removal as in LazyList
2. Separate Window class stores two Nodes: prev, curr
3. NonblockingList method find returns a Window
• find also removes any marked nodes encountered

Question. Why should methods perform physical removal for other pending operations?

## Removal Sketch

1. Find Node curr storing value with predecessor pred
2. Mark curr for (logical) removal
• set mark of cur.next to true
• retry if this fails
3. Perform physical removal
• update pred.next

## Removal in Code I

public boolean remove(T item) {
int key = item.hashCode();
boolean snip;
while (true) {
Node pred = window.pred;
Node curr = window.curr;
if (curr.key != key) { return false; }
// curr contains item
...
}
}


## Removal in Code II

public boolean remove(T item) {
...
while (true) {
...
// curr contains item
Node succ = curr.next.getReference();
snip = curr.next.compareAndSet(succ, succ, false, true);
if (!snip) {continue;}
pred.next.compareAndSet(curr, succ, false, false);
return true;
}
}


## A Puzzle

Question. Why donâ€™t we care about return value of pred.next.compareAndSet?

public boolean remove(T item) {
while (true) {
...
// curr logically removed
pred.next.compareAndSet(curr, succ, false, false);
return true;
}
}