General Model of Monitor:
When an active task signals blocked task in conditions
, it moves the task from conditions
to signalled
and the active task goes to signaller
to empty the monitor.
The difference between monitors from different languages is once the monitor is empty, which is going to be pulled first from calling
, signalled
, signaller
.
calling (C), signalled (W), signaller(S)
C = W
or C = S
may result in barging, but with barging avoidance implementations, it can prevent starvation.calling
has greater priority than any one of signalled
or signaller
, it may cause starvation because calling
has unbounded incoming tasks.Monitor either have an explicit signal - statement - or an implicit signal - automatic signal.
Implicit signal has no condition variable or explicit signal statement.
But it has a waitUntil
statement
_Monitor BoundedBuffer{
int front=0, back=0, count=0;
int elements[20];
public:
_Nomutex int query() const {return count;}
void insert (int elem){
waitUntil count != 20; // not in uC++
elements[back] = elem;
back = (back+1)%20;
count += 1;
}
void remove(){
waitUntil count != 0;
elements[front] = elem;
back = (front+1)%20;
count -= 1;
return elem;
}
}
This is not efficient because we are not doing any cooperation. We are leaving everything to the monitor to figure out.
** Assignment note: there are 3 textbook solutions for implementing waitUntil
by your self
first two are explicit signaling and 3rd is implicit.
_Mutex _Coroutine
= _Cormonitor
_Cormonitor{
void main(){
... suspend() ...
... suspend() ...
}
public:
void m1(...){... resume(); ...} // mutual exclusion
void m2(...){... resume(); ...} // mutual exclusion
... // destructor is ALWAYS mutex
};
This allows a coroutine from being accessd from multiple threads.
Java has synchronized class members - _Mutex members but incorrectly named
Java synchronized methods have 3 methods
wait();
notify();
notifyAll();
you don't know the name of the condition variable, neither it gives you.
class Buffer{
// buffer declarations
private int count = 0;
public synchronized void insert (int elem) {
while(count == Size) wait(); // busy waiting
//add to buffer
copunt += 1;
if (count == 1) notifyAll();
}
public synchronized int remove() {
while (count == 0) wait(); // busy waiting
// remove from buffer
count -= 1;
if (count == Size -1) notifyAll();
return elem;
}
}
Java monitor is of type non-priority non-blocking monitor.
So it wakes C = W < S
There does exists barging, so the threads must busy wait.
class Barrier {
private int N, count = 0;
public Barrier(int N){this.N = N;}
public synchronized void block(){
count += 1;
if (count < N){
try{wait();}
catch(InterruptedException e){}
}else{
notifyAll();
}
count -= 1;
}
}
This does not work because of barging.
If the Barrier race was in a loop, lets say 15 runs,
and finish all 15 runs before other tasks even complets their first run.
SO how do we solve this?
Attempt 1: instead of
}else{
notifyAll();
}
count -= 1;
do this
}else{
count = 0;
notifyAll();
}
This does not work because of spurious wakeup
This means A WAITING THREAD CAN JUST WOKEN UP AT RANDOM - this typically happens due to barging. A thread gets woken up but before it run, another thread runs in and changes the condition to a invalid condition.
So...
Attempt 2: lets put wait clause in a loop with a ticket of barrier run number
class Barrier {
private int N, count = 0, generation = 0;
public Barrier(int N){this.N = N;}
public synchronized void block(){
int mygen = generation;
count += 1;
if (count < N){
while(mygen == generation){
try{wait();}
catch(InterruptedException e){}
}
}else{
count = 0;
generation += 1;
notifyAll();
}
}
}
The loop waiting is a kind of barging avoidence.
What if we just make condition variables?
This does not work because when it calls Condition::Wait(), it just ran into a nested-monitor problem