more musings on classic mistakes readers writers with
play

More Musings on Classic Mistakes Readers/Writers with Semaphores N - PowerPoint PPT Presentation

More Musings on Classic Mistakes Readers/Writers with Semaphores N U J V If readers and writers are waiting, I P(S) and a writer exits, who goes first? I stuck on 2nd P(). Subsequent CS processes hopelessly pile on 1st P() int read() { Why


  1. More Musings on Classic Mistakes Readers/Writers with Semaphores N U J V If readers and writers are waiting, I P(S) and a writer exits, who goes first? I stuck on 2nd P(). Subsequent CS processes hopelessly pile on 1st P() int read() { Why do readers use a mutex? P(S) rcount_mutex.P(); void write() { rcount := rcount+1; rOw_lock.P(); Why don’ t writers use a mutex? if (rcount == 1) then Undermines mutex: … J rOw_lock.P(); /* Perform write */ V(S) • J does not get permission via P() What if we move rcount_mutex.V(); … CS • “extra” V() allows other processes into … rcount_mutex.V() just above rOw_lock.V(); /* Perform read */ V(S) CS inappropriately } if (rcount = 1)? … rcount_mutex.P(); L rcount := rcount-1; P(S) Conditional code can change code if (rcount == 0) then if (x) return; flow in the CS. Caused by code rOw.lock.V(); Shared: rcount_mutex.V(); int rcount = 0; CS updates (bug fixes, etc.) by someone } Semaphore rcount_mutex (1); other than original author of code. V(S) Semaphore rOw_lock(1); � 89 � 90 Semaphores considered Edsger’ s perspective harmful “During system conception it transpired that we used the semaphores in Semaphores are “low-level” primitives. Small two completely different ways. The difference is so marked that, looking errors back, one wonders whether it was really fair to present the two ways as can introduce incorrect executions or grind the uses of the very same primitives. On the one hand, we have the program to a halt semaphores used for mutual exclusion, on the other hand, the private very difficult to debug semaphores.” Semaphores conflate two distinct uses The structure of the ’THE’-Multiprogramming System” Communications of the ACM v. 11 n. 5 May 1968. mutex condition synchronization (e.g., bounded buffer) � 92 � 91

  2. How did Monitors Enter Monitors come about? Collect shared data into an object/module First introduced as an OO programming language construct Define methods for accessing shared data synchronization object + methods Separate the concerns of mutual exclusion and condition synchronization calling a method defined in the monitor automatically acquires the lock They are comprised of Mesa, Java (synchronized methods) one lock, and A programming convention zero or more condition variables for managing can be defined in any language concurrent access to shared data � 93 � 94 How do I wait for thee? Condition Variables Let me count the ways… An abstraction for conditional synchronization At the entry of the monitor associated with a monitor threads can queue on the mutex that protects the Enable threads to wait inside a critical monitor, waiting for the thread that is currently in section by releasing the monitor lock the monitor to exit (or to release the lock by waiting on a condition variable) A misnomer On a condition variable can neither be read nor set to a value threads can queue waiting on the associated think of them as a label associated with a condition condition on a resource and a queue thread can wait in the queue (inside the CS) until t hey are notified that condition holds � 95 � 96

  3. Condition Variables: Resource Variables Operations Condition variables (unlike semaphores) are stateless Three operations on condition variable x Each condition variable should be associated with a resource variable (RV) tracking the state of that x.wait(lock) resource Atomically: Release lock and go to sleep It is your job to maintain the RV! sleep by waiting on the queue associated with x Check its RV before calling wait() on a condition variable to ensure the resource is truly unavailable x.notify (historically called x.signal()) Once the resource is available, claim it (subtract the wake up a waiter if any; otherwise no-op amount you are using!) wake up by moving waiter to the ready queue Before notifying you are releasing a resource, indicate it x.notifyall (historically called x.broadcast()) has become available by increasing the corresponding RV � 97 � 98 Notify() semantics: Notify() Semantics Mesa vs. Hoare Mesa (or Brinch Hansen) semantics: Which thread executed once notify() is signaled thread is moved to ready list, but not called on CV? guaranteed to run right away if no thread is waiting on CV , notifier continues Hoare semantics: if one or more thread waiting on CV: signaling thread is suspended and, atomically, ownership of the lock is passed to one of the at least two ready threads: notifier and thread(s) waiting threads, whose execution is immediately that are moved from the queue of the CV to the resumed. ready queue notifying thread is resumed if former waiter exits only one can run… crucial section, or if it waits again …but which one? � 99 � 100

  4. What are the notify() vs notifyall() implications? (signal() vs. broadcast()) Mesa/Brinch Hansen Hoare It is always safe to use notifyall() instead of notify() signal() and broadcast() are hints Signaling is atomic with the only performance is affected resumption of waiting thread adding them affects performance, never safety shared state cannot change notify() is preferable when before waiting thread is resumed Shared state must be checked in a at most one waiting thread can make progress loop (could have changed! (tricky Shared state can be checked using tricky…)) an if statement any thread waiting on the condition variable can make robust to spurious wakeups progress Makes it easier to prove liveness Simple implementation Tricky to implement notifyall() is preferable when Used in most systems interferes with scheduling multiple waiting thread may be able to make progress Sponsored by a Turing Award Used in most books (but not yours!) a single condition variable is used for multiple predicates Butler Lampson Sponsored by a Turing Award some waiting threads can make progress, others can’ t Tony Hoare � 101 � 102 Condition Variables vs Producer-Consumer Semaphores with Bounded Buffer 0 N-1 wait() vs P() P() blocks threads only if value = 0 in out wait() always block and gives up monitor lock Shared: / / remove item from buffer notify() vs V() int buf[N]; / / add item to buffer int consume() { int in := 0, out := 0; void produce(int item) { V is stateful - future thread does not wait on P() full.P(); Semaphore mutex_in(1), empty.P(); mutex_out.P(); if no waiting thread, notify() is a no op mutex_out(1); mutex_in.P(); int item := buf[out%N]; Semaphore empty(N), full(0); condition variables are stateless buf[in%N] := item; out := out+1; in := in+1; mutex_out.V(); Code that uses monitors is easier to read mutex_in.V(); Semaphores empty.V(); full.V(); return(item); Conditions for which threads are waiting are explicit } } � 103 � 104 � 104

  5. Kid and Cook Threads Producer-Consumer Monitor BurgerKing { Lock mlock; with Bounded Buffer 0 int numburgers = 0; condition hungrykid; kid_main() { cook_main() { 0 N-1 dig_in_mud(); wake(); void kid_eat() { BK.kid_eat(); shower(); mlock.acquire() in out bathe(); drive_to_work(); while (numburgers==0) draw_on_walls(); while(not_5pm) hungrykid.wait() / / remove item from buffer BK.kid_eat(); BK.makeburger(); Monitor Producer Consumer { numburgers -= 1 facetime_Karthik(); drive_to_home(); / / add item to buffer int consume() { char buf[N]; mlock.release() facetime_oma(); watch_got(); lock.Acquire(); void produce(int item) { Lock lock; } BK.kid_eat(); sleep(); while (n == 0) lock.Acquire() int n := 0, in := 0, out := 0; while (n == N) wait(nonEmpty); Condition notEmpty, notFull; void makeburger() { } } int item := buf[out%N]; mlock.acquire() wait(notFull); ++numburger; buf[in%N] := item; out := out+1; hungrykid.signal(); in := in+1; n:= n-1 mlock.release() Monitor notify(notFull); n := n+1; } Ready Running notify(notEmpty); lock.Release(); } return(item); lock.Release() } } �106 � 105 � 105 Kid and Cook Threads Kid and Cook Threads Monitor BurgerKing { Monitor BurgerKing { Lock mlock; Lock mlock; 0 0 int numburgers = 0; int numburgers = 0; condition hungrykid; condition hungrykid; kid_main() { cook_main() { kid_main() { cook_main() { dig_in_mud(); wake(); dig_in_mud(); wake(); void kid_eat() { void kid_eat() { BK.kid_eat(); shower(); BK.kid_eat(); shower(); mlock.acquire() mlock.acquire() bathe(); drive_to_work(); bathe(); drive_to_work(); while (numburgers==0) while (numburgers==0) draw_on_walls(); while(not_5pm) draw_on_walls(); while(not_5pm) hungrykid.wait() hungrykid.wait() BK.kid_eat(); BK.makeburger(); BK.kid_eat(); BK.makeburger(); numburgers -= 1 numburgers -= 1 facetime_Karthik(); drive_to_home(); facetime_Karthik(); drive_to_home(); mlock.release() mlock.release() facetime_oma(); watch_got(); facetime_oma(); watch_got(); } } BK.kid_eat(); sleep(); BK.kid_eat(); sleep(); void makeburger() { void makeburger() { } } } } mlock.acquire() mlock.acquire() ++numburger; ++numburger; hungrykid.signal(); hungrykid.signal(); mlock.release() mlock.release() } } Ready Running Ready Running } } girl swapped in girl executes �107 �108

Recommend


More recommend