This is the solution using 3 semaphores. Wondering if there's a way to do with fewer and if not, why not?
sem_t full; // # of filled slots
sem_t empty; // # of empty slots
sem_t mutex; // mutual exclusion
sem_init(&full, 0, 0);
sem_init(&empty, 0, N);
sem_init(&mutex, 0, 1);
Producer(){
sem_down(empty);
sem_down(&mutex);
// fill a slot
sem_up(&mutex);
sem_up(full);
}
Consumer() {
sem_down(full);
sem_down(&mutex);
// empty a slot
sem_up(&mutex);
sem_up(empty);
}
The only way to avoid the the mutex is if all operations inside the semaphore are atomic (can be rare). The mutex is there to make sure the dangerous bits happen one at a time and without being interrupted.
Imagine if you had two threads attempting to do something that depended on each other.
Thread 1: Add 3 to the ordered list
Thread 2: Add 4 to the ordered list
List: { 1, 2, 5, 7}
Thread 2 needs to see where thread 1 sticks his number before inserting his own. Therefore we use a mutex to make sure only one thread at a time tries to insert their number into the list.
The way it is written in your example (and others online) might make one believe the whole process needs to be inside the mutex. If you did this only one thread would be able to work at a time! In actuality you would do stuff that doesn't depend on other threads outside of the mutex but inside the semaphore (say generating a random number) and only guard the insertion of the number into the list.
semaphore(x)
long_expensive_computation
mutex(y)
order_critical_stuff
release(y)
release(x)