12 Commandments of Monitors in Python Synchronization class BK: def __init__(self): I. Thou shalt name your self.lock = Lock() VIII. Honor thy shared synchronization variables data with an invariant, properly self.hungrykid = Condition(self.lock) which your code may assume II. Thou shalt not violate holds when a lock is acquired self.nBurgers= 0 abstraction boundaries nor successfully and your code wait try to change the semantics of must make true before the synchronization primitives lock is released • releases lock when called III. Thou shalt use monitors IX. Thou shalt cover thy def kid_eat(self): and condition variables naked waits. • re-acquires lock when it returns instead of semaphores with self.lock: whenever possible X. Thou shalt guard your wait predicates in a while IV. Thou shalt not mix while self.nBurgers == 0: loop. Thou shalt never semaphores and guard a wait statement condition variables self.hungrykid.wait() with an if statement V. Thou shalt not busy wait X1. Thou shalt not split self.nBurgers = self.nBurgers - 1 predicates. VI. Thou shalt protect all shared state XII. Thou shalt help make VII. Thou shalt grab the monitor the world a better place def make_burger(self): lock upon entry to, and release for the creator ’s mighty it upon exit from, a synchronization vision. with self.lock: procedure self.nBurgers = self.nBurgers + 1 self.hungrykid.notify() � 158 � 159 Monitors in “4410 Python”: Monitors in “4410 Python”: __init__ kid_eat def kid_eat(self): Python class BK: Python with self.lock: def __init__(self): while self.nBurgers == 0: self.lock = Lock() self.hungrykid. wait() self.hungrykid = Condition(self.lock) self.nBurgers = self.nBurgers - 1 self.nBurgers= 0 def kid_eat(self): 4410 Python with self.lock: from rvr import MP, MPthread 4410 Python while (self.nBurgers.read() == 0): self.hugryKid.wait() class BurgerKingMonitor(MP): self.nBurgers.dec() def __init__(self): MP.__init__(self,None) We do this for helpful feedback: Look in the A2/doc self.lock = Lock(“monitor lock”) self.hungrykid = self.lock.Condition(“hungry kid”) • from auto-grader directory for details self.nBurgers = self.Shared(“num burgers”, 0) • from debugger and example code. � 160 � 161
Readers/Writers Readers/Writers Monitor ReadersNWriters { Safety int waitingWriters=0, waitingReaders=0, activeReaders=0, activeWriters=0; Condition canRead, canWrite; (# r ≥ 0) ∧ (0 ≤ # w ≤ 1) ∧ (# r > 0) ⇒ (# w = 0)) void BeginWrite() with monitor.lock: ++waitingWriters What about fairness? while (activeWriters >0 or activeReaders >0) canWrite.wait(); the last thread to live the critical section will --waitingWriters activeWriters = 1; give priority to writers To implement this policy, one needs to keep } track of waitingWriters, waitingReaders, activeWriters, and activeReaders � 162 � 163 Readers/Writers Readers/Writers Monitor ReadersNWriters { Monitor ReadersNWriters { int waitingWriters=0, waitingReaders=0, activeReaders=0, activeWriters=0; int waitingWriters=0, waitingReaders=0, activeReaders=0, activeWriters=0; Condition canRead, canWrite; Condition canRead, canWrite; void BeginRead() void BeginWrite() void BeginWrite() with monitor.lock: with monitor.lock: with monitor.lock: ++waitingReaders ++waitingWriters ++waitingWriters while (activeWriters>0 or waitingWriters>0) while (activeWriters >0 or activeReaders >0) while (activeWriters >0 or activeReaders >0) canRead.wait(); canWrite.wait(); canWrite.wait(); --waitingReaders --waitingWriters --waitingWriters ++activeReaders activeWriters = 1; activeWriters = 1; void EndWrite() void EndWrite() with monitor.lock: with monitor.lock: activeWriters = 0 activeWriters = 0 if waitingWriters > 0 if waitingWriters > 0 canWrite.signal(); canWrite.signal(); else if waitingReaders > 0 else if waitingReaders > 0 canRead.broadcast(); canRead.broadcast(); � 164 � 165 } }
Readers/Writers Barrier Synchronization Monitor ReadersNWriters { n threads divide work, run rounds of int waitingWriters=0, waitingReaders=0, activeReaders=0, activeWriters=0; computation separated by barriers Condition canRead, canWrite; void BeginRead() Common paradigm in HPC void BeginWrite() with monitor.lock: with monitor.lock: ++waitingReaders Create n threads and barrier ++waitingWriters while (activeWriters>0 or waitingWriters>0) while (activeWriters >0 or activeReaders >0) Each thread does round1() canRead.wait(); canWrite.wait(); --waitingReaders barrier.chackin() --waitingWriters ++activeReaders activeWriters = 1; Each thread does round2() barrier.checkin() void EndWrite() void EndRead() with monitor.lock: with monitor.lock: activeWriters = 0 --activeReaders; if waitingWriters > 0 if (activeReaders==0 and waitingWriters>0) canWrite.signal(); canWrite.signal(); else if waitingReaders > 0 canRead.broadcast(); � 166 � 167 } Checkin with one condition variable self.allCheckedIn = Condition(self.lock) What ’ s def checkin(): wrong with self.lock: with this? nArrived++ if nArrived < nThreads: while nArrived < nThreads: allCheckedIn.wait() else: allCheckedIn.broadcast() nArrived = 0 � 168
System Model Exclusive (one-at-a-time) computer resources Deadlocks: CPU, printers, memory, locks, etc. Processes Prevention, Avoidance, Acquire resource Detection, Recovery if resource is available, access is granted if not, process is blocked Use resource Release resource � 1 � 2 1 2 Deadlock Deadlock A cycle of waiting among a set of threads A cycle of waiting among a set of threads A violation of liveness A violation of liveness T 1 acquire resource 1, waits for resource 2 T 1 acquire resource 1, waits for resource 2 T 2 acquires resource 2, waits for resource 1 T 2 acquires resource 2, waits for resource 1 T 1 T 2 { { P(file_mutex) P(printer_mutex) P(printer_mutex) P(file_mutex) semaphore: file_mutex = 1 /* use resources */ /* use resources */ printer_mutex = 1 V(printer_mutex) V(file_mutex) V(file_mutex) V(printer_mutex) � 3 � 3 } } 3-1 3-2
Dining Philosophers Musings on Deadlock Deadlock vs Starvation class Philosopher: chopsticks[N] = [Semaphore(1),…] Starvation: some thread’ s access to a resource is def __init__(mynum) indefinitely postponed self.id = mynum Deadlock: circular waiting for resources def eat(): right = self.id Deadlock implies Starvation, but not vice versa left = (self.id+1) % N while True: “Subject to deadlock” does not imply “Will deadlock” P(chopsticks[left]) N philosophers; N plates; N chopsticks P(chopsticks[right]) Testing is not the solution # om nom nom nom If all philosophers grab right chopstick V(chopsticks[right]) System must be deadlock-free by design deadlock! V(chopsticks[left]) Need exclusive access to two chopsticks � 4 � 5 4 5 A Graph Theoretic Model Necessary Conditions of Deadlock for Deadlock Computer system modeled as a RAG, a Deadlock possible only if all four hold directed graph G(V , E) Bounded resources A finite number of threads can use a resource; resources are finite V = {P 1 ,…,P n } ⋃ {R 1 ,…,R n } P i R j No preemption the resource is mine, MINE! (until I E = {edges from a resource to a process} ⋃ release it) Hold & Wait {edges from a process to a resource} holds one resource while waiting for another Circular waiting P i P k allocation T i waits for T i+1 and holds a resource request edge edge requested by T i-1 sufficient only if one instance of each resource R j � 6 � 7 6 7-1
Necessary Conditions Necessary Conditions for Deadlock for Deadlock Deadlock possible only if all four hold Deadlock possible only if all four hold Bounded resources Bounded resources P 0 A finite number of threads can use a A finite number of threads can use a resource; resources are finite resource; resources are finite No preemption No preemption Resource type Resource type the resource is mine, MINE! (until I with 5 instances the resource is mine, MINE! (until I with 5 instances P 4 P 1 release it) release it) Hold & Wait Hold & Wait holds one resource while waiting for holds one resource while waiting for another another Circular waiting Circular waiting T i waits for T i+1 and holds a resource T i waits for T i+1 and holds a resource requested by T i-1 requested by T i-1 P 3 P 2 sufficient only if one instance of each sufficient only if one instance of each resource resource � 7 � 7 7-2 7-3 Necessary Conditions Necessary Conditions for Deadlock for Deadlock Deadlock possible only if all four hold Deadlock possible only if all four hold Bounded resources Bounded resources P 0 P 0 A finite number of threads can use a A finite number of threads can use a owned owned resource; resources are finite resource; resources are finite by by No preemption No preemption Resource type Resource type the resource is mine, MINE! (until I with 5 instances the resource is mine, MINE! (until I with 5 instances P 4 P 1 P 4 P 1 release it) release it) Hold & Wait Hold & Wait holds one resource while waiting for holds one resource while waiting for another another Circular waiting Circular waiting T i waits for T i+1 and holds a resource T i waits for T i+1 and holds a resource requested by T i-1 requested by T i-1 P 3 P 2 P 3 P 2 sufficient only if one instance of each sufficient only if one instance of each resource resource � 7 � 7 7-4 7-5
Recommend
More recommend