Monitor Types
Monitors differ by the relative priority assigned to three queues, Calling (entry), Waiting (signalled), and Signaller, after a task waits, signals, or exits. Different priorities yield different semantics: barging vs. no-barging, usable vs. rejected, blocking vs. nonblocking signal.
Why does the priority ordering matter?
Because it determines whether a newly-arrived task can barge past a signalled task. If
C < W(calling strictly lower priority than signalled/waiting), no barging, the signalled task runs before any new caller. IfW = C, new callers can race in and grab the state the signalled task was promised. Barging βwhileloops around everywait, because the predicate may be false again when you wake.
The priority table (Buhr)
| Relative priority | Notes | |
|---|---|---|
| 1 | C < W < S | Useful, has prevention (no barging) |
| 2 | C < S < W | No barging |
| 3 | C = W < S | Usable, needs avoidance |
| 4 | C = S < W | Barging; starvation without avoidance |
| 5β6 | C = W = S, C < W = S | Rejected, confusing, arbitrary selection |
| 7β13 | anything with C β₯ W, S | Rejected, unsound, uncontrolled barging, unpreventable starvation |
Four canonical monitor types
| Signal type | Priority (no barging) | No priority (barging) |
|---|---|---|
| Blocking (signaller blocks) | Priority Blocking (Hoare), C < S < W, uC++ signalBlock | No Priority Blocking, C = S < W |
| Nonblocking (signaller continues) | Priority Nonblocking, C < W < S, uC++ signal | No Priority Nonblocking, C = W < S, Java / C# |
Implicit Signal (waitUntil) | Priority Implicit C < W | No Priority Implicit C = W |
uC++ default is priority nonblocking (signal): no barging + signaller runs to exit/wait before signalled runs. Java/C# are no-priority nonblocking β barging β while ( !pred ) wait(); is mandatory.
General Monitor Model (Buhr Β§8.10)
Every monitor has three queues that the implicit scheduler picks from when a task waits or exits:
βββ calling (C) βββ
β β
condition vars βββΊ mutex object ββββΊ signalled (W)
β β
βββ signaller (S) β
β
βΌ exit
active task
- Explicit scheduling:
_Acceptpushes acceptor onto A/S stack and picks from mutex-member queues;signalmoves a task from its condition to W. - Implicit scheduling: after a wait/exit, monitor picks from A/S first, then entry queue (C).
Assigning different relative priorities to C, W, S yields all 13 theoretical monitors in the table below; only a handful are practically useful.
Implicit signal (waitUntil)
No condition variables, no explicit signal. A task writes waitUntil expr;. Runtime re-evaluates all waitersβ expressions on every monitor exit and wakes any whose predicate now holds.
_Monitor BoundedBuffer {
int count = 0;
public:
void insert( int elem ) {
waitUntil count != 20; // not in uC++; prototype semantics
// ...
}
};Good for prototyping; poor performance (re-evaluate N predicates per exit).
Immediate-return signal
Restricted: signal β return, signaller must immediately leave the monitor. Optimizes the most common case (signal-before-return) but canβt express dating-service handoff.
Coroutine monitor (_Cormonitor)
A _Coroutine with monitor-style mutual exclusion on calls, lets a coroutine print-formatter, cache, etc., be safely used by multiple threads.