Monitors were developed in the 1970s to make it easier to avoid deadlocks.
A monitor is a collection of procedures, variables, and
data structures grouped together.
Processes can call the monitor procedures but cannot access the
internal data structures.
Only one process at a time may be be active in a monitor.
Active in a monitor means in ready queue or CPU with the program counter
somewhere in a monitor method.
A monitor is a language construct.
Compare this with semaphores, which are usually an OS construct.
The compiler usually enforces mutual exclusion.
Condition variables allow for blocking and unblocking.
cv.wait() blocks a process.
The process is said to be waiting for (or waiting on) the
condition variable cv.
Usually this is implemented by putting the process in a queue of
processes waiting for the condition variable
(similar to processes waiting for I/O to complete).
cv.signal() (also called cv.notify)
unblocks a process waiting for the
condition variable cv.
When this occurs, we need to still require that only one process
is active in the monitor. This can be done in several ways,
but the usual way is to move the newly unblocked process from
the queue waiting for the condition variable to a queue waiting
for the monitor to become free.
If a condition variable is signaled with nobody waiting,
the signal is lost.
Compare this with semaphores, in which a signal will allow a process
that executes a wait in the future to not block.
You should not think of a condition variable as a variable in the
traditional sense.
It does not have a value.
Think of it as an object in the OOP sense.
It has two methods, wait and signal that manipulate
the calling process.
An example: Dining Philosophers
5 philosophers sit at a round table with 5 plates of rice and 5 chopsticks.
2 chopsticks are require to eat.
Each philosopher is in one of the states:
Thinking
Hungry
Eating
Dining Philosophers
Look at code for almost solving this using monitor pseudocode.
Note that this "solution" allows starvation.
Monitor Implementation
Monitors are implemented by using queues to keep track of the processes
attempting to become active int he monitor.
To be active, a monitor must obtain a lock to allow it to execute the
monitor code.
Processes that are blocked are put in a queue of processes waiting for an
unblocking event to occur.
These are the queues that might be used:
The entry queue contains processes attempting to call a monitor
procedure from outside the monitor.
Each monitor has one entry queue.
The signaller queue contains processes processes that have executed
a notify operation.
Each monitor has at most one signaller queue.
In some implementations, a notify leaves the process active and no
signaller queue is needed.
The waiting queue contains processes that have been awakened by
a notify operation.
Each monitor has one waiting queue.
Condition variable queues contain processes that have executed
a condition variable wait operation.
There is one such queue for each condition variable.
The relative priorities of these queues determines the operation of the
monitor implementation.
The queues associated with a monitor that does not have a signaller queue.
The lock becomes available when the active process executes a wait or leaves
the monitor.