Monitor

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. If W = C, new callers can race in and grab the state the signalled task was promised. Barging β‡’ while loops around every wait, because the predicate may be false again when you wake.

The priority table (Buhr)

Relative priorityNotes
1C < W < SUseful, has prevention (no barging)
2C < S < WNo barging
3C = W < SUsable, needs avoidance
4C = S < WBarging; starvation without avoidance
5–6C = W = S, C < W = SRejected, confusing, arbitrary selection
7–13anything with C β‰₯ W, SRejected, unsound, uncontrolled barging, unpreventable starvation

Four canonical monitor types

Signal typePriority (no barging)No priority (barging)
Blocking (signaller blocks)Priority Blocking (Hoare), C < S < W, uC++ signalBlockNo Priority Blocking, C = S < W
Nonblocking (signaller continues)Priority Nonblocking, C < W < S, uC++ signalNo Priority Nonblocking, C = W < S, Java / C#
Implicit Signal (waitUntil)Priority Implicit C < WNo 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: _Accept pushes acceptor onto A/S stack and picks from mutex-member queues; signal moves 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.