CS533 Concepts of Operating Systems Class 3 Monitors
Enforcing mutual exclusion Assumptions: Every thread sets the lock before accessing shared data! Every thread releases the lock after it is done! Only works if you follow these programming conventions all the time! Thread 1 Thread 2 Thread 3 Lock Lock A = 2 A = A+1 A = A*B Unlock Unlock CS533 - Concepts of Operating Systems
Solutions to misuse (or no use) of locks Solution 1: use static or dynamic checking tools to help track down misuses of locking primitives (synchronization bugs) Solution 2: have the compiler insert the synchronization primitives for you automatically CS533 - Concepts of Operating Systems
Solution 1: Checking tools (class 2 cont.) Eraser A dynamic checker that uses binary re-writing techniques Gathers an “execution history” of reads, writes and lock acquisitions Evaluates consistency with rules Is it enough to simply check that some lock is held whenever a global variable is accessed? CS533 - Concepts of Operating Systems
Automated checking of conventions Eraser doesn’t know ahead of time which locks protect which variables It infers which locks protect which variables using a lock-set algorithm Assume all locks are candidates for a variable ( C(v) is full) For each access take intersection of C(v) and locks held by thread and make these the candidate set C(v) If C(v) becomes empty, issue warning CS533 - Concepts of Operating Systems
Improving the locking discipline The standard approach produces many false positives that arise due to special cases: Initialization No need to lock if no thread has a reference yet Read sharing No need to lock if all threads are readers Reader/writer locking Distinguish concurrent readers from concurrent readers and writers CS533 - Concepts of Operating Systems
CS533 - Concepts of Operating Systems Improved algorithm wr, new thread virgin rd, wr First thread shared Modified (race?) exclusive rd rd, new thread wr wr shared CS533 - Concepts of Operating Systems
CS533 - Concepts of Operating Systems Solution 2: Monitors Monitors employ two key concepts, both of which can be automated by a compiler: Encapsulation: Local data variables are accessible only via the monitor’s entry procedures (like methods) Mutual exclusion: The entry procedures are treated as critical sections CS533 - Concepts of Operating Systems
Two kinds of synchronization Mutual exclusion Only one at a time in the critical section Condition synchronization Wait until a certain condition holds Signal waiting threads when the condition holds CS533 - Concepts of Operating Systems
(Each has an associated list of waiting threads) Monitor structures initialization code “entry” methods y x shared data condition variables monitor entry queue List of threads waiting to enter the monitor Can be called from outside the monitor. Only one active at any moment. Local to monitor (Each has an associated list of waiting threads) local methods CS533 - Concepts of Operating Systems
Implementing mutual exclusion for monitors How can we implement mutual exclusion for monitor procedures? CS533 - Concepts of Operating Systems
Implementing mutual exclusion for monitors How can we implement mutual exclusion for monitor procedures? Will spinning locks work? CS533 - Concepts of Operating Systems
Implementing mutual exclusion for monitors How can we implement mutual exclusion for monitor procedures? Will spinning locks work? Will yielding locks work? CS533 - Concepts of Operating Systems
Implementing mutual exclusion for monitors How can we implement mutual exclusion for monitor procedures? Will spinning locks work? Will yielding locks work? What if we don’t have atomic instructions? CS533 - Concepts of Operating Systems
Implementing mutual exclusion for monitors How can we implement mutual exclusion for monitor procedures? Will spinning locks work? Will yielding locks work? What if we don’t have atomic instructions? Idea 1: Disable interrupts during monitor procedures CS533 - Concepts of Operating Systems
Using monitors to build a blocking mutex Blocking_mutex:monitor Begin busy:boolean; nonbusy:condition; busy:=false; // initial value Procedure acquire() if busy then nonbusy.wait; busy:=true; End Procedure release() busy:=false; nonbusy.signal End; End Blocking_mutex; CS533 - Concepts of Operating Systems
Using monitors to build a blocking mutex Blocking_mutex:monitor Begin busy:boolean; nonbusy:condition; Busy:=false; // initial value Procedure acquire() Begin <----- disable interrupts if busy then nonbusy.wait; busy:=true; End <----- enable interrupts Procedure release() busy:=false; nonbusy.signal End; <----- enable interrupts End Blocking_mutex; CS533 - Concepts of Operating Systems
Using monitors to build a blocking mutex Blocking_mutex:monitor Begin busy:boolean; nonbusy:condition; Busy:=false; // initial value Procedure acquire() Begin ----- disable interrupts if busy then nonbusy.wait; <----- ???? busy:=true; End ----- enable interrupts Procedure release() busy:=false; nonbusy.signal <----- ???? End; ----- enable interrupts End Blocking_mutex; CS533 - Concepts of Operating Systems
Implementing condition variables Wait Add process to queue of processes waiting on this condition Suspend process Release monitor’s mutual exclusion Reenable interrupts Wake up / schedule next process trying to enter monitor Signal Wake up / schedule first process waiting on this condition Release monitor’s mutual exclusion? Suspend yourself On what? … and how do you ever wake up again? CS533 - Concepts of Operating Systems
Implementing mutual exclusion for monitors How can we implement mutual exclusion for monitor procedures? Will spinning locks work? Will yielding locks work? What if we don’t have atomic instructions? Idea 1: Disable interrupts during monitor procedures Idea 2: Use binary semaphores CS533 - Concepts of Operating Systems
Building monitors from binary semaphores See example in paper CS533 - Concepts of Operating Systems
Bounded buffer solution with monitors process Producer begin loop <produce char “c”> BoundedBuffer.append(c) end loop end Producer BoundedBuffer: monitor var buffer : ...; nextIn, nextOut :... ; procedure append (c: char) begin ... end procedure remove (var c: char) end BoundedBuffer process Consumer begin loop BoundedBuffer.remove(c) <consume char “c”> end loop end Consumer CS533 - Concepts of Operating Systems
Bounded buffer solution with monitors BoundedBuffer: monitor var buffer : array[0..n-1] of char nextIn,nextOut : 0..n-1 := 0 Count : 0..n := 0 nonEmpty, nonFull : condition procedure append(c:char) procedure remove(var c: char) begin begin if (Count = n) then if (Count = n) then wait(nonFull) wait(nonEmpty) end if end if buffer[nextIn] := c c := buffer[nextOut] nextIn := nextIn+1 mod n nextOut := nextOut+1 mod n Count := Count+1 Count := Count-1 signal(nonEmpty) signal(nonFull) end append end remove end BoundedBuffer CS533 - Concepts of Operating Systems
CS533 - Concepts of Operating Systems Alarm clock example AlarmClock: monitor Begin now: integer; wakeup: condition; now := 0; Procedure wakeme(n: integer); alarmsetting: integer; alarmsetting := now + n; While now < alarmsetting do wakeup.wait (alarmsetting); wakeup.signal; End; Procedure tick; now := now + 1; End AlarmClock; CS533 - Concepts of Operating Systems