Linked Lists: Locking, Lock- Free, and Beyond … Companion slides for The Art of Multiprocessor Programming by Maurice Herlihy & Nir Shavit.

Slides:



Advertisements
Similar presentations
Linked Lists: Locking, Lock-Free, and Beyond … Companion slides for The Art of Multiprocessor Programming by Maurice Herlihy & Nir Shavit.
Advertisements

Ordered linked list implementation of a set
Linked Lists: Locking, Lock- Free, and Beyond … Companion slides for The Art of Multiprocessor Programming by Maurice Herlihy & Nir Shavit.
Linked Lists: Locking, Lock- Free, and Beyond … Based on the companion slides for The Art of Multiprocessor Programming (Maurice Herlihy & Nir Shavit)
Multicore Programming Skip list Tutorial 10 CS Spring 2010.
Wait-Free Linked-Lists Shahar Timnat, Anastasia Braginsky, Alex Kogan, Erez Petrank Technion, Israel Presented by Shahar Timnat 469-+
Linked Lists: Locking, Lock-Free, and Beyond … Companion slides for The Art of Multiprocessor Programming by Maurice Herlihy & Nir Shavit.
CS492B Analysis of Concurrent Programs Lock Basics Jaehyuk Huh Computer Science, KAIST.
Process Synchronization Continued 7.2 The Critical-Section Problem.
Mutual Exclusion By Shiran Mizrahi. Critical Section class Counter { private int value = 1; //counter starts at one public Counter(int c) { //constructor.
CH7 discussion-review Mahmoud Alhabbash. Q1 What is a Race Condition? How could we prevent that? – Race condition is the situation where several processes.
Linked Lists: Locking, Lock-Free, and Beyond … The Art of Multiprocessor Programming Spring 2007.
Introduction Companion slides for
Linked Lists: Lazy and Non-Blocking Synchronization Based on the companion slides for The Art of Multiprocessor Programming (Maurice Herlihy & Nir Shavit)
Spin Locks and Contention Based on slides by by Maurice Herlihy & Nir Shavit Tomer Gurevich.
CSC1016 Coursework Clarification Derek Mortimer March 2010.
Shared Counters and Parallelism Companion slides for The Art of Multiprocessor Programming by Maurice Herlihy & Nir Shavit.
Introduction to Lock-free Data-structures and algorithms Micah J Best May 14/09.
CS510 Advanced OS Seminar Class 10 A Methodology for Implementing Highly Concurrent Data Objects by Maurice Herlihy.
CS510 Concurrent Systems Class 2 A Lock-Free Multiprocessor OS Kernel.
Companion slides for The Art of Multiprocessor Programming by Maurice Herlihy & Nir Shavit Concurrent Skip Lists.
CS2110 Recitation Week 8. Hashing Hashing: An implementation of a set. It provides O(1) expected time for set operations Set operations Make the set empty.
Multicore Programming
CS510 Concurrent Systems Jonathan Walpole. A Lock-Free Multiprocessor OS Kernel.
Linked Lists: Locking, Lock- Free, and Beyond … Companion slides for The Art of Multiprocessor Programming by Maurice Herlihy & Nir Shavit.
State Teleportation How Hardware Transactional Memory can Improve Legacy Data Structures Maurice Herlihy and Eli Wald Brown University.
CSC 211 Data Structures Lecture 13
11/18/20151 Operating Systems Design (CS 423) Elsa L Gunter 2112 SC, UIUC Based on slides by Roy Campbell, Sam.
Linked Lists: Optimistic, Lock-Free, … Companion slides for The Art of Multiprocessor Programming by Maurice Herlihy & Nir Shavit Modified by Pavol Černý,
Linked Lists: Locking, Lock- Free, and Beyond … Companion slides for The Art of Multiprocessor Programming by Maurice Herlihy & Nir Shavit Modified by.
Concurrent Linked Lists and Linearizability Proofs Companion slides for The Art of Multiprocessor Programming by Maurice Herlihy & Nir Shavit Modified.
DOUBLE INSTANCE LOCKING A concurrency pattern with Lock-Free read operations Pedro Ramalhete Andreia Correia November 2013.
Advanced Locking Techniques
A Simple Optimistic skip-list Algorithm Maurice Herlihy Brown University & Sun Microsystems Laboratories Yossi Lev Brown University & Sun Microsystems.
CS510 Concurrent Systems Jonathan Walpole. A Methodology for Implementing Highly Concurrent Data Objects.
Software Transactional Memory Should Not Be Obstruction-Free Robert Ennals Presented by Abdulai Sei.
Concurrent Queues Companion slides for The Art of Multiprocessor Programming by Maurice Herlihy & Nir Shavit.
Copyright © 0 Pearson Education, Inc. Publishing as Pearson Addison-Wesley Starting Out with Java From Control Structures through Data Structures by Tony.
Multiprocessor Architecture Basics Companion slides for The Art of Multiprocessor Programming by Maurice Herlihy & Nir Shavit.
Concurrent Hashing and Natural Parallelism Chapter 13 in The Art of Multiprocessor Programming Instructor: Erez Petrank Presented by Tomer Hermelin.
Concurrent Stacks Companion slides for The Art of Multiprocessor Programming by Maurice Herlihy & Nir Shavit.
Companion slides for The Art of Multiprocessor Programming by Maurice Herlihy & Nir Shavit Concurrent Skip Lists.
Linked Lists: Locking, Lock-Free, and Beyond … Companion slides for The Art of Multiprocessor Programming by Maurice Herlihy & Nir Shavit.
SkipLists and Balanced Search The Art Of MultiProcessor Programming Maurice Herlihy & Nir Shavit Chapter 14 Avi Kozokin.
Recursion ITI 1121 N. El Kadri. Reminders about recursion In your 1 st CS course (or its equivalent), you have seen how to use recursion to solve numerical.
Linked Lists: Locking, Lock- Free, and Beyond … Companion slides for The Art of Multiprocessor Programming by Maurice Herlihy & Nir Shavit.
Linked Lists: Locking, Lock- Free, and Beyond … Companion slides for The Art of Multiprocessor Programming by Maurice Herlihy & Nir Shavit.
Spin Locks and Contention Companion slides for The Art of Multiprocessor Programming by Maurice Herlihy & Nir Shavit.
Spin Locks and Contention Companion slides for The Art of Multiprocessor Programming by Maurice Herlihy & Nir Shavit.
Queue Locks and Local Spinning Some Slides based on: The Art of Multiprocessor Programming by Maurice Herlihy & Nir Shavit.
1 Chapter 4 Unordered List. 2 Learning Objectives ● Describe the properties of an unordered list. ● Study sequential search and analyze its worst- case.
The Relative Power of Synchronization Operations Companion slides for The Art of Multiprocessor Programming by Maurice Herlihy & Nir Shavit.
Concurrency: Locks and synchronization Slides by Prof. Cox.
Companion slides for The Art of Multiprocessor Programming by Maurice Herlihy & Nir Shavit Concurrent Skip Lists.
Lecture 9 : Concurrent Data Structures Companion slides for The Art of Multiprocessor Programming by Maurice Herlihy & Nir Shavit.
6.852 Lecture 21 ● Techniques for highly concurrent objects – coarse-grained mutual exclusion – read/write locking – fine-grained locking (mutex and read/write)
Linked Lists: Locking, Lock-Free, and Beyond …
Lecture 6-1 : Concurrent Data Structures (Concurrent Linked Lists)
Linked Lists: Locking, Lock-Free, and Beyond …
Concurrent Objects Companion slides for
CO890 CONCURRENCY & PARALLELISM
Linked Lists: Locking, Lock-Free, and Beyond …
Concurrent Data Structures Concurrent Algorithms 2017
Linked Lists: Locking, Lock-Free, and Beyond …
Concurrency and Multicore Programming
Multicore programming
Linked Lists: Locking, Lock-Free, and Beyond …
Multicore programming
CSE 153 Design of Operating Systems Winter 19
Linked Lists: Locking, Lock-Free, and Beyond …
Presentation transcript:

Linked Lists: Locking, Lock- Free, and Beyond … Companion slides for The Art of Multiprocessor Programming by Maurice Herlihy & Nir Shavit

Art of Multiprocessor Programming© Herlihy-Shavit Last Lecture: Spin-Locks CS Resets lock upon exit spin lock critical section...

Art of Multiprocessor Programming© Herlihy-Shavit 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© Herlihy-Shavit 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© Herlihy-Shavit 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© Herlihy-Shavit 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 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 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 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 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 Linked List Illustrate these patterns … Using a list-based Set –Common application –Building block for other apps

Art of Multiprocessor Programming© Herlihy-Shavit 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 List-Based Sets public interface Set { public boolean add(T x); public boolean remove(T x); public boolean contains(T x); }

Art of Multiprocessor Programming© Herlihy-Shavit List-Based Sets public interface Set { 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 List-Based Sets public interface Set { 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 List-Based Sets public interface Set { 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 List Node public class Node { public T item; public int key; public Node next; }

Art of Multiprocessor Programming© Herlihy-Shavit List Node public class Node { public T item; public int key; public Node next; } item of interest

Art of Multiprocessor Programming© Herlihy-Shavit List Node public class Node { public T item; public int key; public Node next; } Usually hash code

Art of Multiprocessor Programming© Herlihy-Shavit List Node public class Node { public T item; public int key; public Node next; } Reference to next node

Art of Multiprocessor Programming© Herlihy-Shavit The List-Based Set abc Sorted with Sentinel nodes (min & max possible keys) -∞ +∞

Art of Multiprocessor Programming© Herlihy-Shavit 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 Specifically … Invariants preserved by –add() –remove() –contains() Most steps are trivial –Usually one step tricky –Often linearization point

Art of Multiprocessor Programming© Herlihy-Shavit 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© Herlihy-Shavit 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© Herlihy-Shavit Abstract Data Types Concrete representation Abstract Type –{a, b} ab

Art of Multiprocessor Programming© Herlihy-Shavit Abstract Data Types Meaning of rep given by abstraction map –S( ) = {a,b} a b

Art of Multiprocessor Programming© Herlihy-Shavit 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© Herlihy-Shavit 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© Herlihy-Shavit 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© Herlihy-Shavit Rep Invariant (partly) Sentinel nodes –tail reachable from head Sorted No duplicates

Art of Multiprocessor Programming© Herlihy-Shavit Abstraction Map S(head) = –{ x | there exists a such that a reachable from head and a.item = x –}

Art of Multiprocessor Programming© Herlihy-Shavit Sequential List Based Set a c d a b c Add() Remove()

Art of Multiprocessor Programming© Herlihy-Shavit Sequential List Based Set a c d b a b c Add() Remove()

Art of Multiprocessor Programming© Herlihy-Shavit Course Grained Locking a b d

Art of Multiprocessor Programming© Herlihy-Shavit Course Grained Locking a b d c

Art of Multiprocessor Programming© Herlihy-Shavit honk! Course Grained Locking a b d c Simple but hotspot + bottleneck honk!

Art of Multiprocessor Programming© Herlihy-Shavit 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 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© Herlihy-Shavit Hand-over-Hand locking abc

Art of Multiprocessor Programming© Herlihy-Shavit Hand-over-Hand locking abc

Art of Multiprocessor Programming© Herlihy-Shavit Hand-over-Hand locking abc

Art of Multiprocessor Programming© Herlihy-Shavit Hand-over-Hand locking abc

Art of Multiprocessor Programming© Herlihy-Shavit Hand-over-Hand locking abc

Art of Multiprocessor Programming© Herlihy-Shavit Removing a Node abcd remove(b)

Art of Multiprocessor Programming© Herlihy-Shavit Removing a Node abcd remove(b)

Art of Multiprocessor Programming© Herlihy-Shavit Removing a Node abcd remove(b)

Art of Multiprocessor Programming© Herlihy-Shavit Removing a Node abcd remove(b)

Art of Multiprocessor Programming© Herlihy-Shavit Removing a Node acd remove(b)

Art of Multiprocessor Programming© Herlihy-Shavit Removing a Node abcd remove(c) remove(b)

Art of Multiprocessor Programming© Herlihy-Shavit Removing a Node abcd remove(b) remove(c)

Art of Multiprocessor Programming© Herlihy-Shavit Removing a Node abcd remove(b) remove(c)

Art of Multiprocessor Programming© Herlihy-Shavit Removing a Node abcd remove(b) remove(c)

Art of Multiprocessor Programming© Herlihy-Shavit Removing a Node abcd remove(b) remove(c)

Art of Multiprocessor Programming© Herlihy-Shavit Removing a Node abcd remove(b) remove(c)

Art of Multiprocessor Programming© Herlihy-Shavit Removing a Node abcd remove(b) remove(c)

Art of Multiprocessor Programming© Herlihy-Shavit Removing a Node abcd remove(b) remove(c)

Art of Multiprocessor Programming© Herlihy-Shavit Uh, Oh acd remove(b) remove(c)

Art of Multiprocessor Programming© Herlihy-Shavit Uh, Oh acd Bad news remove(b) remove(c)

Art of Multiprocessor Programming© Herlihy-Shavit Problem To delete node b –Swing node a’s next field to c Problem is, –Someone could delete c concurrently ba cbac

Art of Multiprocessor Programming© Herlihy-Shavit 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 Hand-Over-Hand Again abcd remove(b)

Art of Multiprocessor Programming© Herlihy-Shavit Hand-Over-Hand Again abcd remove(b)

Art of Multiprocessor Programming© Herlihy-Shavit Hand-Over-Hand Again abcd remove(b)

Art of Multiprocessor Programming© Herlihy-Shavit Hand-Over-Hand Again abcd remove(b) Found it!

Art of Multiprocessor Programming© Herlihy-Shavit Hand-Over-Hand Again abcd remove(b) Found it!

Art of Multiprocessor Programming© Herlihy-Shavit Hand-Over-Hand Again acd remove(b)

Art of Multiprocessor Programming© Herlihy-Shavit Removing a Node abcd remove(b) remove(c)

Art of Multiprocessor Programming© Herlihy-Shavit Removing a Node abcd remove(b) remove(c)

Art of Multiprocessor Programming© Herlihy-Shavit Removing a Node abcd remove(b) remove(c)

Art of Multiprocessor Programming© Herlihy-Shavit Removing a Node abcd remove(b) remove(c)

Art of Multiprocessor Programming© Herlihy-Shavit Removing a Node abcd remove(b) remove(c)

Art of Multiprocessor Programming© Herlihy-Shavit Removing a Node abcd remove(b) remove(c)

Art of Multiprocessor Programming© Herlihy-Shavit Removing a Node abcd remove(b) remove(c)

Art of Multiprocessor Programming© Herlihy-Shavit Removing a Node abcd remove(b) remove(c)

Art of Multiprocessor Programming© Herlihy-Shavit Removing a Node abcd remove(b) remove(c)

Art of Multiprocessor Programming© Herlihy-Shavit Removing a Node abcd remove(b) remove(c)

Art of Multiprocessor Programming© Herlihy-Shavit Removing a Node abd remove(b)

Art of Multiprocessor Programming© Herlihy-Shavit Removing a Node abd remove(b)

Art of Multiprocessor Programming© Herlihy-Shavit Removing a Node abd remove(b)

Art of Multiprocessor Programming© Herlihy-Shavit Removing a Node ad remove(b) remove(c)

Art of Multiprocessor Programming© Herlihy-Shavit Removing a Node ad

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

Art of Multiprocessor Programming© Herlihy-Shavit 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© Herlihy-Shavit Remove method try { pred = this.head; pred.lock(); curr = pred.next; curr.lock(); … } finally { … }

Art of Multiprocessor Programming© Herlihy-Shavit Remove method try { pred = this.head; pred.lock(); curr = pred.next; curr.lock(); … } finally { … } lock pred == head

Art of Multiprocessor Programming© Herlihy-Shavit Remove method try { pred = this.head; pred.lock(); curr = pred.next; curr.lock(); … } finally { … } Lock current

Art of Multiprocessor Programming© Herlihy-Shavit Remove method try { pred = this.head; pred.lock(); curr = pred.next; curr.lock(); … } finally { … } Traversing list

Art of Multiprocessor Programming© Herlihy-Shavit 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© Herlihy-Shavit 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; Search key range

Art of Multiprocessor Programming© Herlihy-Shavit 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; At start of each loop: curr and pred locked

Art of Multiprocessor Programming© Herlihy-Shavit 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; If item found, remove node

Art of Multiprocessor Programming© Herlihy-Shavit 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; If node found, remove it

Art of Multiprocessor Programming© Herlihy-Shavit 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; Unlock predecessor

Art of Multiprocessor Programming© Herlihy-Shavit 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; Only one node locked!

Art of Multiprocessor Programming© Herlihy-Shavit 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; demote current

Art of Multiprocessor Programming© Herlihy-Shavit Remove: searching while (curr.key <= key) { if (item == curr.item) { pred.next = curr.next; return true; } pred.unlock(); pred = currNode; curr = curr.next; curr.lock(); } return false; Find and lock new current

Art of Multiprocessor Programming© Herlihy-Shavit Remove: searching while (curr.key <= key) { if (item == curr.item) { pred.next = curr.next; return true; } pred.unlock(); pred = currNode; curr = curr.next; curr.lock(); } return false; Lock invariant restored

Art of Multiprocessor Programming© Herlihy-Shavit 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; Otherwise, not present

Art of Multiprocessor Programming© Herlihy-Shavit Why does this work? To remove node e –Must lock e –Must lock e’s predecessor Therefore, if you lock a node –It can’t be removed –And neither can its successor

Art of Multiprocessor Programming© Herlihy-Shavit 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; Why remove() is linearizable pred reachable from head curr is pred.next So curr.item is in the set

Art of Multiprocessor Programming© Herlihy-Shavit 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; Why remove() is linearizable Linearization point if item is present

Art of Multiprocessor Programming© Herlihy-Shavit 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; Why remove() is linearizable Node locked, so no other thread can remove it ….

Art of Multiprocessor Programming© Herlihy-Shavit 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; Why remove() is linearizable Item not present

Art of Multiprocessor Programming© Herlihy-Shavit 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; Why remove() is linearizable pred reachable from head curr is pred.next pred.key < key key < curr.key

Art of Multiprocessor Programming© Herlihy-Shavit 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; Why remove() is linearizable Linearization point

Art of Multiprocessor Programming© Herlihy-Shavit 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© Herlihy-Shavit 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 Rep Invariant Easy to check that –tail always reachable from head –Nodes sorted, no duplicates

Art of Multiprocessor Programming© Herlihy-Shavit Drawbacks Better than coarse-grained lock –Threads can traverse in parallel Still not ideal –Long chain of acquire/release –Inefficient

Art of Multiprocessor Programming© Herlihy-Shavit Optimistic Synchronization Find nodes without locking Lock nodes Check that everything is OK

Art of Multiprocessor Programming© Herlihy-Shavit Optimistic: Traverse without Locking b d e a add(c) Aha!

Art of Multiprocessor Programming© Herlihy-Shavit Optimistic: Lock and Load b d e a add(c)

Art of Multiprocessor Programming© Herlihy-Shavit What Can Possibly Go Wrong? b d e a add(c)

Art of Multiprocessor Programming© Herlihy-Shavit What Can Possibly Go Wrong? b d e a add(c) remove(b )

Art of Multiprocessor Programming© Herlihy-Shavit What Can Possibly Go Wrong? b d e a add(c)

Art of Multiprocessor Programming© Herlihy-Shavit Validate (1) b d e a add(c) Yes, b still reachable from head

Art of Multiprocessor Programming© Herlihy-Shavit What Else Can Go Wrong? b d e a add(c)

Art of Multiprocessor Programming© Herlihy-Shavit What Else Can Go Wrong? b d e a add(c) add(b’) b’

Art of Multiprocessor Programming© Herlihy-Shavit What Else Can Go Wrong? b d e a add(c) b’

Art of Multiprocessor Programming© Herlihy-Shavit Optimistic: Validate(2) b d e a add(c) Yes, b still points to d

Art of Multiprocessor Programming© Herlihy-Shavit Optimistic: Linearization Point b d e a add(c) c

Art of Multiprocessor Programming© Herlihy-Shavit 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 Invariants Careful: we may traverse deleted nodes But we establish properties by –Validation –After we lock target nodes

Art of Multiprocessor Programming© Herlihy-Shavit 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© Herlihy-Shavit Removing an Absent Node abde remove(c ) Aha!

Art of Multiprocessor Programming© Herlihy-Shavit Validate (1) abde Yes, b still reachable from head remove(c )

Art of Multiprocessor Programming© Herlihy-Shavit Validate (2) abde remove(c ) Yes, b still points to d

Art of Multiprocessor Programming© Herlihy-Shavit OK Computer abde remove(c ) return true

Art of Multiprocessor Programming© Herlihy-Shavit 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© Herlihy-Shavit 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 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; } Validation Predecessor & current nodes

Art of Multiprocessor Programming© Herlihy-Shavit 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; } Validation Begin at the beginning

Art of Multiprocessor Programming© Herlihy-Shavit 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; } Validation Search range of keys

Art of Multiprocessor Programming© Herlihy-Shavit 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; } Validation Predecessor reachable

Art of Multiprocessor Programming© Herlihy-Shavit 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; } Validation Is current node next?

Art of Multiprocessor Programming© Herlihy-Shavit 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; } Validation Otherwise move on

Art of Multiprocessor Programming© Herlihy-Shavit 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; } Validation Predecessor not reachable

Art of Multiprocessor Programming© Herlihy-Shavit 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 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; } … Remove: searching Search key

Art of Multiprocessor Programming© Herlihy-Shavit 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; } … Remove: searching Retry on synchronization conflict

Art of Multiprocessor Programming© Herlihy-Shavit 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; } … Remove: searching Examine predecessor and current nodes

Art of Multiprocessor Programming© Herlihy-Shavit 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; } … Remove: searching Search by key

Art of Multiprocessor Programming© Herlihy-Shavit 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; } … Remove: searching Stop if we find item

Art of Multiprocessor Programming© Herlihy-Shavit 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; } … Remove: searching Move along

Art of Multiprocessor Programming© Herlihy-Shavit 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 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© Herlihy-Shavit 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(); }}} Remove Method Always unlock

Art of Multiprocessor Programming© Herlihy-Shavit 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(); }}} Remove Method Lock both nodes

Art of Multiprocessor Programming© Herlihy-Shavit 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(); }}} Remove Method Check for synchronization conflicts

Art of Multiprocessor Programming© Herlihy-Shavit 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(); }}} Remove Method target found, remove node

Art of Multiprocessor Programming© Herlihy-Shavit 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(); }}} Remove Method target not found

Art of Multiprocessor Programming© Herlihy-Shavit 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 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 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 Lazy List Like optimistic, except –Scan once –contains(x) never locks … Key insight –Removing nodes causes trouble –Do it “lazily”

Art of Multiprocessor Programming© Herlihy-Shavit 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© Herlihy-Shavit Lazy Removal aa b c d

Art of Multiprocessor Programming© Herlihy-Shavit Lazy Removal aa b c d Present in list

Art of Multiprocessor Programming© Herlihy-Shavit Lazy Removal aa b c d Logically deleted

Art of Multiprocessor Programming© Herlihy-Shavit Lazy Removal aa b c d Physically deleted

Art of Multiprocessor Programming© Herlihy-Shavit Lazy Removal aa b d Physically deleted

Art of Multiprocessor Programming© Herlihy-Shavit 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© Herlihy-Shavit 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© Herlihy-Shavit Business as Usual abc

Art of Multiprocessor Programming© Herlihy-Shavit Business as Usual abc

Art of Multiprocessor Programming© Herlihy-Shavit Business as Usual abc

Art of Multiprocessor Programming© Herlihy-Shavit Business as Usual abc remove(b)

Art of Multiprocessor Programming© Herlihy-Shavit Business as Usual abc a not marked

Art of Multiprocessor Programming© Herlihy-Shavit Business as Usual abc a still points to b

Art of Multiprocessor Programming© Herlihy-Shavit Business as Usual a bc Logical delete

Art of Multiprocessor Programming© Herlihy-Shavit Business as Usual a bc physical delete

Art of Multiprocessor Programming© Herlihy-Shavit Business as Usual a bc

Art of Multiprocessor Programming© Herlihy-Shavit 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© Herlihy-Shavit 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© Herlihy-Shavit Validation private boolean validate(Node pred, Node curr) { return !pred.marked && !curr.marked && pred.next == curr); }

Art of Multiprocessor Programming© Herlihy-Shavit private boolean validate(Node pred, Node curr) { return !pred.marked && !curr.marked && pred.next == curr); } List Validate Method Predecessor not Logically removed

Art of Multiprocessor Programming© Herlihy-Shavit private boolean validate(Node pred, Node curr) { return !pred.marked && !curr.marked && pred.next == curr); } List Validate Method Current not Logically removed

Art of Multiprocessor Programming© Herlihy-Shavit private boolean validate(Node pred, Node curr) { return !pred.marked && !curr.marked && pred.next == curr); } List Validate Method Predecessor still Points to current

Art of Multiprocessor Programming© Herlihy-Shavit 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© Herlihy-Shavit 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(); }}} Validate as before

Art of Multiprocessor Programming© Herlihy-Shavit 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(); }}} Key found

Art of Multiprocessor Programming© Herlihy-Shavit 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(); }}} Logical remove

Art of Multiprocessor Programming© Herlihy-Shavit 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(); }}} physical remove

Art of Multiprocessor Programming© Herlihy-Shavit 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© Herlihy-Shavit 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© Herlihy-Shavit 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© Herlihy-Shavit 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© Herlihy-Shavit 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© Herlihy-Shavit Summary: Wait-free Contains a a b c 0 e 1 d 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© Herlihy-Shavit Lazy List a a b c 0 e 1 d Lazy add() and remove() + Wait-free contains()

Art of Multiprocessor Programming© Herlihy-Shavit Evaluation Good: –contains() doesn’t lock –In fact, its wait-free! –Good because typically high % contains() –Uncontended calls don’t re-traverse Bad –Contended calls do re-traverse –Traffic jam if one thread delays

Art of Multiprocessor Programming© Herlihy-Shavit 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 … Software error, … –Everyone else using that lock is stuck!

Art of Multiprocessor Programming© Herlihy-Shavit Reminder: Lock-Free Data Structures No matter what … –Some thread will complete method call –Even if others halt at malicious times –Weaker than wait-free, yet Implies that –You can’t use locks (why?) –Um, that’s why they call it lock-free

Art of Multiprocessor Programming© Herlihy-Shavit 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© Herlihy-Shavit Adding a Node abc

Art of Multiprocessor Programming© Herlihy-Shavit Adding a Node abcb

Art of Multiprocessor Programming© Herlihy-Shavit Adding a Node abcb CAS

Art of Multiprocessor Programming© Herlihy-Shavit Adding a Node abcb

Art of Multiprocessor Programming© Herlihy-Shavit Adding a Node abcb

Art of Multiprocessor Programming© Herlihy-Shavit Removing a Node abcd remov e b remov e c CAS

Art of Multiprocessor Programming© Herlihy-Shavit Bad news Look Familiar? abcd remov e b remov e c

Art of Multiprocessor Programming© Herlihy-Shavit Problem Method updates node’s next field After node has been removed

Art of Multiprocessor Programming© Herlihy-Shavit 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© Herlihy-Shavit Marking a Node AtomicMarkableReference class –Java.util.concurrent.atomic package address F mark bit Reference

Art of Multiprocessor Programming© Herlihy-Shavit Extracting Reference & Mark Public Object get(boolean[] marked);

Art of Multiprocessor Programming© Herlihy-Shavit Extracting Reference & Mark Public Object get(boolean[] marked); Returns reference Returns mark at array index 0!

Art of Multiprocessor Programming© Herlihy-Shavit Extracting Reference Only public boolean isMarked(); Value of mark

Art of Multiprocessor Programming© Herlihy-Shavit Changing State Public boolean compareAndSet( Object expectedRef, Object updateRef, boolean expectedMark, boolean updateMark);

Art of Multiprocessor Programming© Herlihy-Shavit 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 …

Art of Multiprocessor Programming© Herlihy-Shavit Changing State Public boolean compareAndSet( Object expectedRef, Object updateRef, boolean expectedMark, boolean updateMark); …then change to this new reference … … and this new mark

Art of Multiprocessor Programming© Herlihy-Shavit Changing State public boolean attemptMark( Object expectedRef, boolean updateMark);

Art of Multiprocessor Programming© Herlihy-Shavit Changing State public boolean attemptMark( Object expectedRef, boolean updateMark); If this is the current reference …

Art of Multiprocessor Programming© Herlihy-Shavit Changing State public boolean attemptMark( Object expectedRef, boolean updateMark);.. then change to this new mark.

Art of Multiprocessor Programming© Herlihy-Shavit Removing a Node abcd remov e c CAS

Art of Multiprocessor Programming© Herlihy-Shavit Removing a Node abd remov e b remov e c c CAS failed

Art of Multiprocessor Programming© Herlihy-Shavit Removing a Node abd remov e b remov e c c

Art of Multiprocessor Programming© Herlihy-Shavit Removing a Node ad remov e b remov e c

Art of Multiprocessor Programming© Herlihy-Shavit 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© Herlihy-Shavit Lock-Free Traversal abcd CAS Uh-oh

Art of Multiprocessor Programming© Herlihy-Shavit 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© Herlihy-Shavit 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© Herlihy-Shavit Using the Find Method Window window = find(head, key); Node pred = window.pred; curr = window.curr;

Art of Multiprocessor Programming© Herlihy-Shavit Using the Find Method Window window = find(head, key); Node pred = window.pred; curr = window.curr; Find returns window

Art of Multiprocessor Programming© Herlihy-Shavit Using the Find Method Window window = find(head, key); Node pred = window.pred; curr = window.curr; Extract pred and curr

Art of Multiprocessor Programming© Herlihy-Shavit The Find Method Window window = find(item); At some instant, predcurrsucc item or …

Art of Multiprocessor Programming© Herlihy-Shavit The Find Method Window window = find(item); At some instant, pred curr= null succ item not in list

Art of Multiprocessor Programming© Herlihy-Shavit 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© Herlihy-Shavit 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© Herlihy-Shavit 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© Herlihy-Shavit 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© Herlihy-Shavit 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; }}} Try to mark node as deleted

Art of Multiprocessor Programming© Herlihy-Shavit 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; }}} If it doesn’t work, just retry, if it does, job essentially done

Art of Multiprocessor Programming© Herlihy-Shavit 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; }}} Try to advance reference (if we don’t succeed, someone else did or will). a

Art of Multiprocessor Programming© Herlihy-Shavit 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© Herlihy-Shavit 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© Herlihy-Shavit 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© Herlihy-Shavit 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; }}}} Install new node, else retry loop

Art of Multiprocessor Programming© Herlihy-Shavit 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© Herlihy-Shavit Wait-free Contains public boolean contains(T 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]) } Only diff is that we get and check marked

Art of Multiprocessor Programming© Herlihy-Shavit 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© Herlihy-Shavit 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; } }} If list changes while traversed, start over Lock-Free because we start over only if someone else makes progress

Art of Multiprocessor Programming© Herlihy-Shavit 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; } }} Lock-free Find Start looking from head

Art of Multiprocessor Programming© Herlihy-Shavit 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; } }} Lock-free Find Move down the list

Art of Multiprocessor Programming© Herlihy-Shavit 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; } }} Lock-free Find Get ref to successor and current deleted bit

Art of Multiprocessor Programming© Herlihy-Shavit 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; } }} Lock-free Find Try to remove deleted nodes in path…code details soon

Art of Multiprocessor Programming© Herlihy-Shavit 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; } }} Lock-free Find If curr key that is greater or equal, return pred and curr

Art of Multiprocessor Programming© Herlihy-Shavit 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; } }} Lock-free Find Otherwise advance window and loop again

Art of Multiprocessor Programming© Herlihy-Shavit 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© Herlihy-Shavit 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); } … Try to snip out node

Art of Multiprocessor Programming© Herlihy-Shavit 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); } … if predecessor’s next field changed must retry whole traversal

Art of Multiprocessor Programming© Herlihy-Shavit 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); } … Otherwise move on to check if next node deleted

Art of Multiprocessor Programming© Herlihy-Shavit Summary: Lock-free Removal a a b c 0 e 1 c Logical Removal = Set Mark Bit Physical Removal CAS pointer Use CAS to verify pointer is correct Not enough!

Art of Multiprocessor Programming© Herlihy-Shavit Lock-free Removal a a b c 0 e 1 c Logical Removal = Set Mark Bit Physical Removal CAS 0 d Problem: d not added to list… Must Prevent manipulation of removed node’s pointer Node added Before Physical Removal CAS

Art of Multiprocessor Programming© Herlihy-Shavit Our Solution: Combine Bit and Pointer a a b c 0 e 1 c Logical Removal = Set Mark Bit Physical Removal CAS 0 d Mark-Bit and Pointer are CASed together Fail CAS: Node not added after logical Removal

Art of Multiprocessor Programming© Herlihy-Shavit A Lock-free Algorithm a a b c 0 e 1 c 1. add() and remove() physically remove marked nodes 2. Wait-free find() traverses both marked and removed nodes

Art of Multiprocessor Programming© Herlihy-Shavit Performance On 16 node shared memory machine Benchmark throughput of Java List-based Set algs. Vary % of Contains() method Calls.

Art of Multiprocessor Programming© Herlihy-Shavit High Contains Ratio Lock-free Lazy list Course Grained Fine Lock-coupling

Art of Multiprocessor Programming© Herlihy-Shavit Low Contains Ratio

Art of Multiprocessor Programming© Herlihy-Shavit As Contains Ratio Increases % Contains()

Art of Multiprocessor Programming© Herlihy-Shavit Summary Coarse-grained locking Fine-grained locking Optimistic synchronization Lock-free synchronization

Art of Multiprocessor Programming© Herlihy-Shavit “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() –Blocking/non-blocking is a property of a method

Art of Multiprocessor Programming© Herlihy-Shavit This work is licensed under a Creative Commons Attribution- ShareAlike 2.5 License.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 – 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.