CS 537 Notes, Section #8: Monitors


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.

  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.

  2. 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:

    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.

  2. Java condition variables:

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:



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.