OSTEP: Chapters 28-30
Monitors are a high-level data abstraction tool combining three features:
Monitors are based on the having synchronization integrated into the programming language. So, the compiler is working together with the operating system to provide an easier-to-use, higher level interface to synchronization. Monitors are considered a higher-level concept than semaphores with P and V, so they should be easier and safer to use.
The most widely used implementation of monitors is in the Java programming language from Sun (now Oracle) Previously, there was an excellent implmentation in the Mesa language from Xerox.
Monitors provide sychronization in two ways: (1) a monitor lock that automatically provides locking on entry to each monitor function/method and (2) condition variables that provide the ability for a process to block when needed in a monitor.
This enforcement of at most one process at a time in a monitor is called the monitor invariant.
We will talk about wo types of condition variables: (1) classic Hoare condition variables and (2) Java condition variables.
Here's a link to
Tony Hoare's seminal paper on monitors:
C.A.R. Hoare,
Monitors:
An Operating System Structuring Concept,
Communications of the ACM 17, 10, October 1974,
pp. 549-557.
Let's look at an example of synchronization with monitors: implement the clasic producer/consumer problem.
The "classic" Hoare-style monitor:
monitor QueueHandler { private: const int QSIZE = 100; int first; int last; int buffer[QSIZE]; condition full; condition empty; int ModIncr(int v) { return (v+1)%BUFFSIZE; } public: void QueueHandler (int); void Enqueue (Thing *); Thing *Dequeue (); }; void QueueHandler::QueueHandler (int val) { first = last = 0; } void QueueHandler::AddToQueue (Thing *item) { { while (ModIncr(last) == first) { full.wait(); } buffer[last] = item; last = ModIncr(last); empty.notify(); } Thing * QueueHandler::RemoveFromQueue (); { while (first == last) { empty.wait(); } Thing *ret = buffer[first]; first = ModIncr(first); full.notify(); return ret; } |
Java only allows one condition variable (implicit) per object. Here is the same solution in Java:
class QueueHandler { final static int QSIZE = 100; private int first; private int last; private int buffer[QSIZE]; private int ModIncr(int v) { return (v+1)%QSIZE; } public QueueHandler (int val) { first = last = 0; } public synchronized void AddToQueue (Thing item) { { while (ModIncr(last) == first) { try { wait(); } catch (InterruptedException e) {} } buffer[last] = item; last = ModIncr(last); notify(); } public synchronized Thing RemoveFromQueue (); { while (first == last) { try { wait(); } catch (InterruptedException e) {} } Thing ret = buffer[first]; first = ModIncr(first); notify(); return ret; } |
Show how wait and notify solve the semaphore implementation problem. Mention that they can be used to implement any scheduling mechanism at all. How do wait and notify compare to P and V?
Do the readers and writers problem with monitors.
Summary: