Documente Academic
Documente Profesional
Documente Cultură
Companion slides for The Art of Multiprocessor Programming by Maurice Herlihy & Nir Shavit
Mutual Exclusion
We will clarify our understanding of mutual exclusion We will also show you how to reason about various properties in an asynchronous concurrent setting
Art of Multiprocessor Programming 2
Mutual Exclusion
In his 1965 paper E. W. Dijkstra wrote:
"Given in this paper is a solution to a problem which, to the knowledge of the author, has been an open question since at least 1962, irrespective of the solvability. [...] Although the setting of the problem might seem somewhat academic at first, the author trusts that anyone familiar with the logical problems that arise in computer coupling will appreciate the significance of the fact that this problem indeed can be solved."
Art of Multiprocessor Programming 3
Mutual Exclusion
Formal problem definitions Solutions for 2 threads Solutions for n threads Fair solutions Inherent costs
Art of Multiprocessor Programming 4
Warning
You will never use these protocols
Get over it
Time
Absolute, true and mathematical time, of itself and from its own nature, flows equably without relation to anything external. (I. Newton, 1689) Time is, like, Natures way of making sure that everything doesnt happen all at once. (Anonymous, circa 1968)
time
Art of Multiprocessor Programming 7
Events
An event a0 of thread A is
Instantaneous No simultaneous events (break ties) a0
time
Art of Multiprocessor Programming 8
Threads
A thread A is (formally) a sequence a0, a1, ... of events
Trace model Notation: a0 a1 indicates order a0
time
Art of Multiprocessor Programming 9
a1
a2
10
a0
a1
11
States
Thread State
Program counter Local variables
System state
Object fields (shared variables) Union of thread states
12
Concurrency
Thread A
time
13
Concurrency
Thread A
time
Thread B
time
14
Interleavings
Events of two or more threads
Interleaved Not necessarily independent (why?)
time
15
Intervals
An interval A0 =(a0,a1) is
Time between events a0 and a1 a0
time
Art of Multiprocessor Programming 16
A0
a1
b0 a0
time
B0 a1
b1
A0
17
b0 a0
time
B0
b1
A0
a1
18
Precedence
Interval A0 precedes interval B0
b0 a0
time
B0
b1
A0
a1
19
Precedence
Notation: A0 B0 Formally,
End event of A0 before start event of B0 Also called happens before or precedes
Art of Multiprocessor Programming 20
Precedence Ordering
Oh wait,
what about this week vs this month?
Art of Multiprocessor Programming 21
Precedence Ordering
Never true that A A If A B then not true that B A If A B & B C then A C Funny thing: A B & B A might both be false!
Art of Multiprocessor Programming 22
Partial Orders
(review)
Irreflexive:
Never true that A A
Antisymmetric:
If A B then not true that B A
Transitive:
If A B & B C then A C
23
Total Orders
(review)
Also
Irreflexive Antisymmetric Transitive
Repeated Events
while (mumble) { a0; a1; }
k-th occurrence
of event a0
a0
k-th occurrence of
A0
interval A0 =(a0,a1)
25
Implementing a Counter
public class Counter { private long value; public long getAndIncrement() { temp = value; value = temp + 1; return temp; Make these } }
27
acquire lock
28
acquire lock
release lock
29
Using Locks
public class Counter { private long value; private Lock lock; public long getAndIncrement() { lock.lock(); try { int temp = value; value = value + 1; } finally { lock.unlock(); } return temp; }}
Art of Multiprocessor Programming 30
Using Locks
public class Counter { private long value; private Lock lock; public long getAndIncrement() { lock.lock(); acquire try { int temp = value; value = value + 1; } finally { lock.unlock(); } return temp; }}
Art of Multiprocessor Programming
Lock
31
Using Locks
public class Counter { private long value; private Lock lock; public long getAndIncrement() { lock.lock(); try { int temp = value; value = value + 1; } finally { Release lock lock.unlock(); } (no matter what) return temp; }}
Art of Multiprocessor Programming 32
Using Locks
public class Counter { private long value; private Lock lock; public long getAndIncrement() { lock.lock(); try { int temp = value; value = value + 1; } finally { lock.unlock(); } return temp; }}
Art of Multiprocessor Programming
Critical section
33
Mutual Exclusion
Let CSik be thread is k-th critical section execution
34
Mutual Exclusion
Let CSik be thread is k-th critical section execution And CSjm be thread js m-th critical section execution
35
Mutual Exclusion
Let CSik be thread is k-th critical section execution And CSjm be js m-th execution Then either
or
36
Mutual Exclusion
Let CSik be thread is k-th critical section execution And CSjm be js m-th execution Then either
or
CSik CSjm
Art of Multiprocessor Programming 37
Mutual Exclusion
Let CSik be thread is k-th critical section execution And CSjm be js m-th execution Then either
or
CSik CSjm
Art of Multiprocessor Programming
CSjm CSik
38
Deadlock-Free
If some thread calls lock()
And never returns Then other threads must complete lock() and unlock() calls infinitely often
39
Starvation-Free
If some thread calls lock()
It will eventually return
40
41
Two-Thread Conventions
class implements Lock { // thread-local index, 0 or 1 public void lock() { int i = ThreadID.get(); int j = 1 - i; } }
42
Two-Thread Conventions
class implements Lock { // thread-local index, 0 or 1 public void lock() { int i = ThreadID.get(); int j = 1 - i; } }
LockOne
class LockOne implements Lock { private boolean[] flag = new boolean[2]; public void lock() { flag[i] = true; while (flag[j]) {} }
LockOne
class LockOne implements Lock { private boolean[] flag = new boolean[2]; public void lock() { flag[i] = true; while (flag[j]) {} Each thread has flag }
LockOne
class LockOne implements Lock { private boolean[] flag = new boolean[2]; public void lock() { flag[i] = true; while (flag[j]) {} Set my flag }
LockOne
class LockOne implements Lock { private boolean[] flag = new boolean[2]; public void lock() { flag[i] = true; while (flag[j]) {} }
48
50
Combining
Assumptions: From the code
readA(flag[B]==false) writeB(flag[B]=true) readB(flag[A]==false) writeA(flag[A]=true) writeA(flag[A]=true) readA(flag[B]==false) writeB(flag[B]=true) readB(flag[A]==false)
51
Combining
Assumptions: From the code
readA(flag[B]==false) writeB(flag[B]=true) readB(flag[A]==false) writeA(flag[A]=true) writeA(flag[A]=true) readA(flag[B]==false) writeB(flag[B]=true) readB(flag[A]==false)
52
Combining
Assumptions: From the code
readA(flag[B]==false) writeB(flag[B]=true) readB(flag[A]==false) writeA(flag[A]=true) writeA(flag[A]=true) readA(flag[B]==false) writeB(flag[B]=true) readB(flag[A]==false)
53
Combining
Assumptions: From the code
readA(flag[B]==false) writeB(flag[B]=true) readB(flag[A]==false) writeA(flag[A]=true) writeA(flag[A]=true) readA(flag[B]==false) writeB(flag[B]=true) readB(flag[A]==false)
54
Combining
Assumptions: From the code
readA(flag[B]==false) writeB(flag[B]=true) readB(flag[A]==false) writeA(flag[A]=true) writeA(flag[A]=true) readA(flag[B]==false) writeB(flag[B]=true) readB(flag[A]==false)
55
Combining
Assumptions: From the code
readA(flag[B]==false) writeB(flag[B]=true) readB(flag[A]==false) writeA(flag[A]=true) writeA(flag[A]=true) readA(flag[B]==false) writeB(flag[B]=true) readB(flag[A]==false)
56
Cycle!
57
Deadlock Freedom
LockOne Fails deadlock-freedom
Concurrent execution can deadlock
flag[i] = true; while (flag[j]){} flag[j] = true; while (flag[i]){}
Sequential executions OK
58
LockTwo
public class LockTwo implements Lock { private int victim; public void lock() { victim = i; while (victim == i) {}; } public void unlock() {} }
59
LockTwo
public class LockTwo implements Lock { private int victim; Let other public void lock() { first victim = i; while (victim == i) {}; } public void unlock() {} }
go
60
LockTwo
public class LockTwo implements Lock { Wait for private int victim; public void lock() { permission victim = i; while (victim == i) {}; } public void unlock() {} }
61
LockTwo
public class Lock2 implements Lock { private int victim; public void lock() { Nothing victim = i; while (victim == i) {}; } public void unlock() {} }
to do
62
LockTwo Claims
Satisfies mutual exclusion
If thread i in CS public void LockTwo() { Then victim == j victim = i; while (victim == i) {}; Cannot be both 0 and 1
63
Petersons Algorithm
public void lock() { flag[i] = true; victim = i; while (flag[j] && victim == i) {}; } public void unlock() { flag[i] = false; }
64
Petersons Algorithm
public void lock() { flag[i] = true; victim = i; while (flag[j] && victim == i) {}; } public void unlock() { flag[i] = false; }
Announce Im interested
65
Petersons Algorithm
public void lock() { flag[i] = true; Defer to other victim = i; while (flag[j] && victim == i) {}; } public void unlock() { flag[i] = false; }
Announce Im interested
66
Petersons Algorithm
public void lock() { flag[i] = true; Defer to other victim = i; while (flag[j] && victim == i) {}; } public void unlock() { Wait while other flag[i] = false; interested & Im }
Announce Im interested
the victim
67
Petersons Algorithm
public void lock() { flag[i] = true; Defer to other victim = i; while (flag[j] && victim == i) {}; } public void unlock() { Wait while other flag[i] = false; interested & Im }
Announce Im interested
No longer interested
the victim
68
Mutual Exclusion
(1) writeB(Flag[B]=true)writeB(victim=B)
public void lock() { flag[i] = true; victim = i; while (flag[j] && victim == i) {}; }
70
Assumption
(3) writeB(victim=B)writeA(victim=A) W.L.O.G. assume A is the last thread to write victim
71
Combining Observations
(1) writeB(flag[B]=true)writeB(victim=B) (3) writeB(victim=B)writeA(victim=A) (2) writeA(victim=A)readA(flag[B]) readA(victim)
Thus, A read flag[B] == true and victim == A, so it could not have entered the CS QED
Art of Multiprocessor Programming 72
Deadlock Free
public void lock() { while (flag[j] && victim == i) {};
Thread blocked
Solo: others flag is false Both: one or the other not the victim
Art of Multiprocessor Programming 73
only at while loop only if others flag is true only if it is the victim
Starvation Free
Thread i blocked only if j repeatedly re-enters so that flag[j] == true and
When
victim == i j
public void lock() { flag[i] = true; victim = i; while (flag[j] && victim == i) {}; } public void unlock() { flag[i] = false; }
re-enters
74
Filter
class Filter implements Lock { int[] level; // level[i] for thread i int[] victim; // victim[L] for level L 2 public Filter(int n) { n-1 0 level = new int[n]; level 0 0 4 0 0 0 0 0 victim = new int[n]; 1 for (int i = 1; i < n; i++) { level[i] = 0; }} 2 4
Thread 2 at level 4
Art of Multiprocessor Programming
n-1 victim
76
Filter
class Filter implements Lock { public void lock(){ for (int L = 1; L < n; L++) { level[i] = L; victim[L] = i; while (($ k != i level[k] >= L) && victim[L] == i ) {}; }} public void unlock() { level[i] = 0; }}
Art of Multiprocessor Programming 77
Filter
class Filter implements Lock { public void lock() { for (int L = 1; L < n; L++) { level[i] = L; victim[L] = i; while (($ k != i) level[k] >= L) && victim[L] == i) {}; }} public void release(int i) { level[i] = 0; }}
Art of Multiprocessor Programming
Filter
class Filter implements Lock { public void lock() { for (int L = 1; L < n; L++) { level[i] = L; victim[L] = i; while (($ k != i) level[k] >= L) && victim[L] == i) {}; // busy wait }} public void release(int i) { level[i] = 0; }}
Art of Multiprocessor Programming
Filter
class Filter implements Lock { int level[n]; int victim[n]; public void lock() { for (int L = 1; L < n; L++) { level[i] = L; victim[L] = i; while (($ k != i) level[k] >= L) && victim[L] == i) {}; }} public void release(int i) { level[i] = 0; }}
Art of Multiprocessor Programming
Filter
class Filter implements Lock { Wait as long as someone else is at int level[n]; int victim[n]; higher level, and Im designated public void lock() { for (int L = 1; L < n; L++) { level[i] = L; victim[L] = i;
same or victim
while (($ k != i) level[k] >= L) && victim[L] == i) {}; }} public void release(int i) { level[i] = 0; }}
Art of Multiprocessor Programming 81
Filter
class Filter implements Lock { int level[n]; int victim[n]; public void lock() { for (int L = 1; L < n; L++) { level[i] = L; victim[L] = i; while (($ k != i) level[k] >= L) && victim[L] == i) {}; }} public void release(int i) { Thread enters level L when level[i] = 0; the loop }}
Art of Multiprocessor Programming
it completes
82
Claim
Start at level L=0 At most n-L threads enter level L Mutual exclusion at level L=n-1
ncs L=0 L=1
L=n-2 cs L=n-1
Art of Multiprocessor Programming 83
Induction Hypothesis
No more than n-(L-1) at level L-1 Induction step: by contradiction Assume all at level L-1 enter level L A last to write victim[L] B is any other thread at level L
ncs
assume
L-1 has n-(L-1) L has n-L
cs
prove
84
Proof Structure
ncs
A
Last to write victim[L]
cs
Show that A must have seen B in level[L] and since victim[L] == A could not have entered
Art of Multiprocessor Programming 85
By Assumption
(3) writeB(victim[L]=B)writeA(victim[L]=A) By assumption, A is the last thread to write victim[L]
88
Combining Observations
(1) writeB(level[B]=L)writeB(victim[L]=B) (2) writeA(victim[L]=A)readA(level[B]) readA(victim[L]) (3) writeB(victim[L]=B)writeA(victim[L]=A)
89
Combining Observations
(1) writeB(level[B]=L)writeB(victim[L]=B) (2) writeA(victim[L]=A)readA(level[B]) readA(victim[L]) (3) writeB(victim[L]=B)writeA(victim[L]=A)
90
Combining Observations
(1) writeB(level[B]=L)writeB(victim[L]=B) (2) writeA(victim[L]=A)readA(level[B]) readA(victim[L])
Thus, A read level[B] L, and victim[L] = A, so it could not have entered level L!
Art of Multiprocessor Programming 91
(3) writeB(victim[L]=B)writeA(victim[L]=A)
No Starvation
Filter Lock satisfies properties:
Just like Peterson Alg at any level So no one starves
92
Bounded Waiting
Want stronger fairness guarantees Thread not overtaken too much If A starts before B, then A enters before B? But what does start mean? Need to adjust definitions .
Art of Multiprocessor Programming 93
Bounded Waiting
Divide lock() method into 2 parts:
Doorway interval:
Written DA always finishes in finite steps
Waiting interval:
Written WA may take unbounded steps
94
First-Come-First-Served
For threads A and B:
If DAk DB j
As k-th doorway precedes Bs j-th doorway As k-th critical section precedes Bs j-th critical section B cannot overtake A
95
Fairness Again
Filter Lock satisfies properties:
No one starves But very weak fairness Can be overtaken arbitrary # of times Thats pretty lame
96
Bakery Algorithm
Provides First-Come-First-Served How?
Take a number Wait until lower numbers have been served
Lexicographic order
(a,i) > (b,j)
Bakery Algorithm
class Bakery implements Lock { boolean[] flag; Label[] label; public Bakery (int n) { flag = new boolean[n]; label = new Label[n]; for (int i = 0; i < n; i++) { flag[i] = false; label[i] = 0; } }
98
Bakery Algorithm
class Bakery implements Lock { boolean[] flag; 6 Label[] label; 2 0 n-1 public Bakery (int n) { flag = new boolean[n]; f f t f f t f f label = new Label[n]; 0 0 4 0 0 5 0 0 for (int i = 0; i < n; i++) { flag[i] = false; label[i] = 0; } }
CS
99
Bakery Algorithm
class Bakery public void flag[i] = label[i] = implements Lock { lock() { true; max(label[0], ,label[n-1])+1;
100
Bakery Algorithm
class Bakery public void flag[i] = label[i] = implements Lock {
Doorway
101
Bakery Algorithm
class Bakery public void flag[i] = label[i] = implements Lock {
Im interested
102
Bakery Algorithm
class Bakery public void flag[i] = label[i] = implements Lock {
103
Bakery Algorithm
class Bakery public void flag[i] = label[i] = implements Lock { lock() { true; max(label[0], ,label[n-1])+1;
Someone is interested
104
Bakery Algorithm
class Bakery implements Lock { boolean flag[n]; Someone int label[n]; public void lock() { flag[i] = true; label[i] = max(label[0], ,label[n-1])+1; while ($k flag[k] && (label[i],i) > (label[k],k)); }
is interested
Bakery Algorithm
class Bakery implements Lock {
public void unlock() { flag[i] = false; } }
106
Bakery Algorithm
class Bakery implements Lock {
public void unlock() { flag[i] = false; } }
No longer interested
107
No Deadlock
There is always one thread with earliest label Ties are impossible (why?)
108
First-Come-First-Served
If DA DBthen As label is smaller And:
writeA(label[A]) readB(label[A]) writeB(label[B]) readB(flag[A])
class Bakery implements Lock { public void lock() { flag[i] = true; label[i] = max(label[0], ,label[n-1])+1; while ($k flag[k] && (label[i],i) > (label[k],k)); }
109
Mutual Exclusion
Suppose A and B in CS together Suppose A has earlier label When B entered, it must have seen
flag[A] is false, or label[A] > label[B]
class Bakery implements Lock { public void lock() { flag[i] = true; label[i] = max(label[0], ,label[n-1])+1; while ($k flag[k] && (label[i],i) > (label[k],k)); }
110
Mutual Exclusion
Labels are strictly increasing so B must have seen flag[A] == false
111
Mutual Exclusion
Labels are strictly increasing so B must have seen flag[A] == false LabelingB readB(flag[A]) writeA(flag[A]) LabelingA
112
Mutual Exclusion
Labels are strictly increasing so B must have seen flag[A] == false LabelingB readB(flag[A]) writeA(flag[A]) LabelingA Which contradicts the assumption that A has an earlier label
Art of Multiprocessor Programming
113
114
115
No
Maybe
Timestamps
Label variable is really a timestamp Need ability to
Read others timestamps Compare them Generate a later timestamp
117
118
is hard
119
Instead
We construct a Sequential timestamping system
As if we use mutex to read & write atomically No good for building locks
But useful anyway
Art of Multiprocessor Programming 120
Precedence Graphs
123
takes 0
takes 1
takes 2
124
125
126
127
128
129
130
Graph Composition
0
2 0
T3=T2*T2
1 0
133
20 < 02 < 12
1 0
134
1 0
and so on
Art of Multiprocessor Programming 135
In General
Tk = T2 * Tk-1
K threads need 3k nodes label size = Log2(3k) = 2k
136
Shared Memory
Shared read/write memory locations called Registers (historical reasons) Come in different flavors
Multi-Reader-Single-Writer (Flag[]) Multi-Reader-Multi-Writer (Victim[]) Not that interesting: SRMW and SRSW
138
Theorem
At least N MRSW (multireader/single-writer) registers are needed to solve deadlock-free mutual exclusion. N registers like Flag[]
139
C
write
one does, show a bad execution that CS violates properties: in our case assume an alg for deadlock free mutual exclusion using < N registers
Art of Multiprocessor Programming 140
C
write
CS
CS
CS
Upper Bound
Bakery algorithm
Uses 2N MRSW registers
142
144
Write(R)
R
CS
CS
Threads run, reading and writing R Deadlock free so at least one gets in
Art of Multiprocessor Programming 145
Write(R)
In any protocol B has to write to the register before entering CS, so stop it just before
Art of Multiprocessor Programming 146
Write(R)
CS
Write(R)
CS
CS
Art of Multiprocessor Programming
Theorem
Deadlock-free mutual exclusion for 3 threads requires at least 3 multireader multi-writer registers
149
Write(RB)
Write(RC)
Only 2 registers
Art of Multiprocessor Programming 150
Run A Solo
A B C
Write(RB)
Write(RC)
Obliterate Traces of A
A B C
Write(RB)
Write(RC)
CS
Write(RB)
Write(RC)
CS
CS
Proof Strategy
Proved: a contradiction starting from a covering state for 2 registers Claim: a covering state for 2 registers is reachable from any state where CS is empty
154
Write(RA)
Write(RB)
If we run B through CS 3 times, B must return twice to cover some register, say RB
Art of Multiprocessor Programming 155
Write(RA)
Write(RB)
Start with B covering register RB for the 1st time Run A until it is about to write to uncovered RA Are we done?
Art of Multiprocessor Programming 156
Write(RA)
Write(RB)
Write(RA)
Write(RB)
Run B obliterating traces of A in RB Run B again until it is about to write to RB Now we are done
Art of Multiprocessor Programming 158
Write(RA)
Write(RB)
Write(RC)
Where k threads not in CS cover k distinct registers Proof follows when k = N-1
Art of Multiprocessor Programming 159
Summary of Lecture
In the 1960s several incorrect solutions to starvation-free mutual exclusion using RW-registers were published Today we know how to solve FIFO N thread mutual exclusion using 2N RW-Registers
Art of Multiprocessor Programming 160
Summary of Lecture
N RW-Registers inefficient
Because writes cover older writes
161
162