Search code examples
cprocesssemaphoreproducer-consumer

Producer consumer semaphore value is not correct


I'm trying to implement producer-consumer problem in C using processes and System V IPC and I'm stuck on one thing. This is early version of my code (without implementing queue operations or even producer and consumer executing in loop) that I was using to learn and test how semaphores work:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <errno.h>
#include <time.h>
#include "sem.h"
#include "shm.h"

#define BUFFER_SIZE 9

int full;
int empty;
int mutex;

struct buff {
    int queue[BUFFER_SIZE];
} *buffer;

void producer();
void consumer();

int main() {

    int parent_pid, pid, i; 

    parent_pid = getpid();

    int shmid = allocate_shared_memory(sizeof(*buffer));         
    buffer = (struct buff *) attach_shared_memory(shmid);

    for (i = 0; i < BUFFER_SIZE; i++) {
        buffer->queue[i] = 0;
    }

    full = sem_allocate();
    empty = sem_allocate();
    mutex = sem_allocate();


    printf("Full %d\n", full);
    printf("Empty %d\n", empty);
    printf("Mutex %d\n", mutex);


    sem_init(full, 0);
    sem_init(empty, BUFFER_SIZE);
    sem_init(mutex, 1);


    printf("Full value %d\n", sem_get_val(full));
    printf("Empty value %d\n", sem_get_val(empty));
    printf("Mutex value %d\n", sem_get_val(mutex));


    srand(time(0));

    pid = fork();
    if (!pid) {
        printf("Producer here: %d\n", getpid());
        producer();
        printf("Full value after prod() %d\n", sem_get_val(full));
        return 0;
    } else printf("Created new producent: %d\n", pid);

    sleep(1);

    pid = fork();
    if (!pid) {
        printf("Consumer here: %d\n", getpid());
        printf("Full value before cons() %d\n", sem_get_val(full)); //here I always get 0
        consumer();
        return 0;
    } else printf("Created new consumer: %d\n", pid);

       while (1)
    {
        int status;
        pid_t done = wait(&status);
        if (done == -1)
        {
            if (errno == ECHILD) break; // no more child processes
        }
        else
        {
            if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
                exit(1);
            }
        }
    }

    if (getpid() == parent_pid) {
        sem_deallocate(full);
        sem_deallocate(empty);
        sem_deallocate(mutex);
    }
}

void producer() {

    sem_wait(empty);
    sem_wait(mutex);
    printf("Producer is producing!\n");
    buffer->queue[0]=0;
    sem_post(mutex);
    sem_post(full);
}


void consumer() {

    sem_wait(full);
    sem_wait(mutex);
    printf("Consumer is consuming!\n");
    sem_post(mutex);
    sem_post(empty);

}

int sem_allocate() {
    return semget(IPC_PRIVATE, 1, IPC_CREAT | 0666);
}

void sem_deallocate(int semid) {

        if (semctl(semid, 0, IPC_RMID, NULL) == -1)
    {
        perror("Error releasing semaphore!\n");
        exit(EXIT_FAILURE);
    }
}

int sem_init(int semid, int value) {
    union semun arg;
    arg.val = value;
    if (semctl(semid, 0, SETVAL, arg) == -1) {
        perror("semctl");
        return -1;
    }else return 1;
}

int sem_wait(int semid) {
    printf("Someone is waiting %d\n", semid);
    struct sembuf sem = { 0, -1, SEM_UNDO };
    return semop(semid, &sem, 1);
}

int sem_post(int semid) {
    printf("Someone is posting %d\n", semid);
    struct sembuf sem = { 0, 1, SEM_UNDO };
    return semop(semid, &sem, 1);
}

int sem_get_val(int semid) {
    return semctl(semid, 0, GETVAL, 0);
}


int allocate_shared_memory(int size) {
    return shmget(IPC_PRIVATE, size, IPC_CREAT | SHM_W | SHM_R);
}

void deallocate_shared_memory(const void* addr, int shmid) {
    shmctl(shmid, IPC_RMID, 0);
}

void* attach_shared_memory(int shmid) {
    return shmat(shmid, NULL, 0);
}

sem.h:

#include <sys/types.h>
#include <errno.h>

union semun {
    int val;
    struct semid_ds *buf;
    ushort* array;
};


int sem_post(int);

int sem_wait(int);

int sem_allocate();

void sem_deallocate(int);

int sem_init(int, int);

int sem_get_val(int);

shm.h:

#include <stdio.h>
#include <sys/shm.h>
#include <sys/stat.h> 

int allocate_shared_memory(int size);

void deallocate_shared_memory(const void* addr, int shmid);

void* attach_shared_memory(int shmid);

Why before executing consumer function value of full semaphore is 0? Even if right after producer finishes his job the value is 1...

I'm new to this kind of topics so maybe there is an obvious explenation of the situation, but I have no idea what can I do and hope you can help me.


Solution

  • You initialize the "full" semaphore to zero. Your "child" producer, prior to exiting calls your sem_post() function, which calls semop() with a SEM_UNDO argument.

    int sem_post(int semid) {
        printf("Someone is posting %d\n", semid);
        struct sembuf sem = { 0, 1, SEM_UNDO };
        return semop(semid, &sem, 1);
    }
    

    The Ubuntu Linux man page for semop says the following about SEM_UNDO:

    ... If an operation specifies SEM_UNDO, it will be automatically undone when the process terminates.

    This means, "producer" increments "full" prior to exiting, then after it exits the system "undoes" the increment (i.e. it decrements "full") setting it back to zero.

    So,for the purposes of the "full" semaphore, you should NOT specify SEM_UNDO.