Skip to content

Monitors


OSTEP: Chapters 28-30

Monitors are a high-level data abstraction tool combining three features:

  • Shared data.

  • Operations on the data.

  • Synchronization.

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.

  1. Monitor lock: There is a lock associated with each monitor. Mutual exclusion is implicit, so you get a lock on entry to any function/method, and an unlock on exit. This synchronization is automatically done by the compiler (i.e., the calls are inserted into the code by the compiler), and the programmer does not seem them. They come for free when the programmer declares a module to be a monitor (or, in Java, when methods are declared to be synchronized. The locking means that every monitor function/method is a critical section.

This enforcement of at most one process at a time in a monitor is called the monitor invariant.

  1. Condition variables: If there in a condition inside a monitor which would require a process to block (such as when a queue is full), then the process can call the wait function/method on a condition variable. When a process blocks on a condition variable, it also implicitly releases the monitor lock. When a process wants to wake up a process that is waiting on a condition variables, it does a notify (also called signal or wakeup in some systems) on the condition variable.

We will talk about wo types of condition variables: (1) classic Hoare condition variables and (2) Java condition variables.

  1. Hoare condition variables:

  2. condition.wait(): release monitor lock, put process to sleep. When process wakes up again, re-acquire monitor lock immediately.

  3. condition.notify(): wake up one process waiting on the condition variable (FIFO). If no process is waiting then do nothing.

  4. condition.broadcast(): wake up all processes waiting on the condition variable. If no process is waiting then do nothing.

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.

  1. Java condition variables:
  2. wait(): release monitor lock on current object; put thread to sleep.

  3. notify(): wake up one process waiting on the condition; this process will try to reacquire the monitor lock. If no process is waiting then do nothing.

  4. notifyall(): wake up all processes waiting on the condition; each process will try to reacquire the monitor lock. (Of course, only one at a time will acquire the lock.) If no process is waiting then do nothing.

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:

  • Was not present in very many languages, but extremely useful. Java made monitors much more popular and well known.

  • Semaphores use a single structure for both exclusion and scheduling, monitors use different structures for each.

  • A mechanism similar to wait/notify is used internally to Unix for scheduling OS processes.

  • Monitors are more than just a synchronization mechanism. Basing an operating system on them is an important decision about the structure of the entire system.


Copyright © 2011, 2018, 2020 Barton P. Miller

Non-University of Wisconsin students and teachers are welcome to print these notes their personal use. Further reproduction requires permission of the author.