Show how this works using the causality diagram Click for diagram in postscript or html.
---------------- Shared Memory Coordination ----------------
See chapter 2 of tannenbaum
We will be looking at processes coordination using SHARED MEMORY and
BUSY WAITING.
So we don't send messages but read and write shared variables.
When we need to wait, we loop and don't context switch
Can be wasteful of resourses if must wait a long time.
Context switching primitives normally use busy waiting in
their implementation.
Mutual Exclusion
Consider adding one to a shared variable V.
When compiled onto many machines get three instructions
load r1 <-- V
add r1 <-- r1+1
store r1 --> V
Assume V is initially 10 and one process begins the 3 inst seq
after the first instruction context to another process
registers are of course saved
new process does all three instructions
context switch back
registers are of course restored
first process finishes
V has been incremented twice but has only reached 11
Problem is the 3 inst seq must be atomic, i.e. cannot be
interleaved with another execution of these instructions
That is one execution excludes the possibility of another. So the
must exclude each other, i.e. mutual exclusion.
This was a RACE CONDITION.
Hard bugs to find since non-deterministic.
Can be more than two processes
The portion of code that requires mutual exclusion is often called
a CRITICAL SECTION.
HOMEWORK 2.2 2.3 2.4
One approach is to prevent context switching
Do this for the kernel of a uniprocessor
Mask interrupts
Not feasible for user mode processes
Not feasible for multiprocessors
CRITICAL SECTION PROBLEM is to implement
loop
trying-part
critical-section
releasing-part
non-critical section
So that when many processes execute this you never have more than one
in the critical section
That is you must write trying-part and releasing-part
Trivial solution.
Let releasing part be simply "halt"
This shows we need to specify the problem better
Additional requirement
Assume that if a process begins execution of its critical section
and no other process enters the critical section, then the first
process will eventually exit the critical section
Then the requirement is "If a process is executing its
trying part, then SOME process will eventually enter the critical
section".
---------------- Software-only solutions to CS problem ----------------
We assume the existence of atomic loads and stores
Only upto wordlength
We start with the case of two processes
Easy if want tasks to alternate in CS and you know which one goes
first in CS
Shared int turn = 1
loop loop
while (turn=2) --EMPTY BODY!! while (turn=1)
CS CS
turn=2 turn=1
NCS NCS
But always alternating does not satisfy the additional requirement
above. Let NCS for process 1 be an infinite loop (or a halt).
Will get to a point when process 1 is in its trying part but
turn=2 and turn will not change. So some process enters its
trying part but neither proc will enter the CS.
Some attempts at a general soln follow
First idea. The trouble was the silly turn
Shared bool P1wants=false, P2wants=false
loop loop
P1wants <-- true P2wants <-- true
while (P2wants) while (P1wants)
CS CS
P1wants <-- false P2wants <-- false
NCS NCS
This fails. Why? This kind of question is very important and
makes a good exam question.
Next idea. It is easy to fix the above
Shared bool P1wants=false, P2wants=false
loop loop
while (P2wants) while (P1wants)
P1wants <-- true P2wants <-- true
CS CS
P1wants <-- false P2wants <-- false
NCS NCS
This also fails. Why?? Show on board what a scenario looks like.
Next idea need a second test.
Shared bool P1wants=false, P2wants=false
loop loop
while (P2wants) while (P1wants)
P1wants <-- true P2wants <-- true
while (P2wants) while (P1wants)
CS CS
P1wants <-- false P2wants <-- false
NCS NCS
Guess what, it fails again.
The first one that worked was discovered by a mathematician named
Dekker. Use turn only to resolve disputes.
Shared bool P1wants=false, P2wants=false
Shared int turn=1
loop loop
P1wants <-- true P2wants <-- true
while (P2wants) while (P1wants)
if turn=2 if turn=1
P1wants <-- false P2wants <-- false
while (turn==2) while (turn=1)
P1wants <-- true P2wants <-- true
CS CS
turn <-- 2 turn <-- 1
P1wants <-- false P2wants <-- false
NCS NCS
First two lines look like deadlock country. But it is not an
empty while loop.
The winner-to-be just loops waiting for the loser to give up
and then goes into the CS.
The loser-to-be
Gives up
Waits to see that the winner has finished
Starts over (knowing it will win)
Dijkstra extended dekker's soln for > 2 processes
Others improved the fairness of dijkstra's algorithm
These complicated methods remained the simplest known until 1981
when Peterson found a much simpler method
Keep dekker's idea of using turn only to resolve disputes, but
drop the complicated then body of the if.
Shared bool P1wants=false, P2wants=false
Shared int turn=1
loop loop
P1wants <-- true P2wants <-- true
while (P2wants and turn=2) while (P1wants and turn=1)
CS CS
turn <-- 2 turn <-- 1
P1wants <-- false P2wants <-- false
NCS NCS
This might be correct!
The standard solution from peterson just has the assignment to
turn moved from the trying to the releasing part
Shared bool P1wants=false, P2wants=false
Shared int turn=1
loop loop
P1wants <-- true P2wants <-- true
turn <-- 2 turn <-- 1
while (P2wants and turn=2) while (P1wants and turn=1)
CS CS
P1wants <-- false P2wants <-- false
NCS NCS
Remarks
Peterson's paper is in Inf. Proc. Lett. 12 #3 (1981) pp
115-116.
A fairly simple proof of its correctness is by Hofri in
OS Review Jan 1980 pp 18-22. I will put a copy in the courant
library in the box for this course.
Peterson actually showed the result for n processes (not just
2).
Hofri's proof also shows that the algorithm satisfies a strong
fairness condition, namely LINEAR WAITING.
Assuming a process continually executes, other cannot pass
it (see hofri for details).