Internal Scheduling
Internal scheduling schedules tasks inside a monitor/task using condition variables, uC++ uCondition. A task waits on a condition to block (atomically releasing the monitor lock); another task signals to wake the front of the queue, or signalBlock to wake and block itself. Unlike [[notes/External Scheduling|external _Accept]], internal scheduling can condition on any state, including member arguments.
Why both signal and signalBlock?
Because the signaller might still hold live state in the monitor.
signal(uC++ default) makes the signalled task ready but the signaller continues; the signalled task runs only after the signaller exits or waits.signalBlock(Hoare-style) immediately swaps: signaller blocks, signalled runs. UsesignalBlockwhen the signaller is done and wants the signalled task to see state right now.
Interface (uCondition)
uCondition c; // external synchronization-lock = queue of waiters
c.wait(); // atomically release monitor lock; block; re-acquire on wake
c.signal(); // wake front of queue; signalled runs after signaller exits/waits
c.signalBlock(); // wake front; signaller blocks, signalled runs now
bool c.empty(); // true if no waiters
int c.front(); // integer stored with the front waiter (for priority)
c.wait( N ); // wait, storing N with this waiterBounded buffer (internal)
_Monitor BoundedBuffer {
uCondition full, empty;
int front = 0, back = 0, count = 0;
int elements[20];
public:
_Nomutex int query() const { return count; }
void insert( int elem ) {
if ( count == 20 ) empty.wait(); // wait for a free slot
elements[back] = elem; back = (back + 1) % 20; count += 1;
full.signal(); // wake a waiting consumer
}
int remove() {
if ( count == 0 ) full.wait(); // wait for an item
int elem = elements[front]; front = (front + 1) % 20; count -= 1;
empty.signal(); // wake a waiting producer
return elem;
}
};Rules
- Wait releases the monitor lock atomically with blocking.
- Signal on empty queue is lost (unlike
Von a semaphore, which is remembered). - No barging in uC++, the signalled task runs before any newly-arrived caller enters the monitor (priority blocking/nonblocking; see Monitor Types).
- Signaller does not block on
signal, signalled waits until signaller exits orwaits. UsesignalBlockto swap immediately.
Shadow queue (CS343 Solution 4)
uCondition stores an integer with each waiter (wait(N) + front()), enabling temporal-order reader-writer: record READER/WRITER at arrival; release cascade checks the front’s kind.
Dating Service, why internal beats external (Buhr §8.4.2)
Canonical case where external scheduling can’t reach: matches depend on member argument (ccode). _Accept can pick a member but can’t inspect its arguments, so you need per-code condition queues:
_Monitor DatingService {
enum { CCodes = 20 };
uCondition girls[CCodes], boys[CCodes], exchange;
int girlPhoneNo, boyPhoneNo;
public:
int girl( int phoneNo, int ccode ) {
if ( boys[ccode].empty() ) { // no compatible boy ?
girls[ccode].wait(); // wait for a boy
girlPhoneNo = phoneNo; // expose phone to the signaller
exchange.signal(); // wake boy from chair
} else {
girlPhoneNo = phoneNo;
boys[ccode].signal(); // wake the waiting boy
exchange.wait(); // sit in chair for boy's number
}
return boyPhoneNo;
}
int boy( int phoneNo, int ccode ) { /* symmetric */ }
};Key trick: the signaller deposits its phone number in the shared variable before signal, and the signalled task reads it after being woken. The exchange condition is a single-slot “chair” for the rendezvous.