bjorn_a.johnsson@cs.lth.se bjorn_a.johnsson@cs.lth.se
Herlihy Ch 9. Linked Lists: The Role of Locking
Non-Blocking Synchronization
BJÖRN A. JOHNSSON
Herlihy Ch 9. Linked Lists: The Role of Locking Non-Blocking - - PowerPoint PPT Presentation
Herlihy Ch 9. Linked Lists: The Role of Locking Non-Blocking Synchronization BJRN A. JOHNSSON bjorn_a.johnsson@cs.lth.se bjorn_a.johnsson@cs.lth.se Overview? So far: Coarse- and fine-grained synchronization Optimistic
bjorn_a.johnsson@cs.lth.se bjorn_a.johnsson@cs.lth.se
Herlihy Ch 9. Linked Lists: The Role of Locking
BJÖRN A. JOHNSSON
bjorn_a.johnsson@cs.lth.se bjorn_a.johnsson@cs.lth.se
bjorn_a.johnsson@cs.lth.se bjorn_a.johnsson@cs.lth.se
bjorn_a.johnsson@cs.lth.se bjorn_a.johnsson@cs.lth.se
Pragma 9.8.1. An AtomicMarkableReference<T> is an object from the java.util.concurrent.atomic package that encapsulates both a reference to an
and mark values, and if both tests succeed, replaces them with updated reference and mark
the test succeeds, replaces it with a new mark value. The get() method has an unusual interface: it returns the object’s reference value and stores the mark value in a Boolean array argument. 1 public boolean compareAndSet(T expectedReference, 2 T newReference, 3 boolean expectedMark, 4 boolean newMark); 5 public boolean attemptMark(T expectedReference, 6 boolean newMark); 7 public T get(boolean[] marked);
bjorn_a.johnsson@cs.lth.se bjorn_a.johnsson@cs.lth.se
1 1 Why? Exercise, that’s why!
bjorn_a.johnsson@cs.lth.se bjorn_a.johnsson@cs.lth.se
public public Window find(Node head, Window find(Node head, int int key) { key) { Node Node pred pred = = null null, , curr curr = = null null, , succ succ = = null null; boolean boolean[] marked = { [] marked = {false false}; }; boolean boolean snip; snip; // physical remove OK? // physical remove OK? retry: retry: while while ( (true true) { ) { pred pred = head; = head; curr curr = = pred.next.getReference pred.next.getReference(); (); while while ( (true true) { ) { succ succ = = curr.next.get curr.next.get(marked); (marked); while while (marked[0]) { (marked[0]) { // physically remove! snip = snip = pred.next.compareAndSet pred.next.compareAndSet( curr curr, , succ succ, , false false, , false false); ); if if (!snip) (!snip) continue continue retry; retry; curr curr = = succ succ; succ succ = = curr.next.get curr.next.get(marked); (marked); } } if if ( (curr.key curr.key >= key) >= key) return return new new Window( Window(pred pred, , curr curr); ); pred pred = = curr curr; curr curr = = succ succ; } } } } } class class Window { Window { public public Node Node pred pred, , curr curr; Window(Node Window(Node myPred myPred, Node , Node myCurr myCurr) { ) { pred pred = = myPred myPred; ; curr curr = = myCurr myCurr; } } }
bjorn_a.johnsson@cs.lth.se bjorn_a.johnsson@cs.lth.se
public public boolean boolean add(T item) { add(T item) { int int key = key = item.hashCode item.hashCode(); (); while while ( (true true) { ) { Window window = find(head, key); Window window = find(head, key); Node Node pred pred = = window. window.pred pred, , curr curr = = window. window.curr curr; if if ( (curr.key curr.key == key) { == key) { return return false false; } } else else { { Node node = Node node = new new Node(item); Node(item); node.next node.next = = new new AtomicMarkableReference AtomicMarkableReference(curr curr, , false false); ); if if ( (pred.next.compareAndSet pred.next.compareAndSet(curr curr, node, , node, false false, , false false)) )) return return true true; } } } } } public public boolean boolean remove remove(T item) { (T item) { int int key key = = item.hashCode item.hashCode(); (); boolean boolean snip; snip; // // logical logical remove remove OK? OK? while while ( (true true) { ) { Window Window window window = find(head, = find(head, key key); ); Node Node pred pred = = window. window.pred pred, , curr curr = = window. window.curr curr; if if ( (curr.key curr.key != key) { != key) { return return false false; } } else else { { Node Node succ succ = = curr.next.getReference curr.next.getReference(); (); snip = snip = curr.next.compareAndSet curr.next.compareAndSet ( (succ succ, , succ succ, , false false, , true true); ); if if (!snip) (!snip) continue continue; pred.next.compareAndSet pred.next.compareAndSet(curr curr, , succ succ, , false false, , false false); ); return return true true; } } } } }
bjorn_a.johnsson@cs.lth.se bjorn_a.johnsson@cs.lth.se
public public boolean boolean contains(T item) { contains(T item) { boolean boolean[] marked = [] marked = false false; int int key = key = item.hashCode item.hashCode(); (); Node curr = head; while while ( (curr.key curr.key < key) { < key) { curr = curr.next.getReference(); Node succ = curr.next.get(marked); } return return ( (curr.key curr.key == key && !marked[0]) == key && !marked[0]) }
bjorn_a.johnsson@cs.lth.se bjorn_a.johnsson@cs.lth.se
⇒ Fully nonblocking list!
– The need to support atomic modification of next has an added performance cost – Concurrent cleanup when traversing can cause contention; may force ”unnecessary” traversal restart
– None of LockFreeList’s weaknesses
bjorn_a.johnsson@cs.lth.se bjorn_a.johnsson@cs.lth.se
bjorn_a.johnsson@cs.lth.se bjorn_a.johnsson@cs.lth.se
In the LockFreeList algorithm, why must threads that add/remove nodes never traverse marked nodes, but instead physically remove them? Illustrate your answer with a figure(s) similar to those in the chapter. Clarify your illustration(s) with a short explanation.