In classical producer-consumer problem, we have producers wait when the shared buffer is full and consumers wait when the shared buffer is empty. We have two posix threads, one producer and other worker, synchroinizing with one another using sem_wait and sem_post for the empty semaphore and full semaphore (both counting semaphore). Thus, the semaphore implementation of the producer-consumer code looks like as below:
procedure producer() {
while (true) {
item = produceItem();
down(emptyCount);
down(buffer_mutex);
putItemIntoBuffer(item);
up(buffer_mutex);
up(fillCount);
}
}
procedure consumer() {
while (true) {
down(fillCount);
down(buffer_mutex);
item = removeItemFromBuffer();
up(buffer_mutex);
up(emptyCount);
consumeItem(item);
}
}
Presuming producer() and consumer() are running in independent concurrent threads, what happens when produceItem
or consumeItem
faces a run-time exception causing the thread to handle the exception gracefully so that both the threads can gracefully come out?
Where to put the try-catch to handle the situation well?
I do most of my multithreading using Ada. The following Ada example shows how applying a timeout to the wait for a condition variable allows the consumer to handle a non-responsive producer, and the producer to handle a non-responsive consumer.
Ada protected objects, such as Buffer in the example above, provide automatic mutual exclusion. In the example above the Buffer object has two entries, Put and Get. The Put entry can only be executed when the Buffer internal variable Is_New is False. The Get entry can only be executed when the Buffer internal variable Is_New is True.
The Producer task (similar to a thread in C++) contains an outer loop that sets the variable "value" to first 1, then 2, and so on up to 15. The inner loop tries to Put the value in the Buffer up to Wait_Limit times. Each time the Producer sets a timer for one half second, and then tries again if not successful. If the producer fails Wait_Limit times it writes an error message and terminates.
The consumer behavior is similar to the producer. It reads values from Buffer by calling the Get entry. It also waits one half second for each try, and terminates after Wait_Limit successive failures to read a value from Buffer.
The output of this program is: