A Tale of Two Pups
Safety Goal:
Liveness Goal:
When Finn needs to go out:
Raise flag
While Scott’s flag is raised, wait
Let Finn out
When Finn comes in, lower flag
When Ru needs to go out:
Raise flag
While Will’s flag is raised:
lower flag
wait until Will’s flag is lowered
raise flag
When Scott’s flag is up and Will’s is down, release Ru
When Ru returns, lower flag
Before letting a dog out, both Scott and Will do:
Claim. If a dog wants to go out, eventually some dog will.
Why?
Is the protocol fair?
Safety Goal:
Liveness Goals:
Two processes (threads) want to access a shared resource
Counter
objectAssume:
0
and 1
ThreadID.get()
returns the ID of the thread calling the methodboolean[] flag
flag[i] == true
if process i
wants to use resourceint victim
victim == i
if process i
is willing to wait (like Ru)Similar to asymmetric protocol with Finn and Ru, but can switch roles.
To obtain lock:
flag
to true
victim
false
, orvictim
To release lock:
flag
to false
lock
Pseudocode public void lock() {
int i = ThreadID.get(); // get my ID, 0 or 1
int j = 1 - i; // other thread's ID
flag[i] = true; // set my flag
victim = i; // set myself to be victim
while (flag[j] && victim == i) {
// wait
}
}
unlock
Pseudocode public void unlock() {
int i = ThreadID.get();
flag[i] = false;
}
Mutual Exclusion. If both threads concurrently call lock()
, then both cannot return until other calls unlock()
.
Starvation Freedom. If thread i
calls lock()
then eventually thread i
returns.
Question. Why does Peterson lock achieve these properties?
Suppose not…
lock()
unlock()
In this case we say both threads enter critical section
Atomic operations:
flag[A] = true
victim = A
flag[B]
victim
flag[B] = true
victim = B
flag[A]
victim
Suppose $(B.2) \to (A.2)$:
i.e., $A $ wrote to victim
last
if not, continue argument with roles of $A $ and $B $ reversed
The Peterson lock satisfies mutual exclusion!
Claim. If thread $A$ calls lock()
, eventually the method will return.
public void lock() {
int i = ThreadID.get(); // get my ID, 0 or 1
int j = 1 - i; // other thread's ID
flag[i] = true; // set my flag
victim = i; // set myself to be victim
while (flag[j] && victim == i) { /*wait*/ }
}
Case 1. $A$ reads flag[B] == false
or victim == B
.
Claim. If thread $A$ calls lock()
, eventually the method will return.
public void lock() {
int i = ThreadID.get(); // get my ID, 0 or 1
int j = 1 - i; // other thread's ID
flag[i] = true; // set my flag
victim = i; // set myself to be victim
while (flag[j] && victim == i) { /*wait*/ }
}
Case 2. $A$ reads flag[B] == true
and victim == A
.
Assumption. Once thread B
obtains lock, eventually B
calls unlock()
public void unlock() {
int i = ThreadID.get();
flag[i] = false;
}
What then happens to thread A
?
The Peterson lock satisfies starvation freedom!
Locks for more threads!