Presentation is loading. Please wait.

Presentation is loading. Please wait.

Universality of Consensus Companion slides for The Art of Multiprocessor Programming by Maurice Herlihy & Nir Shavit.

Similar presentations


Presentation on theme: "Universality of Consensus Companion slides for The Art of Multiprocessor Programming by Maurice Herlihy & Nir Shavit."— Presentation transcript:

1 Universality of Consensus Companion slides for The Art of Multiprocessor Programming by Maurice Herlihy & Nir Shavit

2 Turing Computability A mathematical model of computation Computable = Computable on a T-Machine Art of Multiprocessor Programming

3 Shared-Memory Computability Model of asynchronous concurrent computation Computable = Wait-free/Lock-free computable on a multiprocessor cache shared memory cache 3 Art of Multiprocessor Programming

4 Consensus Hierarchy 1 Read/Write Registers, Snapshots… 2 getAndSet, getAndIncrement, … compareAndSet,… Art of Multiprocessor Programming

5 Who Implements Whom? 1 Read/Write Registers, Snapshots… 2 getAndSet, getAndIncrement, … compareAndSet,… no 5 Art of Multiprocessor Programming

6 Hypothesis 1 Read/Write Registers, Snapshots… 2 getAndSet, getAndIncrement, … compareAndSet,… yes? 6 Art of Multiprocessor Programming

7 Theorem: Universality Consensus is universal From n-thread consensus build a –Wait-free –Linearizable –n-threaded implementation –Of any sequentially specified object 7 Art of Multiprocessor Programming

8 Proof Outline A universal construction –From n-consensus objects –And atomic registers Any wait-free linearizable object –Not a practical construction –But we know where to start looking … 8 Art of Multiprocessor Programming

9 Like a Turing Machine This construction –Illustrates what needs to be done –Optimization fodder Correctness, not efficiency –Why does it work? (Asks the scientist) –How does it work? (Asks the engineer) –Would you like fries with that? (Asks the liberal arts major) 9 Art of Multiprocessor Programming

10 A Generic Sequential Object public interface SeqObject { public abstract Response apply(Invocation invoc); } 10 Art of Multiprocessor Programming

11 A Generic Sequential Object public interface SeqObject { public abstract Response apply(Invocation invoc); } Push:5, Pop:null 11 Art of Multiprocessor Programming

12 Invocation public class Invoc { public String method; public Object[] args; } 12 Art of Multiprocessor Programming

13 Invocation public class Invoc { public String method; public Object[] args; } Method name 13 Art of Multiprocessor Programming

14 Invocation public class Invoc { public String method; public Object[] args; } Arguments 14 Art of Multiprocessor Programming

15 A Generic Sequential Object public interface SeqObject { public abstract Response apply(Invocation invoc); } 15 Art of Multiprocessor Programming

16 A Generic Sequential Object public interface SeqObject { public abstract Response apply(Invocation invoc); } OK, 4 16 Art of Multiprocessor Programming

17 Response public class Response { public Object value; } 17 Art of Multiprocessor Programming

18 Response public class Response { public Object value; } Return value 18 Art of Multiprocessor Programming

19 19 Universal Concurrent Object public interface SeqObject { public abstract Response apply(Invocation invoc); } A universal concurrent object is linearizable to the generic sequential object 19 Art of Multiprocessor Programming

20 Start with Lock-Free Universal Construction First Lock-free: infinitely often some method call finishes. Then Wait-Free: each method call takes a finite number of steps to finish 20 Art of Multiprocessor Programming

21 Naïve Idea Consensus object stores reference to cell with current state Each thread creates new cell –computes outcome, –tries to switch pointer to its outcome Sadly, no … –consensus objects can be used once only 21 Art of Multiprocessor Programming

22 Naïve Idea enq deq 22 Art of Multiprocessor Programming

23 head Naïve Idea deq Concurrent Object enq ? Decide which to apply using consensus No good. Each thread can use consensus object only once 23 Art of Multiprocessor Programming

24 Once is not Enough? public T decide(T value) { propose(value); Ball ball = queue.deq(); if (ball == Ball.RED) return proposed[i]; else return proposed[1-i]; } Solved one-shot 2-consensus. Not clear how to reuse or reread … Queue based consensus 24 Art of Multiprocessor Programming

25 Improved Idea: Linked-List Representation enq tail deq Each node contains a fresh consensus object used to decide on next operation 25 Art of Multiprocessor Programming

26 Universal Construction Object represented as –Initial Object State –A Log: a linked list of the method calls 26 Art of Multiprocessor Programming

27 Universal Construction Object represented as –Initial Object State –A Log: a linked list of the method calls New method call –Find end of list –Atomically append call –Compute response by replaying log 27 Art of Multiprocessor Programming

28 Basic Idea Use one-time consensus object to decide next pointer All threads update actual next pointer based on decision –OK because they all write the same value Challenges –Lock-free means we need to worry what happens if a thread stops in the middle 28 Art of Multiprocessor Programming

29 public class Node implements java.lang.Comparable { public Invoc invoc; public Consensus decideNext; public Node next; public int seq; public Node(Invoc invoc) { invoc = invoc; decideNext = new Consensus () seq = 0; } Basic Data Structures 29 Art of Multiprocessor Programming

30 public class Node implements java.lang.Comparable { public Invoc invoc; public Consensus decideNext; public Node next; public int seq; public Node(Invoc invoc) { invoc = invoc; decideNext = new Consensus () seq = 0; } Basic Data Structures Standard interface for class whose objects are totally ordered 30 Art of Multiprocessor Programming

31 public class Node implements java.lang.Comparable { public Invoc invoc; public Consensus decideNext; public Node next; public int seq; public Node(Invoc invoc) { invoc = invoc; decideNext = new Consensus () seq = 0; } Basic Data Structures the invocation 31 Art of Multiprocessor Programming

32 public class Node implements java.lang.Comparable { public Invoc invoc; public Consensus decideNext; public Node next; public int seq; public Node(Invoc invoc) { invoc = invoc; decideNext = new Consensus () seq = 0; } Basic Data Structures Decide on next node (next method applied to object) 32 Art of Multiprocessor Programming

33 public class Node implements java.lang.Comparable { public Invoc invoc; public Consensus decideNext; public Node next; public int seq; public Node(Invoc invoc) { invoc = invoc; decideNext = new Consensus () seq = 0; } Basic Data Structures Traversable pointer to next node (needed because you cannot repeatedly read a consensus object) 33 Art of Multiprocessor Programming

34 public class Node implements java.lang.Comparable { public Invoc invoc; public Consensus decideNext; public Node next; public int seq; public Node(Invoc invoc) { invoc = invoc; decideNext = new Consensus () seq = 0; } Basic Data Structures Seq number 34 Art of Multiprocessor Programming

35 public class Node implements java.lang.Comparable { public Invoc invoc; public Consensus decideNext; public Node next; public int seq; public Node(Invoc invoc) { invoc = invoc; decideNext = new Consensus () seq = 0; } Basic Data Structures Create new node for this method invocation 35 Art of Multiprocessor Programming

36 Universal Object head 123 decideNext (Consensus Object) Ptr to cell w/highest Seq Num Seq number, Invoc tail node next 4 36 Art of Multiprocessor Programming

37 Universal Object head 123 All threads repeatedly modify head…back to where we started? tail node 4 37 Art of Multiprocessor Programming

38 … The Solution head 123 tail node i 4 Make head an array Ptr to node at front Thread i updates location i Threads find head by finding Max of nodes pointed to by head array 38 Art of Multiprocessor Programming

39 Universal Object public class Universal { private Node[] head; private Node tail = new Node(); tail.seq = 1; for (int j=0; j < n; j++){ head[j] = tail } 39 Art of Multiprocessor Programming

40 Universal Object public class Universal { private Node[] head; private Node tail = new Node(); tail.seq = 1; for (int j=0; j < n; j++){ head[j] = tail } Head Pointers Array 40 Art of Multiprocessor Programming

41 Universal Object public class Universal { private Node[] head; private Node tail = new Node(); tail.seq = 1; for (int j=0; j < n; j++){ head[j] = tail } Tail is a sentinel node with sequence number 1 41 Art of Multiprocessor Programming

42 Universal Object public class Universal { private Node[] head; private Node tail = new Node(); tail.seq = 1; for (int j=0; j < n; j++){ head[j] = tail } Initially head points to tail 42 Art of Multiprocessor Programming

43 public static Node max(Node[] array) { Node max = array[0]; for (int i = 1; i < array.length; i++) if (max.seq < array[i].seq) max = array[i]; return max; } Find Max Head Value 43 Art of Multiprocessor Programming

44 public static Node max(Node[] array) { Node max = array[0]; for (int i = 0; i < array.length; i++) if (max.seq < array[i].seq) max = array[i]; return max; } Find Max Head Value Traverse the array 44 Art of Multiprocessor Programming

45 public static Node max(Node[] array) { Node max = array[0]; for (int i = 0; i < array.length; i++) if (max.seq < array[i].seq) max = array[i]; return max; } Find Max Head Value Compare the seq nums of nodes pointed to by the array 45 Art of Multiprocessor Programming

46 public static Node max(Node[] array) { Node max = array[0]; for (int i = 0; i < array.length; i++) if (max.seq < array[i].seq) max = array[i]; return max; } Find Max Head Value Return node with maximal sequence number 46 Art of Multiprocessor Programming

47 Universal Application Part I public Response apply(Invoc invoc) { int i = ThreadID.get(); Node prefer = new node(invoc); while (prefer.seq == 0) { Node before = Node.max(head); Node after = before.decideNext.decide(prefer); before.next = after; after.seq = before.seq + 1; head[i] = after; } … 47 Art of Multiprocessor Programming

48 Universal Application Part I public Response apply(Invoc invoc) { int i = ThreadID.get(); Node prefer = new node(invoc); while (prefer.seq == 0) { Node before = Node.max(head); Node after = before.decideNext.decide(prefer); before.next = after; after.seq = before.seq + 1; head[i] = after; } … Apply will have invocation as input and return the appropriate response 48 Art of Multiprocessor Programming

49 Universal Application Part I public Response apply(Invoc invoc) { int i = ThreadID.get(); Node prefer = new node(invoc); while (prefer.seq == 0) { Node before = Node.max(head); Node after = before.decideNext.decide(prefer); before.next = after; after.seq = before.seq + 1; head[i] = after; } … My id 49 Art of Multiprocessor Programming

50 Universal Application Part I public Response apply(Invoc invoc) { int i = ThreadID.get(); Node prefer = new node(invoc); while (prefer.seq == 0) { Node before = Node.max(head); Node after = before.decideNext.decide(prefer); before.next = after; after.seq = before.seq + 1; head[i] = after; } … My method call 50 Art of Multiprocessor Programming

51 Universal Application Part I public Response apply(Invoc invoc) { int i = ThreadID.get(); Node prefer = new node(invoc); while (prefer.seq == 0) { Node before = Node.max(head); Node after = before.decideNext.decide(prefer); before.next = after; after.seq = before.seq + 1; head[i] = after; } … As long as I have not been threaded into list 51 Art of Multiprocessor Programming

52 Universal Application Part I public Response apply(Invoc invoc) { int i = ThreadID.get(); Node prefer = new node(invoc); while (prefer.seq == 0) { Node before = Node.max(head); Node after = before.decideNext.decide(prefer); before.next = after; after.seq = before.seq + 1; head[i] = after; } … Head of list to which we will try to append 52 Art of Multiprocessor Programming

53 Universal Application Part I public Response apply(Invoc invoc) { int i = ThreadID.get(); Node prefer = new node(invoc); while (prefer.seq == 0) { Node before = Node.max(head); Node after = before.decideNext.decide(prefer); before.next = after; after.seq = before.seq + 1; head[i] = after; } Propose next node 53 Art of Multiprocessor Programming

54 Universal Application public Response apply(Invoc invoc) { int i = ThreadID.get(); Node prefer = new node(invoc); while (prefer.seq == 0) { Node before = Node.max(head); Node after = before.decideNext.decide(prefer); before.next = after; after.seq = before.seq + 1; head[i] = after; } Set next field to consensus winner 54 Art of Multiprocessor Programming

55 Universal Application Part I public Response apply(Invoc invoc) { int i = ThreadID.get(); Node prefer = new node(invoc); while (prefer.seq == 0) { Node before = Node.max(head); Node after = before.decideNext.decide(prefer); before.next = after; after.seq = before.seq + 1; head[i] = after; } … Set seq number (indicating node was appended) 55 Art of Multiprocessor Programming

56 Universal Application Part I public Response apply(Invoc invoc) { int i = ThreadID.get(); Node prefer = new node(invoc); while (prefer.seq == 0) { Node before = Node.max(head); Node after = before.decideNext.decide(prefer); before.next = after; after.seq = before.seq + 1; head[i] = after; } … add to head array so new head will be found 56 Art of Multiprocessor Programming

57 Part 2 – Compute Response nullenq( ) tail Reds method call … deq() enq( ) Return Private copy of object 57 Art of Multiprocessor Programming

58 Universal Application Part 2... //compute my response SeqObject MyObject = new SeqObject(); current = tail.next; while (current != prefer){ MyObject.apply(current.invoc); current = current.next; } return MyObject.apply(current.invoc); } 58 Art of Multiprocessor Programming

59 Universal Application Part II... //compute my response SeqObject MyObject = new SeqObject(); current = tail.next; while (current != prefer){ MyObject.apply(current.invoc); current = current.next; } return MyObject.apply(current.invoc); } Compute result by sequentially applying method calls in list to a private copy 59 Art of Multiprocessor Programming

60 Universal Application Part II... //compute my response SeqObject MyObject = new SeqObject(); current = tail.next; while (current != prefer){ MyObject.apply(current.invoc); current = current.next; } return MyObject.apply(current.invoc); } Start with copy of sequential object 60 Art of Multiprocessor Programming

61 Universal Application Part II... //compute my response SeqObject MyObject = new SeqObject(); current = tail.next; while (current != prefer){ MyObject.apply(current.invoc); current = current.next; } return MyObject.apply(current.invoc); } new method call appended after tail 61 Art of Multiprocessor Programming

62 Universal Application Part II... //compute my response SeqObject MyObject = new SeqObject(); current = tail.next; while (current != prefer){ MyObject.apply(current.invoc); current = current.next; } return MyObject.apply(current.invoc); } While my method call not linked … 62 Art of Multiprocessor Programming

63 Universal Application Part II... //compute my response SeqObject MyObject = new SeqObject(); current = tail.next; while (current != prefer){ MyObject.apply(current.invoc); current = current.next; } return MyObject.apply(current.invoc); } Apply current nodes method 63 Art of Multiprocessor Programming

64 Universal Application Part II... //compute my response SeqObject MyObject = new SeqObject(); current = tail.next; while (current != prefer){ MyObject.apply(current.invoc); current = current.next; } return MyObject.apply(current.invoc); } Return result after my method call applied 64 Art of Multiprocessor Programming

65 Correctness List defines linearized sequential history Thread returns its response based on list order 65 Art of Multiprocessor Programming

66 66 Lock-freedom Lock-free because –A thread moves forward in list –Can repeatedly fail to win consensus on real head only if another succeeds –Consensus winner adds node and completes within a finite number of steps 66 Art of Multiprocessor Programming

67 Wait-free Construction Lock-free construction + announce array Stores (pointer to) node in announce –If a thread doesnt append its node –Another thread will see it in array and help append it 67 Art of Multiprocessor Programming

68 Helping Announcing my intention –Guarantees progress –Even if the scheduler hates me –My method call will complete Makes protocol wait-free Otherwise starvation possible 68 Art of Multiprocessor Programming

69 … … Wait-free Construction head 123 tail i 4 announce Ptr to cell thread i wants to append i 69 Art of Multiprocessor Programming

70 public class Universal { private Node[] announce; private Node[] head; private Node tail = new node(); tail.seq = 1; for (int j=0; j < n; j++){ head[j] = tail; announce[j] = tail }; The Announce Array 70 Art of Multiprocessor Programming

71 public class Universal { private Node[] announce; private Node[] head; private Node tail = new node(); tail.seq = 1; for (int j=0; j < n; j++){ head[j] = tail; announce[j] = tail }; The Announce Array Announce array 71 Art of Multiprocessor Programming

72 public class Universal { private Node[] announce; private Node[] head; private Node tail = new node(); tail.seq = 1; for (int j=0; j < n; j++){ head[j] = tail; announce[j] = tail }; The Announce Array All entries initially point to tail 72 Art of Multiprocessor Programming

73 public Response apply(Invoc invoc) { int i = ThreadID.get(); announce[i] = new Node(invoc); head[i] = Node.max(head); while (announce[i].seq == 0) { … // while node not appended to list … } A Cry For Help 73 Art of Multiprocessor Programming

74 public Response apply(Invoc invoc) { int i = ThreadID.get(); announce[i] = new Node(invoc); head[i] = Node.max(head); while (announce[i].seq == 0) { … // while node not appended to list … } A Cry For Help Announce new method call, asking help from others 74 Art of Multiprocessor Programming

75 public Response apply(Invoc invoc) { int i = ThreadID.get(); announce[i] = new Node(invoc); head[i] = Node.max(head); while (announce[i].seq == 0) { … // while node not appended to list … } A Cry For Help Look for end of list 75 Art of Multiprocessor Programming

76 public Response apply(Invoc invoc) { int i = ThreadID.get(); announce[i] = new Node(invoc); head[i] = Node.max(head); while (announce[i].seq == 0) { … // while node not appended to list … } A Cry For Help Main loop, while node not appended (either by me or helper) 76 Art of Multiprocessor Programming

77 Main Loop Non-zero sequence # means success Thread keeps helping append nodes Until its own node is appended 77 Art of Multiprocessor Programming

78 while (announce[i].seq == 0) { Node before = head[i]; Node help = announce[(before.seq + 1 % n)]; if (help.seq == 0) prefer = help; else prefer = announce[i]; … Main Loop 78 Art of Multiprocessor Programming

79 while (announce[i].seq == 0) { Node before = head[i]; Node help = announce[(before.seq + 1 % n)]; if (help.seq == 0) prefer = help; else prefer = announce[i]; … Main Loop Keep trying until my cell gets a sequence number 79 Art of Multiprocessor Programming

80 while (announce[i].seq == 0) { Node before = head[i]; Node help = announce[(before.seq + 1 % n)]; if (help.seq == 0) prefer = help; else prefer = announce[i]; … Main Loop Possible end of list 80 Art of Multiprocessor Programming

81 while (announce[i].seq == 0) { Node before = head[i]; Node help = announce[(before.seq + 1 % n)]; if (help.seq == 0) prefer = help; else prefer = announce[i]; … Main Loop Whom do I help? 81 Art of Multiprocessor Programming

82 Altruism Choose a thread to help If that thread needs help –Try to append its node –Otherwise append your own Worst case –Everyone tries to help same pitiful loser –Someone succeeds 82 Art of Multiprocessor Programming

83 Help! When last node in list has sequence number k All threads check … –Whether thread k+1 mod n wants help –If so, try to append her node first 83 Art of Multiprocessor Programming

84 Help! First time after thread k+1 announces –No guarantees After n more nodes appended –Everyone sees that thread k+1 wants help –Everyone tries to append that node –Someone succeeds 84 Art of Multiprocessor Programming

85 Sliding Window Lemma After thread A announces its node No more than n other calls –Can start and finish –Without appending As node 85 Art of Multiprocessor Programming

86 Helping head Max head +1 = N+4 N+2 N+3 … announce Thread 4: Help me! 4 So all see and help append 4 tail 86 Art of Multiprocessor Programming

87 The Sliding Help Window head N+2 N+3 … announce 4 tail Help 3 Help Art of Multiprocessor Programming

88 while (announce[i].seq == 0) { Node before = head[i]; Node help = announce[(before.seq + 1 % n)]; if (help.seq == 0) prefer = help; else prefer = announce[i]; … Sliding Help Window In each main loop iteration pick another thread to help 88 Art of Multiprocessor Programming

89 while (announce[i].seq == 0) { Node before = head[i]; Node help = announce[(before.seq + 1 % n)]; if (help.seq == 0) prefer = help; else prefer = announce[i]; … Sliding Help Window Help if help required, but otherwise its all about me! 89 Art of Multiprocessor Programming

90 Rest is Same as Lock-free while (announce[i].seq == 0) { … Node after = before.decideNext.decide(prefer); before.next = after; after.seq = before.seq + 1; head[i] = after; } 90 Art of Multiprocessor Programming

91 Rest is Same as Lock-free while (announce[i].seq == 0) { … Node after = before.decideNext.decide(prefer); before.next = after; after.seq = before.seq + 1; head[i] = after; } Decide next node to be appended 91 Art of Multiprocessor Programming

92 Rest is Same as Lock-free while (announce[i].seq == 0) { … Node after = before.decideNext.decide(prefer); before.next = after; after.seq = before.seq + 1; head[i] = after; } Update next based on decision 92 Art of Multiprocessor Programming

93 Rest is Same as Lock-free while (announce[i].seq == 0) { … Node after = before.decideNext.decide(prefer); before.next = after; after.seq = before.seq + 1; head[i] = after; } Tell world that node is appended 93 Art of Multiprocessor Programming

94 Finishing the Job Once threads node is linked The rest is again the same as in lock- free alg Compute the result by sequentially applying the method calls in the list to a private copy of the object starting from the initial state 94 Art of Multiprocessor Programming

95 Then Same Part II... //compute my response SeqObject MyObject = new SeqObject(); current = tail.next; while (current != announce[i]){ MyObject.apply(current.invoc); current = current.next; } return MyObject.apply(current.invoc); } 95 Art of Multiprocessor Programming

96 Universal Application Part II... //compute my response SeqObject MyObject = new SeqObject(); current = tail.next; while (current != prefer){ MyObject.apply(current.invoc); current = current.next; } return MyObject.apply(current.invoc); } Return result after applying my method 96 Art of Multiprocessor Programming

97 Shared-Memory Computability Wait-free/Lock-free computable = Solving n-consensus Universal Object 97 Art of Multiprocessor Programming

98 public class RMWRegister { private int value; public boolean getAndSet(int update) { int prior = this.value; this.value = update; return prior; } Swap (getAndSet) not Universal 98 Art of Multiprocessor Programming

99 public class RMWRegister { private int value; public boolean getAndSet(int update) { int prior = this.value; this.value = update; return prior; } Consensus number 2 Swap (getAndSet) not Universal 99 Art of Multiprocessor Programming

100 public class RMWRegister { private int value; public boolean getAndSet(int update) { int prior = this.value; this.value = update; return prior; } Not universal for 3 threads Swap (getAndSet) not Universal 100 Art of Multiprocessor Programming

101 public class RMWRegister { private int value; public boolean compareAndSet(int expected, int update) { int prior = this.value; if (this.value == expected) { this.value = update; return true; } return false; }} CompareAndSet is Universal 101 Art of Multiprocessor Programming

102 public class RMWRegister { private int value; public boolean compareAndSet(int expected, int update) { int prior = this.value; if (this.value == expected) { this.value = update; return true; } return false; }} CompareAndSet is Universal Consensus number 102 Art of Multiprocessor Programming

103 public class RMWRegister { private int value; public boolean compareAndSet(int expected, int update) { int prior = this.value; if (this.value == expected) { this.value = update; return true; } return false; }} CompareAndSet is Universal Universal for any number of threads 103 Art of Multiprocessor Programming

104 104 On Older Architectures IBM 360 –testAndSet (getAndSet) NYU UltraComputer –getAndAdd (fetchAndAdd) Neither universal –Except for 2 threads 104 Art of Multiprocessor Programming

105 105 On Newer Architectures Intel x86, Itanium, SPARC –compareAndSet (CAS, CMPXCHG) Alpha AXP, PowerPC –Load-locked/store-conditional All universal –For any number of threads Trend is clear … 105 Art of Multiprocessor Programming

106 Practical Implications Any architecture that does not provide a universal primitive has inherent limitations You cannot avoid locking for concurrent data structures … 106 Art of Multiprocessor Programming

107 107 Shared-Memory Computability Wait-free/Lock-free computable = Threads with methods that solve n- consensus Universal Object 107 Art of Multiprocessor Programming

108 Principles to Practice We saw how to define concurrent objects We discussed the computational power of our machine instructions Lets use these foundations as a basis for understanding the real world 108 Art of Multiprocessor Programming

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


Download ppt "Universality of Consensus Companion slides for The Art of Multiprocessor Programming by Maurice Herlihy & Nir Shavit."

Similar presentations


Ads by Google