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).