340 likes | 478 Views
Chapter 7 Monitors. Structure of a Monitor in JBACI. monitor <name> <declaration of monitor variables> <procedure 1> <procedure 2> . . <procedure n> begin <statements to initialize the variables> end. General Remarks.
E N D
Structure of a Monitor in JBACI monitor <name> <declaration of monitor variables> <procedure 1> <procedure 2> . . <procedure n> begin <statements to initialize the variables> end
General Remarks • The variables declared in a monitor can only be accessed within the monitor procedures • The procedures may have parameters. Communication between a monitor and the outside world (ie., processes) is through these parameters • The begin-end block to initialize the variables is activated when the program containing the monitor is activated. Thereafter, the monitor is only a package of data and procedures
General Remarks (Cont.) • A monitor is invoked by calling one of the monitor procedures • A monitor is uniterruptable • The execution of a monitor procedure is mutually exclusive meaning that entry to a monitor by a process excludes entry by any other process • Different monitors can be executed concurrently • If there are several processes attempting to enter a monitor, only one of them will succeed. There is no explicit queue associated with the monitor entry, so starvation is possible
Declaring and Using Monitors • Processes p and q call the monitor operation CS.increment • Mutual exclusion is ensured and what ever the process execution order is, the resulting n value is always 2 Executing a Monitor Operation
Conditional Signalling Statements Waitc(c) Signalc(c) Empty(c) • cis a type called a condition
Waitc(c) • The calling process is blocked and put on a queue of processes on the condition c (assume a FIFO queue) • When a wait(c) is issued, the mutual exclusion on the entry to a monitor is released. That is, the process exits from the monitor procedure
Signalc(c) • If the condition queue c is empty, the signal is lost. If the queue is not empty then wake up the first process in the queue • When a signal is sent, a process in the condition queue is the next one to be activated even though there may be other processes (not blocked) to enter the monitor
Empty(c) • This function returns TRUE if the condition queue c is empty else it returns FALSE
Correctness of the Semaphore Simulation Monitor operations are shown as single steps as they are mutually executed and the statements can not be interleaved There is no state of the form (p2: Sem.signal, q2: Sem.signal,..,), which means that both processes were in their CS, so the mutual exclusion requirement is satisfied
Simulation of a Binary Semaphore program semaphore_emulation; MONITOR monSemaphore; PROCEDURE monP; ............. END; PROCEDURE monV; ............. END; END; procedure p1; begin while true do begin monP; writeln('p1 is in critical section'); monV; end; end; procedure p2; begin while true do begin monP; writeln('p2 is in critical section'); monV; end; end; begin {main} cobegin p1; p2 coend; end.
Simulation of a Binary Semaphore (Cont.) MONITOR monSemaphore; VAR semvalue : INTEGER; notbusy : CONDITION; PROCEDUREmonP; BEGIN IF (semvalue = 0) THEN WAITC(notbusy); semvalue := semvalue -1; END; PROCEDUREmonV; BEGIN semvalue := semvalue + 1; SIGNALC(notbusy); END; BEGIN { initialization code } semvalue := 1; END;
Producer-Consumer with N Buffers program producer_consumer; const n=5; monitor producer_consumer_monitor; procedure append(i: integer); ............... end; procedure take(i: integer); .................... end; end; procedure producer; var i: integer; begin while true do begin i:=random(10); writeln('value produced =',i); (* produce item *) append(i); end; end; procedure consumer; var i: integer; begin while true do begin take(i); writeln(‘value consumed =',i); (* consume item *) end; end; begin {main} cobegin producer; consumer coend; end.
Producer-Consumer with N Buffers (Cont.) monitor producer_consumer_monitor; var b: array[1..n] of integer; in_ptr, out_ptr: integer; count: integer; not_full, not_empty: condition; procedureappend(i: integer); begin if count = n then waitc(not_full); count:=count+1; b[in_ptr]:= i; in_ptr:= in_ptr+1; if in_ptr > n then in_ptr:= 1; signalc(not_empty); end; proceduretake(var i: integer); begin if count = 0 then waitc(not_empty); count:=count-1; i:= b[out_ptr]; out_ptr:= out_ptr+1; if out_ptr > n then out_ptr:= 1; signalc(not_full); end; begin in_ptr:=1; out_ptr:=1; for count:= 0 to n do b[count]:=-1; count:=0; end;
Readers and Writers Problem • A file is open for multiple reads and writes. • Readers :Processes which are required to exclude writers but not other readers (multiple reads require no mutual exclusion) • Writers : Processes which are required to exclude both readers and other writers (a writing operation requires mutual exclusion with all reads and writes) • Rules: • If there are waiting writers then a new reader is required to wait for the termination of a write (not the other waiting writers) • If there are readers waiting for the termination of a write, they have priority over the next write Hence, at any time, there may be several readers active or only one writer
Readers and Writers Problem program ReadersWriters; var screen :semaphore:=1; monitor RW; procedure StartRead; ................. end; procedure EndRead; ................ end; procedure StartWrite; ................ end; procedure EndWrite; ................ end; end; procedure Reader(N: integer); begin while true do begin StartRead; wait(screen); writeln(N, " is reading"); signal(screen); EndRead; end; end; procedure Writer(N: integer); begin while true do begin StartWrite; wait(screen); writeln(N, " is writing"); signal(screen); EndWrite; end; end; begin cobegin Reader(1); Reader(2); Reader(3); Reader(4); Reader(5); Reader(6); Reader(7); Reader(8); Reader(9); Writer(1); Writer(2); Writer(3); Writer(4); Writer(5); Writer(6); Writer(7); Writer(8); coend; end.
Readers and Writers Problem (Cont.) monitor RW; varreadercount: integer; writing: boolean; OKtoRead, OKtoWrite: condition; procedureStartRead; begin if writing or not empty(OKtoWrite) then waitc(OKtoRead); readercount := readercount + 1; writeln("Reader count is ", readercount); signalc(OKtoRead); end; procedureEndRead; begin readercount := readercount - 1; if readercount = 0 then signalc(OKtoWrite); end; procedureStartWrite; begin if writing or (readercount <> 0) then waitc(OKtoWrite); writing := true; end; procedureEndWrite; begin writing := false; if empty(OKtoRead) then signalc(OKtoWrite) else signalC(OKtoRead); end; Begin (* monitor main *)readercount := 0; writing := false;end;
Remarks • StartRead: If a process is writing or if a process is waiting to write then wait for OktoRead signal • EndRead: When the number of readers become zero then activate a writer (if there is one waiting) • StartWrite: wait for an OKtowrite signal until there are no active readers or a writer • EndWrite: If there are processes waiting for reading then activate a reader else a writer (if waiting)
Remarks (Cont.) • The “signalc(OktoRead)” statement at the end of StartRead procedure is used to wake up waiting readers one after the other. Suppose readers R1, R2,.., Rn begin to execute during a writing operation. Since the “writing” flag is true at that time, all of these processes wait OktoRead signals. When the writing is over, EndWrite procedure will send one signal to wake up R1. R1wakes up R2 by sending OktoRead signal at the end of StartRead. R2 then wakes up the next reader. The last signal sent after waking up Rn will be lost by definition.
Remarks (Cont.) • Rule (a) implementation: • Waiting writers wait in StartWrite for an OktoWrite signal • A reader issues StartRead but waits for an OktoRead signal • A writer terminates in EndWrite and sends an OktoRead signal So a new reader and all other waiting readers wait for the completion of the current write only
Remarks (Cont.) • Rule (b) implementation: • Waiting readers wait for OktoRead signals • A writer terminates and sends a signal to the first waiting reader. This reader wakes up the next waiting readers etc.
Remarks (Cont.) • “not empty(OKtoWrite)” statement in StartRead’s “if writing or not empty(OKtoWrite) then waitc(OKtoRead);” statement • Readers R1 and R2 are executing. There is a writer W1 waiting. A new reader R3 starts when the readers are still executing. Should we let it execute? • There is no end to it. There may be new readers coming which means that if they execute, the writer W1 will be delayed indefinitely • So the “not empty(OKtoWrite)” condition ensures that readers that come after writer W1will be blocked until W1 executes
Dining Philosophers program dining_philosophers; var screen :semaphore:=1; monitor forks; procedure take_forks(i :integer); end; procedure release_forks(i :integer); end; end; procedure think; var i : integer; begin for i:= 1 to random(100) do end; procedure eat; var i : integer; begin for i:= 1 to random(50) do end; procedure philosopher(i :integer); begin repeat think; take_forks(i); wait(screen); writeln('philosopher ',i,' is now eating'); signal(screen); eat; release_forks(i); wait(screen); writeln('philosopher ',i,' has finished eating'); signal(screen); until false; end; begin cobegin philosopher(0); philosopher(1); philosopher(2); philosopher(3); philosopher(4); coend; end.
Dining Philosophers monitor forks; var fork :array[0..4] of integer; oktoeat :array[0..4] of condition; i :integer; procedure take_forks(i :integer); begin if (fork[i] <> 2) then waitc(oktoeat[i]); (* wait for 2 forks *) fork[(i+1) mod 5] := fork[(i+1) mod 5]-1; (* take the right fork *) fork[(i-1) mod 5] := fork[(i-1) mod 5]-1; (* take the left fork *) end; procedure release_forks(i :integer); begin fork[(i+1) mod 5] := fork[(i+1) mod 5]+1; fork[(i-1) mod 5] := fork[(i-1) mod 5]+1; if fork[(i+1) mod 5] = 2 then signalc(oktoeat[(i+1) mod 5]); if fork[(i-1) mod 5] = 2 then signalc(oktoeat[(i-1) mod 5]); end; begin for i:= 0 to 4 do fork[i]:= 2; (* all forks free *) end;
Remarks • In the semaphore solution of the dining philosophers problem there is no way to test the value of two fork semaphores (left and right forks) simultaneously. Hence, we can not design a solution which causes a philosopher to wait until both forks are free so we forced four to sit at the most • In the monitor solution, the array fork counts the number of forks available to each philosopher. The take_forks procedure waits on oktoeat condition until two forks are available • The solution may cause starvation if two philosophers conspire to starve their mutual neighbours. That is, if they finish at the same time and if they are quick enough to grasp the two forks to start eating once more, the neigbours may starve to death
Emulation of Monitors Using Semaphores • Declare: • A semaphore for monitor procedure mutual exclusion (S) • A semaphore for each condition variable (C_S) • A counter for each condition variable to record the number of processes in the condition queue (C_C) • Put a wait(s) as the first instruction and a signal(S) as the last in each monitor procedure or at appropriate places for the mutual exclusion
Emulation of Monitors Using Semaphores (Cont.) • Replace each signalc(C) by If C_C > 0 then signal(C_S) else signal(S); Remarks: If there are processes waiting on condition C then wake up one (FIFO) by calling the signal function else release mutual exclusion. Note that, mutual exclusion is to be released by the awakened process
Emulation of Monitors Using Semaphores (Cont.) • Replace each waitc(C) by C_C := C_C + 1;Increment C_C Signal(S); Release mutual exclusion Wait(C_S);Join the condition queue Wait(S); Mutual exclusion for the next statement C_C := C_C – 1;Decrement C_C
Emulation of Monitors Using Semaphores (Cont.) • Initialize S to 1 C_S and C_C to zero
Producer-Consumer with N Buffers program producer_consumer; const n=5; k=4;{k is n-1} var b: array[0..k] of integer; (* All monitor variables are global now *) in_ptr, out_ptr: integer; count: integer; m: integer; mon_mutex : semaphore := 1; (* Variables needed for emulation *) not_full_S : semaphore := 0; not_empty_S : semaphore := 0; not_full_C : integer := 0; not_empty_C : integer := 0 ; procedure append(i: integer); end; (* Monitor procedures are main procedures now *) procedure take(i: integer); end; procedure producer; ..... end; (* No change in processes *) procedure consumer; .... end; begin {main} in_ptr:=0; out_ptr:=0; for count:= 0 to k do b[count]:=-1; count:=0; cobegin producer; consumer coend; end.
Producer-Consumer with N Buffers (Cont.) procedure append(i: integer); Begin wait(mon_mutex); if count = n then begin { waitc(not_full)} not_full_C := not_full_C + 1; signal(mon_mutex); wait(not_full_S); wait(mon_mutex); not_full_C := not_full_C - 1; end; count:=count+1; b[in_ptr]:= i; in_ptr:= (in_ptr+1) mod n; for m:=0 to k do writeln('append:', b[m],' in_ptr',in_ptr); if not_empty_C > 0 then signalc(not_empty_S); signal(mon_mutex); end;
Producer-Consumer with N Buffers (Cont.) procedure take(i: integer); begin wait(mon_mutex); if count = 0 then begin {waitc(not_empty)} not_empty_C := not_empty_C + 1; signal(mon_mutex); wait(not_empty_S); wait(mon_mutex); not_empty_C := not_empty_C - 1; end; count:=count-1; i:= b[out_ptr]; writeln('i',i); out_ptr:= (out_ptr+1) mod n; for m:=0 to k do writeln('take:', b[m],'out_ptr',out_ptr); if not_full_C > 0 then signal(not_full_C); signal(mon_mutex); end;