Monitor

Nested Monitor Problem

Nested monitor problem (aka lock composition): a task holds monitor M₁’s lock, calls into monitor M₂, and waits on a condition inside M₂. wait releases M₂’s lock, but not M₁’s. Anyone needing M₁ to signal the condition now deadlocks.

Why doesn't wait release all locks?

Because wait is a property of one condition variable bound to one monitor; it doesn’t know about outer monitors. Releasing “all locks” is attractive but unsafe, you might drop a lock an intervening caller needs acquired in a specific order, violating the locking protocol. Same issue exists for composed mutex locks generally, hence “lock composition problem.”

Canonical sketch and timing

                  M1          M2
T0   acquire ─────┐
                  │  acquire ─┐
                  │           │  block (wait)
                  │           │  release M2  ← only M2 released!
                  │           │
                  │  (M1 still held — potential deadlock)

wait only knows about the condition’s monitor (M2). It cannot release M1, because (a) the wait API is tied to one condition/lock, and (b) “release all locks” can break outer locking protocols an unrelated caller relies on. This is the lock composition problem in general, the same issue appears whenever you hold lock A and block on lock B.

Uses, guardian lock for R/W

Nested monitors used deliberately as a guardian: outer monitor serializes writers, inner monitor counts readers. Writer calls outer → inner’s startEndWrite waits on reader count; outer monitor lock prevents concurrent writers, inner monitor protects reader count.

_Monitor RW {
    _Monitor RWN {
        uCondition bench;
        int rcnt = 0;
    public:
        void startRead() { rcnt += 1; }
        void endRead()   { rcnt -= 1; if ( rcnt == 0 ) bench.signal(); }
        void startEndWrite() {
            if ( rcnt > 0 ) bench.wait();
            // sequential write
        }
    } rwn;
    _Mutex void mutexRead() { rwn.startRead(); }
public:
    void write() { rwn.startEndWrite(); }
    _Nomutex void read() {
        mutexRead();                            // acquire outer, then inner-read
        // concurrent reads
        rwn.endRead();                          // let readers out
    }
};

If the writer waits in rwn, new readers/writers cannot acquire rw, FIFO + no starvation by construction.