Implementing ¡Critical ¡Sections ¡in ¡Software ¡Hard ¡ The following example will demonstrate the difficulty of providing mutual exclusion with memory reads and writes Ø Hardware support is needed The code must work all of the time Thre read S d Synchro ronization: Ø Most concurrency bugs generate correct results for some Too M Much M Milk interleavings Designing mutual exclusion in software shows you how to think about concurrent updates Ø Always look for what you are checking and what you are updating Ø A meddlesome thread can execute between the check and the update, the dreaded race condition 1 2 Thre read C d Coordi rdination Formalizing ¡ “ Too ¡Much ¡Milk ” ¡ Too much milk! Shared variables Ø “ Look in the fridge for milk ” – check a variable Ø “ Put milk away ” – update a variable Jill Jack Safety property Look in the fridge; out of Ø At most one person buys milk milk Go to store Liveness Look in fridge; out of milk Buy milk Ø Someone buys milk when needed Go to store Arrive home; put milk away Buy milk How can we solve this problem? Arrive home; put milk away Oh, no! Fridge and milk are shared data structures 3 4 How ¡to ¡think ¡about ¡synchronization ¡code ¡ The ¡correctness ¡conditions ¡ Every thread has the same pattern Safety Ø Entry section: code to attempt entry to critical section Ø Only one thread in the critical region Ø Critical section: code that requires isolation (e.g., with mutual Liveness exclusion) Ø Some thread that enters the entry section eventually enters the Ø Exit section: cleanup code after execution of critical region critical region Ø Non-critical section: everything else Ø Even if some thread takes forever in non-critical region There can be multiple critical regions in a program Bounded waiting Ø Only critical regions that access the same resource (e.g., data Ø A thread that enters the entry section enters the critical section structure) need to synchronize with each other within some bounded number of operations. Failure atomicity while(1) { Ø It is OK for a thread to die in the critical region Entry section Ø Many techniques do not provide failure atomicity Critical section while(1) { Entry section Exit section Critical section Non-critical section Exit section } Non-critical section } 5 6
Too ¡Much ¡Milk: ¡Solution ¡#0 ¡ Too ¡Much ¡Milk: ¡Solution ¡#1 ¡ turn := Jill // Initialization while(1) { if (noMilk) { // check milk (Entry section) while(1) { while(1) { if (noNote) { // check if roommate is getting milk while(turn ≠ Jack) ; //spin while(turn ≠ Jill) ; //spin leave Note; //Critical section while (Milk) ; //spin while (Milk) ; //spin buy milk; buy milk; // Critical section buy milk; remove Note; // Exit section turn := Jill // Exit section turn := Jack } // Non-critical section // Non-critical section // Non-critical region } } } Is this solution Is this solution Ø 1. Correct Ø 1. Correct Ø 2. Not safe Ø 2. Not safe Ø 3. Not live Ø 3. Not live Ø 4. No bounded wait Ø 4. No bounded wait Ø 5. Not safe and not live What if we switch the Ø 5. Not safe and not live order of checks? It works sometime and doesn’t some other times At least it is safe Ø Threads can be context switched between checking and leaving note Ø Live, note left will be removed Ø Bounded wait (‘buy milk’ takes a finite number of steps) 7 8 Solution ¡#2 ¡(a.k.a. ¡Peterson ’ s ¡algorithm): ¡ ¡ Peters rson ’ s A Algori rithm combine ¡ideas ¡of ¡0 ¡and ¡1 ¡ in 0 = in 1 = false; Jack Jill Variables: while (1) { while (1) { Spin! Ø in i : thread T i is executing , or attempting to execute, in CS in 0 := true; in 1 := true; Ø turn : id of thread allowed to enter CS if multiple want to turn := Jill; turn := Jack; while (turn == Jill while (turn == Jack && in 1 ) ;//wait && in 0 );//wait Claim: We can achieve mutual exclusion if the following invariant holds Critical section Critical section before thread i enters the critical section: in 0 := false; in 1 := false; Non-critical section } Non-critical section } ((¬ in 0 ∨ ( in 0 ∧ turn = 1)) ∧ in 1 ) ∧ ((¬ in 1 ∨ ( in 1 ∧ turn = 0)) ∧ in 0 ) {(¬ in j ∨ ( in j ∧ turn = i )) ∧ in i } ⇒ turn=Jill, in 0 = true turn=Jack, in 0 = false, in 1 := true turn=Jack, in 0 = true, in 1 := true (( turn = 0) ∧ ( turn = 1)) = false Safe, live, and bounded waiting But, only 2 participants Intuitively: j doesn’t want to execute or it is i’s turn to execute 9 10 Too ¡Much ¡Milk: ¡Lessons ¡ Towards ¡a ¡solution ¡ The problem boils down to establishing the following right after Peterson ’ s works, but it is really unsatisfactory entry i (¬ in j ∨ ( in j ∧ turn = i )) ∧ in i = (¬ in j ∨ turn = i ) ∧ in i Ø Limited to two threads Ø Solution is complicated; proving correctness is tricky even for the simple example Or, intuitively, right after Jack enters: Ø While thread is waiting, it is consuming CPU time u Jack has signaled that he is in the entry section ( in i ) u - And - How can we do better? u Jill isn’t in the critical section or entry section (¬ in j ) Ø Use hardware to make synchronization faster u - Or – u Jill is also in the entry section but it is Jack’s turn ( in j ∧ turn = i ) Ø Define higher-level programming abstractions to simplify concurrent programming How can we do that? entry i = in i := true; while ( in j ∧ turn ≠ i ); 11 12
We h hit a a s snag What c can w we do do? ? Thread T 0 Add assignment to turn to establish the second disjunct while (!terminate) { Thread T 1 in 0 := true while (!terminate) { { in 0 } Thread T 0 Thread T 1 in 1 := true while ( in 1 ∧ turn ≠ 0); while (!terminate) { while (!terminate) { { in 1 } in 0 := true; in 1 := true; { in 0 ∧ (¬ in 1 ∨ turn = 0)} α 0 while ( in 0 ∧ turn ≠ 1); turn := 1; turn := 0; α 1 CS 0 { in 0 } { in 1 } { in 1 ∧ (¬ in 0 ∨ turn = 1)} ……… while ( in 1 ∧ turn ≠ 0); while ( in 0 ∧ turn ≠ 1); CS 1 { in 0 ∧ (¬ in 1 ∨ turn = 0 ∨ at( α 1 ) )} { in 1 ∧ (¬ in 0 ∨ turn = 1 ∨ at( α 0 ) )} ……… } CS 0 CS 1 in 0 := false; in 1 := false; } NCS 0 NCS 1 } } The assignment to in 0 invalidates the invariant! 13 14 Safe? ? Live? ? Thread T 0 Thread T 1 while (!terminate) { while (!terminate) { Thread T 0 Thread T 1 {S 1 : ¬ in 0 ∧ ( turn = 1 ∨ turn = 0)} {R 1 : ¬ in 0 ∧ ( turn = 1 ∨ turn = 0)} while (!terminate) { while (!terminate) { in 0 := true; in 1 := true; in 0 := true; in 1 := true; {S 2 : in 0 ∧ ( turn = 1 ∨ turn = 0)} {R 2 : in 0 ∧ ( turn = 1 ∨ turn = 0)} α 0 α 1 turn := 1; turn := 0; α 0 turn := 1; α 1 turn := 0; { in 0 } { in 1 } {S 2 } {R 2 } while ( in 1 ∧ turn ≠ 0); while ( in 0 ∧ turn ≠ 1); while ( in 1 ∧ turn ≠ 0); while ( in 0 ∧ turn ≠ 1); {S 3 : in 0 ∧ (¬ in 1 ∨ at( α 1 ) ∨ turn = 0)} {R 3 : in 1 ∧ (¬ in 0 ∨ at( α 0 ) ∨ turn = 1)} { in 0 ∧ (¬ in 1 ∨ turn = 0 ∨ at( α 1 ) )} { in 1 ∧ (¬ in 0 ∨ turn = 1 ∨ at( α 0 ) )} CS 0 CS 1 CS 1 CS 0 {S 3 } {R 3 } in 1 := false; in 0 := false; in 0 := false; in 1 := false; NCS 1 NCS 0 {S 1 } {R 1 } } } NCS 0 NCS 1 } } If both in CS, then Non-blocking: T 0 before NCS 0 , T 1 stuck at while loop in 0 ∧ ( ¬in 1 ∨ at( α 1 ) ∨ turn = 0) ∧ in 1 ∧ ( ¬in 0 ∨ at( α 0 ) ∨ turn = 1) ∧ S 1 ∧ R 2 ∧ in 0 ∧ ( turn = 0) = ¬in 0 ∧ in 1 ∧ in 0 ∧ ( turn = 0) = false ∧ ¬ at( α 0 ) ∧ ¬ at( α 1 ) = ( turn = 0) ∧ ( turn = 1) = false Deadlock-free: T 1 and T 0 at while , before entering the critical section S 2 ∧ R 2 ∧ ( in 0 ∧ ( turn = 0)) ∧ ( in 1 ∧ ( turn = 1)) ⇒ ( turn = 0) ∧ ( turn = 1) = false 15 16 Bounde ded w d waiting? ? Thread T 0 Thread T 1 while (!terminate) { while (!terminate) { in 0 := true; in 1 := true; turn := 1; turn := 0; while ( in 1 ∧ turn ≠ 0); while ( in 0 ∧ turn ≠ 1); CS 0 CS 0 in 0 := false; in 1 := false; NCS 0 NCS 0 } } Yup! 17
Recommend
More recommend