Don't forget to go over 10.17 and 10.18 Hand out "Simulating Concurrency" notes for lab 1 These notes and lab 1, Process Coordination, are on the web ---------------- (binary) Semaphores ---------------- Trying and release often called ENTRY and EXIT, or WAIT and SIGNAL, or DOWN and UP, or P and V (the latter are from dutch words--dijkstra). Lets try to formalize the entry and exit parts. To get mutual exclusion we need to ensure that no more than one task can pass through P until a V has occurred. The idea is to keeptrying to walk through the gate and when you succeed ATOMICALLY closed the gate behind you so that no one else can enter. Definition (NOT an implementation) Let S be an enum with values closed and open (like a gate). P(S) is while S=closed S <-- closed The failed test and the assignment are a single atomic action. P(S) is label: {[ --begin atomic part if S=open S <-- closed else }] --end atomic part goto label V(S) is S <-- open Note that this P and V (NOT yet implemented) can be used to solve the critical section problem very easily The entry part is P(S) The exit part is V(S) Make very sure the "atomic part" is understood. Note that dekker and peterson do not give us a P and V since each process has a unique entry and a unique exit S is called a (binary) semaphore. To implement binary semaphores we need some help from our hardware friends. Boolean in out X TestAndSet(X) is oldx <-- X X <-- true return oldx Note that the name is a good one. This function tests the value of X and sets it (i.e. sets it true; reset is to set false) Boolean in out X, in e FetchAndOr(X, e) oldx <-- X X <-- X OR e return oldx TestAndSet(X) is the same as FetchAndOr(X, true) FetchAndOr is also a good name, X is fetched and OR'ed with e Why is it better to return the old than the new value? Now P/V for binary semaphores is trivial. S is Boolean variable (false is open, true is closed) P(S) is while (TestAndSet(S)) V(S) is S <-- false HOMEWORK 2.5 Just because P and V are now simple to implement does not mean that the implementation is trivial to understand. Go over the implementation of P(S) This works fine no matter how many processes are involved ---------------- Counting Semaphores ---------------- Now want to consider permitting a bounded number of processors into what might be called a SEMI-CRITICAL SECTION. loop P(S) SCS -- at most k processes can be here simultaneously V(S) NCS A semaphore S with this property is called a COUNTING SEMAPHORE. If k=1, get a binary semaphore so counting semaphore generalizes binary semaphore. How can we implement a counting semaphore given binary semaphores? S is a nonnegative integer Initialize S to k, the max number allowed in SCS Use k=1 to get binary semaphore (hence the name binary) We only ask for Limit of k in SCS (analogue of mutual exclusion) Progress: If process enters P and < k in SCS, a process will enter the SCS We do not ask for fairness, and don't assume it (for the binary semaphore) either. Idea: use bin sem to protect arith on S binary semaphore q P(S) is V(S) is start-over: P(q); S++; V(q) P(q) if S<=0 V(q) goto start-over else S-- V(q) Explain how this works. Let's make this a little more elegant (avoid goto) binary semaphore q P(S) is V(S) is loop P(q); S++; V(q) P(q) exit when S>0 -- will exit loop holding the lock V(q) S-- V(q) This so-call n 1/2 loop clearly shows what is going on. An alternative is to "prime" the loop. get-character -- priming read while not eof put-character get-character This is all very nice but the real trouble is that the above code is WRONG! Distinguish between DEADLOCK, LIVELOCK, STARVATION Deadlock: Cannot make progress Livelock: Might not make progress (race condition) Starvation: Some process does/might not make progress The counting semaphore livelocks (no progress) if the binary semaphore is unfair (starvation). Talk about letting people off subway when full and many waiting at the platform. New idea: Release q sema early and use two others to force alternation Remember that multiple Vs on a bin sem may not free multiple processes waiting on Ps. binary semaphore q,t initially open binary semaphore r initially closed integer NS; -- might be negative, keeps value of S P(S) is V(S) is P(q) P(q) NS-- NS++ if NS < 0 if NS <= 0 V(q) V(q) P(r) <-- these two lines --> P(t) V(t) <-- force alternation --> V(r) else else V(q) V(q) Explain how this works. Do some scenarios "factor out" V(q) and move above if P(S) is V(S) is P(q) P(q) NS-- NS++ V(q) V(q) if NS < 0 if NS <= 0 P(r) <-- these two lines --> P(t) V(t) <-- force alternation --> V(r) It fails!! You need to decrement/increment NS and test atomically. If you release the lock between testing and doing something, the information you found during the test is only a rummor during the something. Sneaky code optimization (applied to the correct code) gives P(S) is V(S) is P(q) P(q) NS-- NS++ if NS < 0 if NS <= 0 V(q) V(r) P(r) else V(q) V(q)