Adobe PDF: 2011_02_08

advertisement
Universality of Consensus
and
The Nature of Progress
Companion slides for
The Art of Multiprocessor
Programming
by Maurice Herlihy & Nir Shavit
Turing Computability
1
0 1 1 0 1 0
• A mathematical model of computation
• Computable = Computable on a T-Machine
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
2
Shared-Memory Computability
cache
cache
cache
shared memory
• Model of asynchronous concurrent
computation
• Computable = Wait-free/Lock-free
computable on
aMultiprocessor
multiprocessor
Art of
Programming© Copyright
Herlihy-Shavit 2007
3
Can we implement them from
The Consensus
Hierarchy
any other object
that has
consensus number ∞?
1 Read/Write Like
Registers,
Snapshots…
compareAndSet()…
2 getAndSet,
getAndIncrement,
…
FIFO Queue,
LIFO Stack
.
.
.
∞
Multiple Assignment
compareAndSet,…
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
4
Theorem: Universality
• Consensus is universal
• From n-thread consensus build a
–
–
–
–
Wait-free
Linearizable
n-threaded implementation
Of any sequentially specified object
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
5
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 …
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
6
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)
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
7
A Generic Sequential Object
public interface SeqObject {
public abstract Response
apply(Invocation invoc);
}
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
8
A Generic Sequential Object
public interface SeqObject {
public abstract Response
apply(Invocation invoc);
}
Push:5, Pop:null
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
9
Invocation
public class Invoc {
public String method;
public Object[] args;
}
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
10
Invocation
public class Invoc {
public String method;
public Object[] args;
}
Method name
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
11
Invocation
public class Invoc {
public String method;
public Object[] args;
}
Arguments
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
12
A Generic Sequential Object
public interface SeqObject {
public abstract Response
apply(Invocation invoc);
}
OK, 4
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
13
Response
public class Response {
public Object value;
}
Return value
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
14
A Universal Concurrent Object
public interface SeqObject {
public abstract Response
apply(Invocation invoc);
}
A universal concurrent object is a
concurrent object that is linearizable
to the generic sequential object
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
15
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
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
16
Lock-free Construction:
Naïve Idea
• Use consensus object to store
pointer to cell with current state
• Each thread creates new cell
– computes outcome,
– and tries to switch pointer to its
outcome
• Unfortunately not…
– consensus objects can be used once only
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
17
Naïve Idea
deq
enq
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
18
Naïve Idea
Concurrent
Object
enq
Decide which
to apply using
consensus
head
deq
?
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
19
Why only once? Why is
consensus object not readable?
Queue based
consensus
public decide(object value) {
propose(value);
Ball ball = this.queue.deq();
if (ball == Ball.RED)
return proposed[i];
else
return proposed[1-i];
}
Solved one time 2-consensus. Not clear how to
allow reuse of object or reading its state…
(1)
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
20
Improved Idea: Linked-List
Representation
Each node contains a fresh
consensus object used to
decide on next operation
deq
tail
enq
enq
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
enq
21
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 traversing the log
upto the call
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
22
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
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
23
Basic Data Structures
public class Node implements
java.lang.Comparable {
public Invoc invoc;
public Consensus<Node> decideNext;
public Node next;
public int seq;
public Node(Invoc invoc) {
invoc = invoc;
decideNext = new Consensus<Node>()
seq = 0;
}
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
24
Basic Data Structures
public class Node implements
java.lang.Comparable {
public Invoc invoc;
public Consensus<Node> decideNext;
public Node next;
public int seq;
public
Node(Invoc
invoc)
Standard
interface
for {class whose
invoc
= invoc;
objects
are totally ordered
decideNext = new Consensus<Node>()
seq = 0;
}
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
25
Basic Data Structures
public class Node implements
java.lang.Comparable {
public Invoc invoc;
public Consensus<Node> decideNext;
public Node next;
public int seq;
public Node(Invoc
invoc) {
the invocation
invoc = invoc;
decideNext = new Consensus<Node>()
seq = 0;
}
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
26
Basic Data Structures
public class Node implements
java.lang.Comparable {
public Invoc invoc;
public Consensus<Node> decideNext;
public Node next;
public int seq;
public Node(Invoc invoc) {
invoc = invoc;
Decide
on next
node
decideNext
= new
Consensus<Node>()
(next
method applied to object)
seq
= 0;
}
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
27
Basic Data Structures
public class Node implements
java.lang.Comparable {
public Invoc invoc;
public Consensus<Node> decideNext;
public Node next;
public int seq;
public Node(Invoc invoc) {
invoc = invoc;
Traversable
to next node
decideNext =pointer
new Consensus<Node>()
because you cannot
seq(needed
= 0;
}repeatedly read
a consensus object)
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
28
Basic Data Structures
public class Node implements
java.lang.Comparable {
public Invoc invoc;
public Consensus<Node> decideNext;
public Node next;
public int seq;
public Node(Invoc invoc) {
invoc = invoc;
number
decideNext Seq
= new
Consensus<Node>()
seq = 0;
}
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
29
Basic Data Structures
Create a new node for a given
public class Node implements
method invocation
java.lang.Comparable {
public Invoc invoc;
public Consensus<Node> decideNext;
public Node next;
public int seq;
public Node(Invoc invoc) {
invoc = invoc;
decideNext = new Consensus<Node>()
seq = 0;
}
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
30
Universal Object
Seq number,
Invoc
next
node
tail
1
2
3
4
decideNext
(Consensus
Object)
head
Ptr to cell
w/highest
Seq Num
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
31
Universal Object
node
tail
1
2
3
4
head
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
All threads
repeatedly
modify
head…back
to where we
started?
32
The Solution
Threads find head
by finding Max of
nodes pointed to
by head array
node
tail
1
2
3
4
…
head
i
Make head
an array
Thread i
updates
i
Art location
of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
Ptr to
node at
front
33
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
}
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
34
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
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
35
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
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
36
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
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
37
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
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
38
Find Max Head Value
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;
}
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
39
Find Max Head Value
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;
}
Traverse
the array
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
40
Find Max Head Value
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;
}
Compare the seq nums of nodes
pointed to by the array
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
41
Find Max Head Value
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;
}
Return the node with max seq num
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
42
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;
}
…
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
43
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]
= have
after;
Apply will
invocation as input
} return the appropriate response
and
…
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
44
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]My
= after;
id
}
…
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
45
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
…
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
46
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);
As long as I
before.next = after;
after.seq = before.seq
1; been
have +not
head[i] = after;
threaded into
}
list
…
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
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;
Node at head of
after.seq = before.seq
+ 1;I will
list that
head[i] = after;
try
to
append
to
}
…
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
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;
Decide
winning
after.seq = before.seq
+ 1;
head[i] = after; node; could have
}
already been
}
decided
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
49
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;
Could have already
Set next pointer
}
been
set by winner…in based on decision
}
which case no affect
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
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;
Set seq number
}
…
indicating node
was appended
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
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;
add to head
head[i] = after;
array so new
}
…
head will be
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
found
52
Part II – Compute Response
Red’s
method call
tail
null
enq( )
Private copy
of object
enq( )
deq()
…
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
Return
53
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);
}
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
54
Universal Application Part II
...
//compute my response
SeqObject MyObject = new SeqObject();
current = tail.next;
while (current != prefer){
MyObject.apply(current.invoc);
current = current.next;
}
Compute
the result by
return
MyObject.apply(current.invoc);
} sequentially applying the
method calls in the list to a
private copy of the object
Art of Multiprocessor
Programming©
Copyright
starting from the
initial
state
Herlihy-Shavit 2007
55
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 initialized copy of
the sequential object
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
56
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);
}
First new method call is
appended after the tail
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
57
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 not reached my own
Art ofmethod
Multiprocessor call
Programming© Copyright
Herlihy-Shavit 2007
58
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 the current nodes
Art of Multiprocessor
method
to object
Programming© Copyright
Herlihy-Shavit 2007
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);
}
Return the result after
Art ofmy
Multiprocessor
applying
own method call
Programming© Copyright
Herlihy-Shavit 2007
60
Correctness
• List defines linearized sequential
history
• Thread returns its response based on
list order
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
61
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
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
62
Wait-free Construction
• Lock-free construction + announce
array
• Stores (pointer to) node in announce
– If a thread doesn’t append its node
– Another thread will see it in array and
help append it
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
63
Helping
• “Announcing” my intention
– Guarantees progress
– Even if the scheduler hates me
– My method call will complete
• Makes protocol wait-free
• Otherwise starvation possible
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
64
Wait-free Construction
announce
…
i
tail
1
Ptr to the cell
thread i wants
to append
2
3
4
…
head
i
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
65
The Announce Array
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
};
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
66
The Announce Array
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
};
Announce array
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
67
The Announce Array
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
};
All entries initially point to tail
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
68
A Cry For Help
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
…
}
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
69
A Cry For Help
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
…
}
Announce new method call (node), asking help
from others
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
70
A Cry For Help
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
…
}
Look for end of list
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
71
A Cry For Help
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
…
}
Main loop, while node not appended (either by
me or some thread helping me)
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
72
Main Loop
• Non-zero sequence number indicates
success
• Thread keeps helping append nodes
• Until its own node is appended
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
73
Main Loop
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];
…
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
74
Main Loop
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];
Keep
trying= until
my cell gets a
…
sequence number
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
75
Main Loop
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];
Possible end of list
…
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
76
Main Loop
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];
…
Who do I help?
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
77
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
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
78
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
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
79
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
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
80
Sliding Window Lemma
• After thread A announces its node
• No more than n other calls
– Can start and finish
– Without appending A’s node
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
81
Helping
So all see
and help
append 4
Thread 4:
Help me!
announce
Max head
+1 = N+4
4
tail
1
2
3
…
N+2 N+3
head
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
82
The Sliding Help Window
announce
3 4
tail
1
2
3
…
Help 3Help 4
N+2 N+3
head
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
83
Sliding Help Window
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];
…
In each main loop iteration pick
another thread to help
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
84
Sliding Help Window
Help if help required, but
otherwise==it’s
while (announce[i].seq
0)all
{ about me!
Node before = head[i];
Node help = announce[(before.seq + 1 % n)];
if (help.seq == 0)
prefer = help;
else
prefer = announce[i];
…
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
85
Rest is Same as Lock-free
while (prefer.seq == 0) {
…
Node after =
before.decideNext.decide(prefer);
before.next = after;
after.seq = before.seq + 1;
head[i] = after;
}
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
86
Rest is Same as Lock-free
while (prefer.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
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
87
Rest is Same as Lock-free
while (prefer.seq == 0) {
… Update next based on decision
Node after =
before.decideNext.decide(prefer);
before.next = after;
after.seq = before.seq + 1;
head[i] = after;
}
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
88
Rest is Same as Lock-free
while (prefer.seq == 0) {
… Tell world that node is appended
Node after =
before.decideNext.decide(prefer);
before.next = after;
after.seq = before.seq + 1;
head[i] = after;
}
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
89
Finishing the Job
• Once thread’s node is linked
• The rest is again the same as in lockfree 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
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
90
Then Same 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);
}
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
91
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 the result after
Art ofmy
Multiprocessor
applying
own method call
Programming© Copyright
Herlihy-Shavit 2007
92
Shared-Memory Computability
Universal
10011
Object
Wait-free/Lock-free computable
=
Threads with methods that solve nconsensus
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
93
GetAndSet is not Universal
public class RMWRegister {
private int value;
public boolean getAndSet(int update)
{
int prior = this.value;
this.value = update;
return prior;
}
}
(1)
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
94
GetAndSet is not Universal
public class RMWRegister {
private int value;
public boolean getAndSet(int update)
{
int prior = this.value;
this.value = update;
return prior;
}
Consensus number 2
}
(1)
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
95
GetAndSet is not Universal
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
}
(1)
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
96
CompareAndSet is Universal
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;
}}
(1)
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
97
CompareAndSet is Universal
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;
Consensus number ∞
}}
(1)
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
98
CompareAndSet is Universal
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;
Universal for any number of threads
}}
(1)
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
99
On Older Architectures
• IBM 360
– testAndSet (getAndSet)
• NYU UltraComputer
– getAndAdd
• Neither universal
– Except for 2 threads
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
100
On Newer Architectures
• Intel x86, Itanium, SPARC
– compareAndSet
• Alpha AXP, PowerPC
– Load-locked/store-conditional
• All universal
– For any number of threads
• Trend is clear …
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
101
Practical Implications
• Any architecture that does not
provide a universal primitive has
inherent limitations
• You cannot avoid locking for
concurrent data structures …
• But why do we care?
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
102
Locking and Schedeuling
• What are the practical implications
of locking?
• Locking affects the assumptions we
need to make on the operating system
in order to guarantee progress
• Lets understand how…
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
103
Schedeuling
• The scheduler is a part of the OS
that determines
– Which thread gets to run on which
processor
– How long it runs for
• A given thread can thus be active,
that is, executing instructions, or
suspended
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
104
Review Progress Conditions
• Deadlock-free: some thread trying to acquire the
locks eventually succeeds.
• Starvation-free: every thread trying to acquire
the locks eventually succeeds.
• Lock-free: some thread calling the method
eventually returns.
• Wait-free: every thread calling the method
eventually returns.
• Obstruction-free: every thread calling the
method returns if it executes in isolation for long
enough.
105
The Simple Snapshot is
Obstruction-Free
• Put increasing labels on each entry
• Collect twice
• If both agree,
– We’re done
• Otherwise,
– Try again
Collect2
Collect1
1
1
22
1
22
1
7
13
18
12
© 2007 Herlihy & Shavit
=
7
13
18
12
106
Obstruction-freedom
• In the simple snapshot alg:
• The update method is wait-free
• But the scan is obstruction-free: will
complete only if it executes for long
enough without concurrent updates.
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
107
Progress of Methods
• Some of the above defs refer to
locks (part of implementation) or
method calls
• And they ignore the scheduler
• Lets refine our progress definitions
so that they apply to methods, and
• Take scheduling into account
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
108
A “Periodic Table” of
Progress Conditions
Non-Blocking
Everyone
makes
progress
Waitfree
Someone
makes
progress
Lockfree
Obstructionfree
Blocking
Starvationfree
Deadlockfree
109
A bit more formally
• Standard notion of abstract object
• Progress conditions relate to method
calls of an object
• Threads on a multiprocessor never
fail:
– A thread is active if it takes an infinite
number of concrete (machine level) steps
– And is suspended if not.
110
Maximal vs. Minimal
• For a given history H:
• Minimal progress: in every suffix of
H, some method call eventually
completes.
• Maximal progress: in every suffix of
H, every method call eventually
completes.
111
The “Periodic Table” of
Progress Conditions
Non-Blocking
Maximal
progress
Minimal
progress
Waitfree
Lockfree
Blocking
Obstruction- Starvationfree
free
Deadlockfree
112
The Scheduler’s Role
On a multiprocessor progress
properties:
• Are not about the guarantees a
method's implementation provides.
• They are about the scheduling
assumptions needed in order to provide
minimal or maximal progress.
113
Fair Scheduling
• A history is fair if each thread
takes an infinite number of steps
• A method implementation is
deadlock-free if it guarantees
minimal progress in every fair
history, and maximal progress in
some fair history.
114
Starvation Freedom
• A method implementation is
starvation-free if it guarantees
maximal progress in every fair
history.
115
Dependent Progress
• A progress condition is dependent if
it does not guarantee minimal
progress in every history, and is
independent if it does.
• The blocking progress conditions
(deadlock-freedom, Starvationfreedom) are dependent
116
Non-blocking Independent
Conditions
• A method implementation is lockfree if it guarantees minimal
progress in every history, and
maximal progress in some history.
• A method implementation is waitfree if it guarantees maximal
progress in every history.
117
The “Periodic Table” of
Progress Conditions
Non-Blocking
Maximal
progress
Minimal
progress
Waitfree
Blocking
Obstruction- Starvationfree
free
Lockfree
Deadlockfree
Independent
Dependent
118
Uniformly Isolating
Schedules
• A history is uniformly isolating if, for
every k > 0, any thread that takes an
infinite number of steps has an
interval where it takes at least k
contiguous steps
• Modern systems provide ways of
providing isolation…later we will learn
about “backoff” and “yeild”.
119
A Non-blocking Dependent
Condition
• A method implementation is
obstruction-free if it guarantees
maximal progress in every uniformly
isolating history.
120
The “Periodic Table” of
Progress Conditions
Non-Blocking
Blocking
Maximal
progress
Uniform iso
Obstruction- StarvationWait-scheduler
Fair scheduler
free
free
free
Minimal
progress
Lockfree
Independent
DeadlockFair scheduler
free
Dependent
121
The “Periodic Table” of
Progress Conditions
Blocking
Non-Blocking
Maximal
progress
Waitfree
Minimal
progress
Lockfree
Independent
Obstruction- Starvationfree
free
?
Clashfree
Deadlockfree
Dependent
122
Clash-Freedom: the
“Einsteinium” of Progress
• A method implementation is clash-free
if it guarantees minimal progress in
every uniformly isolating history.
• Thm: clash-freedom strictly weaker
than obstruction-freedom
123
Getting from Minimal to
Maximal
Blocking
Non-Blocking
Maximal
progress
Minimal
progress
Waitfree
Lockfree
Independent
Obstruction- Starvationfree
free
?
?
Clashfree
Deadlock- Helping
free
Dependent
124
Universal Constructions
• Our lock-free universal construction
provides minimal progress
• A scheduler is benevolent for that
algorithm if it guarantees maximal
progress in every allowable history.
• Many real-world operating system
schedulers are benevolent
• They do not single out any indiviudual
thread
125
Getting
from Minimal to
Universal Lock-free
Construction
Maximal
Universal Wait-free
Non-Blocking
Maximal
progress
Minimal
progress
Waitfree
Construction
Blocking
Obstruction- Starvationfree
free
?
?
ClashLockDeadlock- Helping
free
free Use Wait-free/Lock-free
free
Consensus Objects
Independent
Dependent
126
Getting
from Construction
Minimal
Universal
Wait-free
Maximal
Non-Blocking
to
Blocking
Universal Lock-free
Maximal Construction
Obstruction- StarvationWaitprogress
free
Minimal
progress
Lockfree
free
free
?
Clashfree
Deadlockfree
If we use Starvation-free/Deadlock-free
Consensus
Objects result Dependent
is respectively
Independent
127
Starvation-free/Deadlock-free
Benevolent Schedulers
• Consider an algorithm that guarantees
minimal progress.
• A scheduler is benevolent for that
algorithm if it guarantees maximal
progress in every allowable history.
• Many real-world operating system
schedulers are benevolent
• They do not single out any indiviudual
thread
128
In Practice
On a multiprocessor we will write code
expecting maximal progress.
Progress conditions will then define the
scheduling assumptions needed in order
to provide it.
s129
This Means
We will mostly write lock-free and
lock-based deadlock-free algorithms…
and expect them to behave as if they
are wait-free…
because modern schedulers can be
made benevolent and fair.
s130
Principles to Practice
• We learned how to define the safety
(correctness) and liveness (progress)
of concurrent programs and objects
• We are ready to start the practice of
implementing them
• Next lecture: implementing spin locks
on multiprocesor machines…
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
131
This work is licensed under a Creative Commons AttributionShareAlike 2.5 License.
•
•
•
•
•
You are free:
– to Share — to copy, distribute and transmit the work
– to Remix — to adapt the work
Under the following conditions:
– Attribution. You must attribute the work to ―The Art of Multiprocessor
Programming‖ (but not in any way that suggests that the authors endorse
you or your use of the work).
– Share Alike. If you alter, transform, or build upon this work, you may
distribute the resulting work only under the same, similar or a compatible
license.
For any reuse or distribution, you must make clear to others the license terms of
this work. The best way to do this is with a link to
– http://creativecommons.org/licenses/by-sa/3.0/.
Any of the above conditions can be waived if you get permission from the
copyright holder.
Nothing in this license impairs or restricts the author's moral rights.
Art of Multiprocessor
Programming© Copyright
Herlihy-Shavit 2007
132
Download