CAR Hoare wrote an article on monitors back when. http://i.stanford.edu/pub/cstr/reports/cs/tr/73/401/CS-TR-73-401.pdf in which Hoare develops "Brinch-Hansen's concept of a monitor".
In page 4 of http://pages.cs.wisc.edu/~remzi/OSTEP/threads-monitors.pdf, the authors write that there is a difference between theory and practice in practice. This book section talks about how Mesa semantics subtly but critically changed signal() so it was just a hint which changed the state of a thread from blocked state to runnable state but not run it immediately. We implement the Mesa semantics (as opposed to Hoare semantics) in which we almost always put the wait() inside a while loop.
public:
void produce(int element)
{
while (fullEntries == MAX) // line P0 (CHANGED IF->WHILE)
wait(&empty); // line P1
buffer[fill] = element; // line P2
fill = (fill + 1) % MAX; // line P3
fullEntries++; // line P4
signal(&full); // line P5
}
int consume()
{
while (fullEntries == 0) // line C0 (CHANGED IF->WHILE)
wait(&full); // line C1
int tmp = buffer[use]; // line C2
use = (use + 1) % MAX; // line C3
fullEntries--; // line C4
signal(&empty); // line C5
return tmp; // line C6
}
Figure D.5: Producer/Consumer with Monitors and Mesa Semantics
void produce(int element)
{
while (fullEntries == MAX) // line P0 (CHANGED IF->WHILE)
wait(&empty); // line P1
buffer[fill] = element; // line P2
fill = (fill + 1) % MAX; // line P3
fullEntries++; // line P4
signal(&full); // line P5
}
int consume()
{
while (fullEntries == 0) // line C0 (CHANGED IF->WHILE)
wait(&full); // line C1
int tmp = buffer[use]; // line C2
use = (use + 1) % MAX; // line C3
fullEntries--; // line C4
signal(&empty); // line C5
return tmp; // line C6
}
Figure D.5: Producer/Consumer with Monitors and Mesa Semantics
The gist is this:
Thus, if you remember nothing else at all from this class, you can just remember: always recheck the condition after being woken! Put in even simpler terms, use while loops and not if statements when checking conditions.