1.17k likes | 1.37k Views
Jonathan Walpole Computer Science Portland State University. CS 333 Introduction to Operating Systems Class 6 – Monitors and Message Passing. Monitors. It is difficult to produce correct programs using semaphores correct ordering of up and down operations is tricky!
E N D
Jonathan Walpole Computer Science Portland State University CS 333Introduction to Operating Systems Class 6 – Monitors and Message Passing
Monitors • It is difficult to produce correct programs using semaphores • correct ordering of up and down operations is tricky! • Can we get the compiler to generate the correct semaphore code for us? • what are suitable higher level abstractions for synchronization?
Monitors • Collect related shared objects together in a monitor • Characteristics • Local data variables are accessible only via the monitor’s procedures • Processes enter the monitor by invoking one of its procedures • Only one process may execute within the monitor at a given time • Condition variables (cv) • Wait(cv) – block on condition • Signal(cv) – wake up process waiting on cv
shared data monitor entry queue condition queues x y monitor operations initialization code Monitor structures
Monitor example for mutual exclusion process Producer begin loop <produce char “c”> BoundedBuffer.deposit(c) end loop end Producer monitor: BoundedBuffer var buffer : ...; nextIn, nextOut :... ; entry deposit(c: char) begin ... end entry remove(var c: char) begin ... end end BoundedBuffer process Consumer begin loop BoundedBuffer.remove(c) <consume char “c”> end loop end Consumer
Condition variables • Condition variables allow processes to synchronize based on some state of the monitor variables • For example: Buffer_full in the producer/consumer • Operations wait(cv) and signal(cv) allow synchronization within a monitor • Only one processor active at one time • what happens on a signal from A to B? • A waits for B to leave monitor, or waits for another condition • B waits for A to leave monitor, or waits for another condition • … and what happens on wait?
Monitor example monitor : BoundedBuffer var buffer : array[0..n-1] of char nextIn,nextOut : 0..n-1 := 0 fullCount : 0..n := 0 notEmpty, notFull : condition entry deposit(c:char) entry remove(var c: char) begin begin if (fullCount = n) then if (fullCount = n) then wait(notFull) wait(notEmpty) end if end if buffer[nextIn] := c c := buffer[nextOut] nextIn := nextIn+1 mod n nextOut := nextOut+1 mod n fullCount := fullCount+1 fullCount := fullCount-1 signal(notEmpty) signal(notFull) end deposit end remove end BoundedBuffer
Condition synchronization semantics • Signaling and signaled processes can not both run • What if signaling process continues to run and signals other condition variable? • What if signaled process continues to run and signals other condition variables? • What order should processes run? • How do you avoid deadlock?
Condition synchronization semantics • Hoare Semantics • On signal, allow signalled process to run; upon its exit from the monitor, signaller process continues • Stonger guarantees • Easier to prove correctness • Mesa Semantics (Xerox PARC) • Signal is merely a “hint” • Requires process to check that condition is ok to continue upon receipt of signal
Barriers • Use of a barrier • processes approaching a barrier • all processes but one blocked at barrier • last process arrives, all are let through
Summary • Process synchronization topics • Semaphores • IPC problems • Monitors • Reading • 2.3-2.4
Monitors • It is difficult to produce correct programs using semaphores • correct ordering of up and down is tricky! • avoiding deadlock is tricky! • boundary conditions are tricky! • Can we get the compiler to generate the correct semaphore code for us? • what are suitable higher level abstractions for synchronization?
Monitors • Collect related shared objects together in a monitor • Encapsulation and mutual exclusion • Local data variables are accessible only via the monitor’s procedures • Processes enter the monitor by invoking one of its procedures • Only one process may execute within the monitor at a given time • Condition variables (cv) • Wait(cv) – process blocked (queued) until condition holds • Signal(cv) – signals the condition and unblocks (dequeues) a process
shared data monitor entry queue condition queues x y monitor operations initialization code Monitor structures
Monitor example for mutual exclusion process Producer begin loop <produce char “c”> BoundedBuffer.deposit(c) end loop end Producer monitor: BoundedBuffer var buffer : ...; nextIn, nextOut :... ; entry deposit(c: char) begin ... end entry remove(var c: char) begin ... end end BoundedBuffer process Consumer begin loop BoundedBuffer.remove(c) <consume char “c”> end loop end Consumer
Monitor example with condition variables monitor : BoundedBuffer var buffer : array[0..n-1] of char nextIn,nextOut : 0..n-1 := 0 fullCount : 0..n := 0 notEmpty, notFull : condition entry deposit(c:char) entry remove(var c: char) begin begin if (fullCount = n) then if (fullCount = n) then wait(notFull) wait(notEmpty) end if end if buffer[nextIn] := c c := buffer[nextOut] nextIn := nextIn+1 mod n nextOut := nextOut+1 mod n fullCount := fullCount+1 fullCount := fullCount-1 signal(notEmpty) signal(notFull) end deposit end remove end BoundedBuffer
Monitor design choices • Condition variables introduce a problem for mutual exclusion • only one process active in the monitor at a time, so what to do when a process is unblocked on signal? • must not block holding the mutex, so what to do when a process blocks on wait? • Should signals be stored/remembered? • signals are not stored • if signal occurs before wait, signal is lost! • Should condition variables count?
Monitor design choices • Choices when A signals a condition that unblocks B • A waits for B to exit the monitor or blocks again • B waits for A to exit the monitor or block • Signal causes A to immediately exit the monitor or block (on what condition?) • Choices when A signals a condition that unblocks B & C • B is unblocked, but C remains blocked • C is unblocked, but B remains blocked • Choices when A calls wait and blocks • a new external process is allowed to enter • but which one?
Common monitor semantics • Hoare semantics • On signal, allow signaled process to run; upon its exit from the monitor, signaler process continues • Brinch Hansen semantics • signaler must immediately exit following signal
Message Passing • Interprocess communication • via shared memory • across machine boundaries • Message passing can be used locally or remotely for synchronization or general communication • processes use send and receive primitives • receive can block like wait • send unblocks a process blocked on receive (like signal unblocking a waiting process)
Design Choices for Message Passing • Mailboxes • system maintains a buffer of sent, but not yet received, messages • Rendezvous • sender and receiver must be active at the same time • receive must be blocked before send occurs • kernel does no buffering • when does the send return?
Barriers • Use of a barrier • processes approaching a barrier • all processes but one blocked at barrier • last process arrives, all are let through
Monitors (1) Example of a monitor
Monitors (2) • Outline of producer-consumer problem with monitors • only one monitor procedure active at one time • buffer has N slots
Monitors (3) Solution to producer-consumer problem in Java (part 1)
Monitors (4) Solution to producer-consumer problem in Java (part 2)
Semantics of monitors • What is the strongest statement we can make about the state of the monitor after a waiter wakes up? entry deposit(c:char) entry remove(var c: char) begin begin if (fullCount = n) then : : wait(notFull) c := buffer[nextOut] fullCount := fullCount-1 end if signal(notFull) : : end deposit end remove
Synchronization problems with Mesa P1 P2 P3 /* fullCount=n */ if (fullCount==n) wait(notFull); remove() ... fullCount--; signal(notFull); ... /* exit monitor */ /* fullCount=n-1*/ deposit() ... fullCount++; ... /* exit monitor */ ... fullCount++;
Mesa semantics monitor : BoundedBuffer var buffer : array[0..n-1] of char nextIn,nextOut : 0..n-1 := 0 fullCount : 0..n := 0 notEmpty, notFull : condition entry deposit(c:char) entry remove(var c: char) begin begin while (fullCount = n) then while (fullCount = n) then wait(notFull) wait(notEmpty) end while end while buffer[nextIn] := c c := buffer[nextOut] nextIn := nextIn+1 mod n nextOut := nextOut+1 mod n fullCount := fullCount+1 fullCount := fullCount-1 signal(notEmpty) signal(notFull) end deposit end remove end BoundedBuffer
Introduction to Operating Systems Chapter 2 (partial)Monitors, Reentrant Code, Message Passing, and Barriers
Introduction • It is difficult to produce correct programs • using locks and semaphores!!! • Correct ordering of Up and Down operations is tricky! • Desirable: • Language / compiler support for IPC • What are suitable high-level abstractions for synchronization?
Monitors • Collect related, shared objects together in a “monitor” • Characteristics: • • Local data variables are accessible only via the monitor’s procedures/methods • • Threads enter the monitor by invoking one of its procedures/methods • • Only one thread may execute within the monitor at a given time • “Condition Variables” (cv) • Wait(cv) – block on condition • Signal(cv) – wake up one thread waiting on cv
Monitor structures shared data monitor entry queue x condition variables y “entry” methods local methods initialization code
Monitor structures shared data monitor entry queue x condition variables y Local to monitor (Each has an associated list of waiting threads) List of threads waiting to enter the monitor “entry” methods Can be called from outside the monitor. Only one active at any moment. local methods initialization code
Example: The “Bounded-Buffer” Monitor monitor BoundedBuffer var buffer: ... nextIn, nextOut :... entry deposit(c: char) begin ... end entry remove() begin ... return c end endMonitor Producer Thread: while true -- Produce char “c” BoundedBuffer.deposit(c) endWhile Consumer Thread: while true c = BoundedBuffer.remove() -- Consume char “c” endWhile
The “BoundedBuffer” Monitor monitor BoundedBuffer var buffer: array[n] ofchar nextIn, nextOut: int = 0 cntFull: int = 0 notEmpty: Condition notFull: Condition entry deposit(c: char) ... entry remove() ... endMonitor nextOut nextIn A B D C 0 N-1 cntFull=4
Code for the “deposit” entry routine monitor BoundedBuffer var buffer: array[n] ofchar nextIn, nextOut: int = 0 cntFull: int = 0 notEmpty: Condition notFull: Condition entry deposit(c: char) if cntFull == N notFull.Wait() endIf buffer[nextIn] = c nextIn = (nextIn+1) mod N cntFull = cntFull + 1 notEmpty.Signal() endEntry entry remove() ... endMonitor nextOut nextIn A B D C 0 N-1 cntFull=4
Code for the “remove” entry routine monitor BoundedBuffer var buffer: array[n] ofchar nextIn, nextOut: int = 0 cntFull: int = 0 notEmpty: Condition notFull: Condition entry deposit(c: char) ... entry remove() if cntFull == 0 notEmpty.Wait() endIf c = buffer[nextOut] nextOut = (nextOut+1) mod N cntFull = cntFull - 1 notFull.Signal() endEntry endMonitor nextOut nextIn A B D C 0 N-1 cntFull=4
Condition Variables • “Condition variables allow processes to synchronize based on some state of the monitor variables.” • Examples from producer/consumer: • “Buffer-Full” condition • “Buffer-Empty” condition • Operations Wait(cv) and Signal(cv) • allow synchronization within the monitor • When a producer thread adds an element... • A consumer may be sleeping • Need to wake the consumer... Signal
Condition synchronization semantics • “Only one thread can be executing in the monitor • at any one time.” • Scenario: • Thread A is executing in the monitor. • Thread A does a Signal, waking up thread B. • What happens now? • Signaling and signaled threads can not both run!
Condition synchronization semantics • Option 1: Hoare Semantics • What happens when a Signal is performed? • The signaling thread (A) is suspended. • The signaled thread (B) wakes up and runs immediately. • B can assume the condition is now true/satisfied • • Stronger guarantees • • Easier to prove correctness • When B leaves monitor, then A can run. • After B leaves monitor... • A might resume execution immediately • ... or maybe another thread (C) will slip in!
Condition synchronization semantics • Option 2: MESA Semantics (Xerox PARC) • What happens when a Signal is performed? • • The signaling thread (A) continues. • • The signaled thread (B) waits. • When A leaves monitor, then B runs. • Issue: What happens when B waits? • When A leaves the monitor, • can some other thread (C) slip in first? • (Can some other thread (C) run • after A signals, but before B runs?) • • A signal is more like a hint. • • Requires B to recheck the state of the monitor variables • to see if it can proceed or must wait some more.
Code for the “deposit” entry routine monitor BoundedBuffer var buffer: array[n] ofchar nextIn, nextOut: int = 0 cntFull: int = 0 notEmpty: Condition notFull: Condition entry deposit(c: char) if cntFull == N notFull.Wait() endIf buffer[nextIn] = c nextIn = (nextIn+1) mod N cntFull = cntFull + 1 notEmpty.Signal() endEntry entry remove() ... endMonitor Hoare Semantics
Code for the “deposit” entry routine monitor BoundedBuffer var buffer: array[n] ofchar nextIn, nextOut: int = 0 cntFull: int = 0 notEmpty: Condition notFull: Condition entry deposit(c: char) while cntFull == N notFull.Wait() endWhile buffer[nextIn] = c nextIn = (nextIn+1) mod N cntFull = cntFull + 1 notEmpty.Signal() endEntry entry remove() ... endMonitor MESA Semantics
Code for the “remove” entry routine monitor BoundedBuffer var buffer: array[n] ofchar nextIn, nextOut: int = 0 cntFull: int = 0 notEmpty: Condition notFull: Condition entry deposit(c: char) ... entry remove() if cntFull == 0 notEmpty.Wait() endIf c = buffer[nextOut] nextOut = (nextOut+1) mod N cntFull = cntFull - 1 notFull.Signal() endEntry endMonitor Hoare Semantics
Code for the “remove” entry routine monitor BoundedBuffer var buffer: array[n] ofchar nextIn, nextOut: int = 0 cntFull: int = 0 notEmpty: Condition notFull: Condition entry deposit(c: char) ... entry remove() while cntFull == 0 notEmpty.Wait() endWhile c = buffer[nextOut] nextOut = (nextOut+1) mod N cntFull = cntFull - 1 notFull.Signal() endEntry endMonitor MESA Semantics
“Hoare Semantics” • What happens when a Signal is performed? • The signaling thread (A) is suspended. • The signaled thread (B) wakes up and runs immediately. • B can assume the condition is now true/satisfied • From the original Hoare Paper: • “No other thread can intervene [and enter the monitor] between the signal and the continuation of exactly one waiting thread.” • “If more than one thread is waiting on a condition, we postulate that the signal operation will reactivate the longest waiting thread. This gives a simple neutral queuing discipline which ensures that every waiting thread will eventually get its turn.”
Implementing Hoare Semantics • Implementation? • Thread A holds the monitor lock. • Thread A issues a Signal. • Thread B will be moved back to the ready queue. • Thread A must be suspended... • Possession of the monitor lock must be passed • from A to B. • When B finishes and gets ready to return... • The lock can be released. • Thread A must re-aquire the lock. • Perhaps A is blocked, waiting to re-aquire the lock.