Monitor (OS)
Monitor is a software module that uses condition variables for signalling.
- Unused signals are lost (!= semaphores)
Chief characteristics
- Local data variables are accessible only by the monitor (⇒ shared data in monitor is “safe”)
- Process enters monitor by invoking one of its procedures (⇒ controlled entry)
- Only one process may be executing in the monitor at a time (⇒ mutex)

uC++ _Monitor (CS343)
uC++ provides a monitor keyword, _Monitor, that combines shared data with implicit mutual exclusion on its public members. Each monitor has an internal mutex lock; member entry Ps it, exit Vs it.
_Monitor AtomicCounter {
int counter; // shared data
public:
AtomicCounter( int init = 0 ) : counter( init ) {}
int inc() { counter += 1; return counter; } // implicit mutex member
int dec() { counter -= 1; return counter; }
};
AtomicCounter a, b{1}, c{3}; // accessed by multiple threads_Mutexqualifier, explicitly mark a non-public member as mutex_Nomutexqualifier, remove implicit mutex (e.g., const getters that don’t need serialization)- Recursive entry allowed, one mutex member can call another (or itself) on the same owner
- Destructor is always mutex, ending a scope with a monitor, or
deleteing one, blocks until the thread leaves - Unhandled exceptions release the lock, the monitor remains usable after an exception propagates out
How _Monitor is implemented (Buhr §8.2)
Every _Monitor is compiled to a plain class with one owner mutex lock and three runtime-managed queues. Each public (mutex) member is wrapped in implicit acquire / release:
class Mon { // what _Monitor expands to
MutexLock mlock; // implicit monitor lock
int v; // shared data
public:
int x( ... ) { // mutex member
mlock.acquire(); // P on entry (may block in calling queue)
// ... body ...
mlock.release(); // V on exit
return v;
}
};Three queues the runtime maintains for every monitor:
- Calling (C), tasks that reached
acquirebut the owner lock was held - Signalled (W), tasks woken by
uCondition::signal, waiting to re-enter - Signaller (S) / acceptor stack, tasks that called
signal/_Acceptand are themselves suspended until their action completes
The relative priority among C/W/S is the single design choice that decides barging, starvation, and Hoare-vs-Mesa semantics, see Monitor Types for the full table.
_Acceptis scheduling on the queue set
_Accept( member )pushes the current task onto the acceptor stack and tells the scheduler “release the monitor lock, but only accept a call tomembernext, not any arrival in C.” When the accepted call exits (or waits on a condition), the acceptor is resumed. Nested_Accepts form the stack of blocked acceptors, each waits for its callee to exit before the one below it resumes.
uConditionis a plain waiter queue, not kernel-managedA
uConditionis just a queue ofuBaseTask *.wait()atomically enqueues the current task and releasesmlock(viayieldNoSchedule, the same primitive used by baton passing);signal()dequeues a waiter and moves it onto the signalled queue W. No kernel calls, all of this is coroutine-style context switching inside the uC++ runtime.
Scheduling inside a monitor
Two techniques:
- External scheduling via
_Accept, monitor chooses which member to accept next. - Internal scheduling via
uCondition::wait/signal/signalBlock.
Classification by queue priority (C/W/S) ⇒ see Monitor Types.