Process Synchronization & the Critical-Section Problem

a monitor

Raw semaphores and condition variables are powerful but error-prone: forget one signal, swap a wait and a signal, leave out a release, and you get a subtle bug that may surface only months later. A monitor is a higher-level construct, usually built into the programming language, that packages the dangerous pieces together and enforces their correct use for you. The everyday picture is a private office with a receptionist: all access to the shared data goes through the monitor's procedures, and the receptionist guarantees that only one visitor is inside at a time. You no longer hand out the key yourself; the structure does it.

Concretely, a monitor bundles three things into one unit: the shared data, the procedures that operate on it, and an implicit mutex. The defining rule is that only one thread may be active inside the monitor at any moment — mutual exclusion is automatic for every monitor procedure, so you never write the lock and unlock by hand and never forget them. For waiting on conditions, a monitor provides condition variables with wait and signal: a thread that cannot proceed calls wait on a condition, which releases the monitor's mutex and sleeps, letting another thread enter; later a thread calls signal to wake it. Because entry and exit are handled by the language, whole classes of locking mistakes simply cannot be written.

One genuine subtlety is what happens at signal, since two threads now both want to be the single active one inside the monitor: the signaler and the just-woken waiter. Different monitor designs answer differently. Under signal-and-wait (Hoare semantics) the signaler steps aside and the waiter runs immediately. Under signal-and-continue (Mesa semantics, used by most real systems including Java and pthreads-style monitors) the signaler keeps running and the waiter only resumes later when it can re-acquire the monitor. The practical consequence of the common Mesa style is exactly the rule from condition variables: because the world may change between the signal and the waiter actually running, the waiter must re-check its condition in a while loop, never assume it is true just because it was signaled.

A bounded-buffer monitor exposes insert(item) and remove(): both are mutually exclusive automatically. insert does while (count == size) wait(notFull); ... signal(notEmpty). remove does while (count == 0) wait(notEmpty); ... signal(notFull). No hand-written lock/unlock appears anywhere — the monitor supplies it.

A monitor = shared data + procedures + automatic mutex + condition variables, all in one.

Most real monitors use Mesa (signal-and-continue) semantics, so a signaled thread is not guaranteed the condition still holds when it runs — re-check with while, exactly as with bare condition variables.

Also called
monitor construct監視器管程