150 likes | 314 Views
Question 4. 4. You are asked to write a simple barrier synchronization monitor, which has only one simple method: monitor Simple_Barrier ... operation Wait_for_all_n () ... end end
E N D
Question 4 4. You are asked to write a simple barrier synchronization monitor, which has only one simple method: monitor Simple_Barrier ... operation Wait_for_all_n() ... end end The semantics of Wait_for_all_n()is as follows: The calling process is put to wait “behind the barrier” if there are less than n-1 processes waiting before it. In the opposite case “the barrier is opened”, i.e. the process will signal all the waiting processes, and all n processes will continue and pass the “barrier”. (a) Write the missing pieces of code for Simple_Barrier and Wait_for_all_n() assuming the monitor has a “signal and wait “ (SW) semantics, and that there is no signal_all() type of mechanisms available, but the processes must be signaled one at a time. (b) Does your monitor behave correctly also when “signal and continue” (SC) semantics is used? • Assume that you have a Java-type condition mechanism, where there is only one shared queue per object where all the to incoming threads waiting for exclusive access and those released by notify() get their turn to access the monitor (E=W>S) • Write the code so that it works also under this assumption and potential new entries can not overtake the n-1 processes waiting already inside the monitor when the barrier is opened.
Answer 4 a) and b) a) monitor Simple_Barrier { int k =0; // number of processes behind barrier cond barrier: // condition variable to implement the wait public operation Wait_for_all_n() { k++; p1: if (k < n) then wait (barrier) k -- ; p2: if (k > 0) then signal (barrier); } } Because there is no signal_all() –type mechanism each process has to signal the next one behind the condition. The test (k>0) is not necessary, because the last signal() to an empty condition has no effect. So the following is ok also: public operation Wait_for_all_n() { k++; p1: if (k < n) then wait (barrier) else k=0; p2: signal (barrier) } b) This works for both SW- and SC- type monitor because signal() operation is the last operation monitor procedure. NOTE: wait condition: (p1 and ( k < n)) and signal condition: (p2 and ( k > 0))
Answer 4c) c) Class Simple_barrier { private k = 0; // number of processes waiting private h = 0; // the next heat waiting to have n members behind the barrier synchronized void Wait_for_all_n() { private ph; // record the heat number of the caller to a private variable ph ph = h; k++; if ( k < n) // if your heat isn’t full yet, start to wait then while (ph = h) { try {wait();} // wait as long as you belong to the next heat catch (interrupter execution ex} {return;} else { h++; // next heat (=your heat) is full (k =n) and can be released k = 0; // change the next heat and signal all processes processes to notifyAll() } // check out whether they belong to your heat, so that they can } } Notes: Because the condition mechanism in Java puts all new monitor entries to the same queue as the processes released from wait() we need the notion of “heat” to separate those n-1 processes who have been in the wait() at the moment of notifyAll() (for them ph = h-1) from those new ones who are just entering the monitor after it (ph=h).
4. One lane bridge (1/5) • Cars coming form north and south have to cross a river along a very long and narrow one-lane bridge. Cars driving to the same direction should be allowed to be on the bridge at the same time, while cars heading to opposite direction have to wait. Implement a monitor solution to the problem, where the cars are processes calling the cross_from_North() and cross_from_South() of the monitor one_lane_bridge. Solution must be symmetric. Specify the monitor invariant. • a) Use the signal wait (SW) semantics and/or signal and continue (SC) semantic. Do not worry about fairness. • b) Modify your solution to ensure fairness • c) Modify your solutions for a) and b) for Java.
One lane bridge (2/5) a) Generic monitor for both SC and SW semantics. Does not guarantee fairness.Invariants: “No crashes, i.e. safety”: { ns = 0 or sn = 0 } “No unnecessary waiting”{ ( !empty(ns_c) => sn > 0 ) and (!empty(sn_c) => ns > 0) } monitor One_lane_bridge { int ns =0; //north-south cars on the bridge intsn =0; //south-north cars on the brigde condns_c: //condition to enter from north condsn_c; //condition to enter from south private procedure startNorth() { if (sn > 0) wait(ns_c); ns++ } private procedure endSouth() { ns--; if (ns == 0) signal_all(sn_c)); //signals possible waiting sn cars } public cross_from_North_to_Shouth() { //to provide a simpler API for cars startNorth(); // cross the bridge endSouth(); } … // the south-north direction is symmetric
One lane bridge (3/5) b) Simple general monitor with fairness. For the sake of argument let’s assume the current traffic is from north to south (ns >0). Stop it if there are cars waiting for the opposite direction !empty(sn_c). Once the last car to ns -direction exits and the bridge becomes empty (ns == 0), all cars waiting for the to opposite sn -direction are released with a signal_all(sn_c)and they will get in directly, whereas all new entrants to sn will get stuck if any new entrants for ns have entered their queue already in the mean time !empty(ns_c) . So in this congested case the next ‘lots’ of cars to enter the bridge concurrently are building up on its both ends until the current lot has passed it and the direction is changed. Why can this solution not deadlock ? Provide a proper invaraint this. NOTE: This monitor works only when the monitor gives priority to released waiters (W) over new entrants(E), by providing separate queues for them. However, this is a standard assumption with monitors providing independent queues for each condition variable declared by the application.
One lane bridge (3/5) monitorOne_lane_bridge { int ns =0; //north-south cars on the bridge intsn =0; //south-north cars on the bridge condns_c: //condition to enter from north condsn_c; //condition to enter from south private procedure startNorth() { if (sn > 0) or !empty(sn_c) wait; ns++; } private procedure endSouth() { ns--; if (ns == 0) signal_all(sn_c)); //signals all possible waiting sn cars } publiccross_from_North() { startNorth(); // north-south crossing operation is embedded here endSouth(); } … // the south-north direction is symmetric
One lane bridge (4/5) C) Java solution without fairness class One_lane_bridge { private int ns =0 //north-south cars on the bridge private intsn =0 //south-north cars on the brigde private synchronized void startNorth() { p1: while (sn > 0){ //A while-statement must be used although sn=0 by try {wait()}; //the notification (***, because a new startSouth() catch (InterruptedExecution ex) //maybe scheduled in between. New entries (=E) may intervene {return;} //with the released (=W) in W=E queue of a Java object. }; //sn=0 p2: ns++; } private synchronized void endSouth() { ns--; p3: if (ns == 0) notifyAll(); //signals possible waiting sn cars, ns=0 (*** } … } } b) 1.Safety, i.e. no crashes: {ns=0 or sn=0}. True intially and preserved by the monitor, because when ns is increased at p2 in startNorth(), sn=0. 2.No unnecessary waiting: (!empty() => (sn>0 or ns>0)). When !empty() becomes true, also sn>0 holds at p1. When ns>0 or sn>0 becomes false at p3, also the !empty() becomes false (queue gets empty) by notifyAll().
Yet another simple solution to CS Problem:Peterson’s algorithm • Safety proof strategy: Focus on states where the CS property is about to be violated, i.e. p or q is in the CS, and the other tries to get in also: • Step 1) Prove mutual exclusion by first proving the invariants: • Mq: p4q5 => (wantq and last=1) • Mp: p5q4 => (wantp and last=2) • Step 2) Prove the invariance of: not(p5q5) with the help of Mp and Mq.
Alternative way to prove fairness of Dekker’s algorithm Process p can get stuck to wait in two loops: 1. Assume p gets stuck at p6: p6 which implies also C { (wantp and turn = 2)}. • wantqmust have been true in order to p get at p6. • So q has passed q2, but it has not reach q10 to set turn = 1, so q3..q9 holds • Because of the progress assumption and C nothing hinders q to get through CS to q10..q1 which falsifying C and contradicting the assumption. So the assumption is false. • Assume p get’s stuck at p3..p4: (wantp and wantq and turn =1). • Then q must be at q8..q10 or at q3..q5 and in both cases q must proceed to the state (wantp and wantq and turn =1) contradicting the assumption.
Yet another simple solution to CS Problem:Peterson’s algorithm • Safety proof strategy: Focus on states where the CS property is about to be violated, i.e. p or q is in the CS, and the other tries to get in also: • Step 1) Prove mutual exclusion by first proving the invariants: • Mq: p4q5 => (wantq and last=1) • Mp: p5q4 => (wantp and last=2) • Step 2) Prove the invariance of: not(p5q5) with the help of Mp and Mq.
Example of a Safety: Mutual exclusion property of Peterson’s Algorithm Step 1) Proof of Mp ( Mq is symmetric) is about preserving an implication: 1.1 Mp: (p4q5) => (wantq last =1) is initially true, because its precedence (p4q5) is false. 1.2 The following local invariants hold trivially for p and q: 1.3 The precedence of Mp, (p4q5) may become true in two ways. We must show that for both of them the consequent (wantq last =1) holds. 1.4 The consequent (wantq last =1) can get false only when q1 or q4 gets true, implying that the precedent of Mp, (p4q5) is false also. Thus Mp is invariant. End of proof. Step 2) The mutual exclusion invariance ME: ¬(p5q5) is trivially true initially. 2.1 Concentrate on states from which ME could be violated: (p4q5) and (p5q4). 2.2 What else do we know about them? From step 1): Mp: (p4q5) => (wantq last =1) Mq: (p5q4) => (wantp last =2). 2.3 With the help of Mp we may prove that ME is preserved in (p4q5) using contradiction: • Assume p4 is to be executed in the state (p4q5) - (Case for (p5q4) is symmetric). • p4: “await wantq = false or last =2” => (¬wantq v last =2) • But: Mp and (p4q5) => ( wantq last =1) • 2) and 3) => Contradiction. Statement p4 can not be terminated. End of proof. P: p3..p6 => wantp Q: q3..q6 => wantq. Executing q4 when p4 holds: => (wantp = false v last =1) P p4 => wantp 2. wantp => last =1 4. Q q5 => (wantq last =1). • Executing p3 when q5 holds: • => last =1 • Q q5 => wantq • 2.3. => (wantq last =1).
Liveness proof for Peterson’s algorithm • Assuming that p starves at p4, what does it mean? Whenever p happens to evaluate the waiting conditions at p4, they turn out to be false, i.e. formally: p4 => (wantq) (last≠2) Observe that the conditions in p4 are tested by separate atomic actions, i.e. they need not hold at the same time. • What do we know locally about q? Either it stays indefinitely in its non-critical section at q1, i.e. (¬ wantq) holds, or eventually it tries to get in CS and executes “q3: last 2”: (¬wantq) v ( last =2) 3. If p starves at p4 and eventually (last =2), it won’t get changed because p3 is never executed p4 (last=2) => (last=2). 4 (2.) and (3.) => p4 => ((¬wantq) v (last=2)) (¬wantq v ¬ (last≠2)) *) • 1 and 4 : p4 => ( (wantq) (last≠2)) (¬wantq v ¬ (last≠2)) = False. • Assumption p4 must be false. End of proof. -------------------------- *) Check out the rule: (A) ¬ (¬A) .