Mutual Exclusion
Companion slides for
The Art of Multiprocessor Programming
by Maurice Herlihy & Nir Shavit
Mutual Exclusion
Art of Multiprocessor Programming
2
Why is Concurrent Programming so Hard?
Art of Multiprocessor Programming
3
Events
Art of Multiprocessor Programming
4
time
a0
Threads
Art of Multiprocessor Programming
5
time
a0
a1
a2
…
Example Thread Events
Art of Multiprocessor Programming
6
Threads are State Machines
Art of Multiprocessor Programming
7
Events are transitions
a0
a1
a2
a3
States
Art of Multiprocessor Programming
8
Concurrency
Art of Multiprocessor Programming
9
time
time
Interleavings
Art of Multiprocessor Programming
10
time
Intervals
Art of Multiprocessor Programming
11
time
a0
a1
A0
Intervals may Overlap
Art of Multiprocessor Programming
12
time
a0
a1
A0
b0
b1
B0
Intervals may be Disjoint
Art of Multiprocessor Programming
13
time
a0
a1
A0
b0
b1
B0
Precedence
Interval A0 precedes interval B0
Art of Multiprocessor Programming
14
time
a0
a1
A0
b0
b1
B0
A0 🡺 B0
Precedence
Art of Multiprocessor Programming
15
Precedence Ordering
Art of Multiprocessor Programming
16
Partial Orders�(review)
Art of Multiprocessor Programming
17
Total Orders�(review)
Art of Multiprocessor Programming
18
Repeated Events
while (mumble) {
a0; a1;
}
Art of Multiprocessor Programming
19
a0k
k-th occurrence of event a0
A0k
k-th occurrence of interval A0 =(a0,a1)
Implementing a Counter
Art of Multiprocessor Programming
20
public class Counter {
private long value;
public long getAndIncrement() {
temp = value;
value = temp + 1;
return temp;
}
}
Make these steps indivisible using locks
Locks (Mutual Exclusion)
Art of Multiprocessor Programming
21
public interface Lock {
public void lock();
public void unlock();
}
Locks (Mutual Exclusion)
Art of Multiprocessor Programming
22
public interface Lock {
public void lock();
public void unlock();
}
acquire lock
Locks (Mutual Exclusion)
Art of Multiprocessor Programming
23
public interface Lock {
public void lock();
public void unlock();
}
release lock
acquire lock
Using Locks
Art of Multiprocessor Programming
24
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;
}}
Using Locks
Art of Multiprocessor Programming
25
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;
}}
acquire Lock
Using Locks
Art of Multiprocessor Programming
26
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;
}}
Release lock
(no matter what)
Using Locks
Art of Multiprocessor Programming
27
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;
}}
Critical section
Mutual Exclusion
Art of Multiprocessor Programming
28
CSik 🡺 CSjm
CSjm 🡺 CSik
Deadlock-Free
Art of Multiprocessor Programming
29
Starvation-Free
Art of Multiprocessor Programming
30
Two-Thread vs n -Thread Solutions
Art of Multiprocessor Programming
31
The ThreadID class
Art of Multiprocessor Programming
32
public class ThreadID {
static final AtomicInteger idSeq =
new AtomicInteger(0);
static final ThreadLocal<Integer> idStore =
new ThreadLocal<>() {
@Override
protected Integer initialValue() {
return idSeq.getAndIncrement();
}
};
public static int get() {
return idStore.get();
}
}
Two-Thread Conventions
Art of Multiprocessor Programming
33
class … implements Lock {
…
// thread-local index, 0 or 1
public void lock() {
int i = ThreadID.get();
int j = 1 - i;
…
}
}
Two-Thread Conventions
Art of Multiprocessor Programming
34
class … implements Lock {
…
// thread-local index, 0 or 1
public void lock() {
int i = ThreadID.get();
int j = 1 - i;
…
}
}
Henceforth: i is current thread, j is other thread
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]) {}
}
Wait for other flag to become false
LockOne Satisfies Mutual Exclusion
((flag[i]==true && flag[j]==false)
&& (flag[j]==true && flag[i]==false))==true
Art of Multiprocessor Programming
39
Deadlock Freedom
Art of Multiprocessor Programming
40
flag[i] = true; flag[j] = true;
while (flag[j]){} while (flag[i]){}
LockTwo
Art of Multiprocessor Programming
41
public class LockTwo implements Lock {
private int victim;
public void lock() {
victim = i;
while (victim == i) {};
}
public void unlock() {}
}
LockTwo
Art of Multiprocessor Programming
42
public class LockTwo implements Lock {
private int victim;
public void lock() {
victim = i;
while (victim == i) {};
}
public void unlock() {}
}
Let other go first, a “can protocol”
LockTwo
Art of Multiprocessor Programming
43
public class LockTwo implements Lock {
private int victim;
public void lock() {
victim = i;
while (victim == i) {};
}
public void unlock() {}
}
Wait for permission
LockTwo
Art of Multiprocessor Programming
44
public class Lock2 implements Lock {
private int victim;
public void lock() {
victim = i;
while (victim == i) {};
}
public void unlock() {}
}
Nothing to do
LockTwo Claims
Art of Multiprocessor Programming
45
public void LockTwo() {
victim = i;
while (victim == i) {};
}
Peterson’s Algorithm
Art of Multiprocessor Programming
46
public void lock() {
flag[i] = true;
victim = i;
while (flag[j] && victim == i) {};
}
public void unlock() {
flag[i] = false;
}
Peterson’s Algorithm
Art of Multiprocessor Programming
47
public void lock() {
flag[i] = true;
victim = i;
while (flag[j] && victim == i) {};
}
public void unlock() {
flag[i] = false;
}
Announce I’m interested
Peterson’s Algorithm
Art of Multiprocessor Programming
48
public void lock() {
flag[i] = true;
victim = i;
while (flag[j] && victim == i) {};
}
public void unlock() {
flag[i] = false;
}
Announce I’m interested
Defer to other
Peterson’s Algorithm
Art of Multiprocessor Programming
49
public void lock() {
flag[i] = true;
victim = i;
while (flag[j] && victim == i) {};
}
public void unlock() {
flag[i] = false;
}
Announce I’m interested
Defer to other
Wait while other interested & I’m the victim
Peterson’s Algorithm
Art of Multiprocessor Programming
50
public void lock() {
flag[i] = true;
victim = i;
while (flag[j] && victim == i) {};
}
public void unlock() {
flag[i] = false;
}
No longer interested
Announce I’m interested
Defer to other
Wait while other interested & I’m the victim
Deadlock Free
Art of Multiprocessor Programming
51
public void lock() {
…
while (flag[j] && victim == i) {};
Starvation Free
Art of Multiprocessor Programming
52
public void lock() {
flag[i] = true;
victim = i;
while (flag[j] && victim == i) {};
}
public void unlock() {
flag[i] = false;
}
The Filter Algorithm for n Threads
There are n-1 “waiting rooms” called levels
many try
Art of Multiprocessor Programming
53
ncs
cs
Filter
Art of Multiprocessor Programming
54
class Filter implements Lock {
int[] level; // level[i] for thread i
int[] victim; // victim[L] for level L
public Filter(int n) {
int i = ThreadID.get();
level = new int[n];
victim = new int[n];
for (int i = 1; i < n; i++) {
level[i] = 0;
}}
…
}
level
victim
n-1
n-1
0
1
0
0
0
0
0
0
4
2
2
Thread 2 at level 4
0
4
Filter
Art of Multiprocessor Programming
55
class Filter implements Lock {
…
public void lock(){
int i = ThreadID.get();
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;
}}
Filter
Art of Multiprocessor Programming
56
class Filter implements Lock {
…
public void lock() {
int i = ThreadID.get();
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;
}}
One level at a time
Filter
Art of Multiprocessor Programming
57
class Filter implements Lock {
…
public void lock() {
int i = ThreadID.get();
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;
}}
Announce intention to enter level L
Filter
Art of Multiprocessor Programming
58
class Filter implements Lock {
int level[n];
int victim[n];
public void lock() {
int i=ThreadID.get();
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;
}}
Give priority to anyone but me
Filter
Art of Multiprocessor Programming
59
class Filter implements Lock {
int level[n];
int victim[n];
public void lock() {
int i = ThreadID.get();
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;
}}
Wait as long as someone else is at same or higher level, and I’m designated victim
Filter
Art of Multiprocessor Programming
60
class Filter implements Lock {
int level[n];
int victim[n];
public void lock() {
int i=ThreadID.get();
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;
}}
Thread enters level L when it completes the loop
Claim
Art of Multiprocessor Programming
61
ncs
cs
L=n-1
L=2
L=n-2
L=1
No Starvation
Art of Multiprocessor Programming
62
Bounded Waiting
Art of Multiprocessor Programming
63
Bounded Waiting
Art of Multiprocessor Programming
64
First-Come-First-Served
Art of Multiprocessor Programming
65
Fairness Again
Art of Multiprocessor Programming
66
Bakery Algorithm
Art of Multiprocessor Programming
67
Bakery Algorithm
Art of Multiprocessor Programming
68
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;
}
}
…
Bakery Algorithm
Art of Multiprocessor Programming
69
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;
}
}
…
n-1
0
f
f
f
f
t
f
t
2
f
0
0
0
0
5
0
4
0
6
CS
Bakery Algorithm
Art of Multiprocessor Programming
70
class Bakery implements Lock {
…
public void lock() {
flag[i] = true;
label[i] = max(label[0], …,label[n-1])+1;
while (∃k flag[k] && k != i
&& (label[i],i) > (label[k],k));
}
Bakery Algorithm
Art of Multiprocessor Programming
71
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));
}
Doorway
Bakery Algorithm
Art of Multiprocessor Programming
72
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));
}
I’m interested
Bakery Algorithm
Art of Multiprocessor Programming
73
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));
}
Take increasing label (read labels in some arbitrary order)
Bakery Algorithm
Art of Multiprocessor Programming
74
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));
}
Someone is interested
Bakery Algorithm
Art of Multiprocessor Programming
75
class Bakery implements Lock {
boolean flag[n];
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));
}
Someone is interested
With lower (label,i) in lexicographic order
Bakery Algorithm
Art of Multiprocessor Programming
76
class Bakery implements Lock {
…
public void unlock() {
flag[i] = false;
}
}
Bakery Algorithm
Art of Multiprocessor Programming
77
class Bakery implements Lock {
…
public void unlock() {
flag[i] = false;
}
}
No longer interested
labels are always increasing
No Deadlock
Art of Multiprocessor Programming
78
First-Come-First-Served
writeA(label[A]) 🡺 readB(label[A]) 🡺 writeB(label[B]) 🡺 readB(flag[A])
Art of Multiprocessor Programming
79
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));
}
Mutual Exclusion
(either impossible)
Art of Multiprocessor Programming
80
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));
}
Art of Multiprocessor Programming
81
�This work is licensed under a Creative Commons Attribution-ShareAlike 2.5 License.