A quick word about semaphores. Unless you need reference a semaphore acrosss multiple processes I highly recommend using sem_init over sem_open. sem_open creates semaphores that can persist beyond the life of your process if you don’t properly close them. Say if you ctrl+c to stop a deadlocked program.

C code for a producer consumer buffer using mutexes and semaphores for synchronization.

Note that all the code is valid I just haven’t included the struct used for passing around the semaphores, mutex, and buffer as that will be specific to your buffer.

The approach is thoroughly described in the code comments.

#include <semaphore.h>
#include <fcntl.h> // For O_CREAT flag


int main() {

    // You may only modify the buffer (not pictured) if you lock this mutex
    pthread_mutex_t mutex;
    pthread_cond_t space_avail; // Waited on by producers when the buffer is full
    pthread_cond_t items_ready; // Waited on by consumers when the buffer is empty

    // Init conditionals and mutex and checkf or failures
    if (pthread_cond_init(items_ready, NULL)) { return 0; }
    if (pthread_cond_init(space_avail, NULL)) { return 0; }
    if (pthread_mutex_init(mutex, NULL) != 0) {  return 0; }

    // Maybe make some threads here and call push n pull below
    return 0;

}

// Producer
int produce(/* mutex, empty, full and your args */) {

    // Lock the mutex to gain exclusive access to the buffer
    pthread_mutex_lock(mutex);
    
    // If you can't push because the buffer is full cond_wait.
    // This releases the mutex until a consumer signals space_avail 
    while (!can_push(buffer))  {
        pthread_cond_wait(space_avail, mutex);
    }
    
    // PUSH AN ITEM

    // After pushing singal items_ready so one consumer waiting 
    // on that conditional variable can wake up.
    pthread_cond_signal(items_ready);

    // We're done with the buffer so release the mutex
    pthread_mutex_unlock(mutex);

    retunr 0
}

// Consumer
int consume(/* mutex, empty, full and your args */) {
    // Get exclsuvie access to the buffer
    pthread_mutex_lock(mutex);

    // If the buffer is empty (i.e. we can't pull) call cond_wait
    // on items_ready. This will release the mutex in arg2 and 
    // wait to be signaled by a producer 
    while (!can_pull(buffer)) {
        pthread_cond_wait(items_ready, mutex);
    }

    // PULL AN ITEM HERE

    // After pulling an item there is space_available in the buffer
    // so singal a producer
    pthread_cond_signal(space_avail);

    // Relase control of the buffer
    pthread_mutex_unlock(mutex);

    return 0;
}