The standard way Producer Consumer is implemented is like:
useQueuemutexemptyCountsemaphore of sizeNfullCountsemaphore of sizeN
produce:
down(emptyCount)
down(useQueue)
putItemIntoQueue(item)
up(useQueue)
up(fullCount)
consume:
down(fullCount)
down(useQueue)
item ← getItemFromQueue()
up(useQueue)
up(emptyCount)
Where if down has a non-positive value, the thread waits.
up pushes the count up
Taken from this Wikipedia article
Why can't we have something like:
class NewSemaphore {
int capacity, permits;
/**
* Initialize the semaphore with a max capacity
* @param n the max capacity
*/
NewSemaphore(int n) {
capacity = n;
permits = 0;
}
/**
* We usually never check this. Check if it's within limits.
* If not, wait
*/
synchronized void up() {
if (permits >= capacity) {
wait();
} else {
permits++;
notify();
}
}
/**
* Standard down/acquire function
*/
synchronized void down() {
if (permits <= 0) {
wait();
} else {
permits--;
notify();
}
}
}
This will be called like:
produce:
up(mySemaphore)
down(useQueue)
putItemIntoQueue(item)
up(useQueue)
consume:
down(mySemaphore)
down(useQueue)
item ← getItemFromQueue()
up(useQueue)
Why do we need 2 different variables emptyCount and fullCount?