Search code examples
cposixipcsemaphore

Sleeping barber - shared memory queue not working?


few days ago I started working on the sleeping barber problem, got some issues with segmentation fault But they have been solved here Even though I fixed the missing parts, I still have a problem. I need to use FIFO queue, and create shared memory for it. I get no errors when creating it. Running the client should let me put clientAmount of clients into the queue, from which barber should be getting them. Every client is described by his process id. But when I try to do so, client program shows that clients have been added to queue:

2101 entered the queue
2099 entered the queue
2104 entered the queue
2097 entered the queue
2103 entered the queue
2095 entered the queue
2102 entered the queue
2098 entered the queue
2096 entered the queue

but when I run the barber code, all I get is:

Queue empty, I fall asleep
I'm waking up
Queue empty, I fall asleep
I'm waking up
Queue empty, I fall asleep
I'm waking up
Queue empty, I fall asleep
I'm waking up
Queue empty, I fall asleep
I'm waking up
Queue empty, I fall asleep
I'm waking up
Queue empty, I fall asleep
I'm waking up
Queue empty, I fall asleep
I'm waking up

I'm not really sure what to do here.

The code for client:

#include <stdio.h>
#include <stdlib.h>
#include <semaphore.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "functions.h"
#include <sys/mman.h>
#include <unistd.h>
#include <signal.h>
int numberOfCuts;
int isCut;
int isDone;
void status(int f);
int main(int argc,char* argv[])
{

     if(argc < 3){
        printf("Error while executing program, invalid amount of arguments");
        return 0;
    }
    sem_t *barber;
    sem_t *queue;
    sem_t *client;
    sem_t *pillow;
    int clientsAmount;

    int sharedmem, waitRoomSize;
    struct Queue* waitroom;
    void *space;
    int i;
    signal(SIGUSR1, status);


    clientsAmount = atoi(argv[1]);
    numberOfCuts = atoi(argv[4]);

    barber = sem_open("/B", O_RDWR);
     if((barber == SEM_FAILED)){
        perror("Error while getting semaphore for barber");
        exit(1);
        }

    queue = sem_open("/Q", O_RDWR);
    if((queue  == SEM_FAILED)) {
        perror("Error while creating semaphore for queue");
        exit(1);
      }

    client = sem_open("/C", O_RDWR);
    if(client == SEM_FAILED){
        perror("Error while creating semaphore for pillow");
        exit(0);
      }

    sharedmem = shm_open("QueueMem", O_RDWR, 0666);
   if(sharedmem==-1){
       perror("Error while getting shared memory");
       exit(1);
    }
    space = mmap(NULL, sizeof(waitroom), PROT_READ | PROT_WRITE, MAP_SHARED, sharedmem, 0);
   if((space == MAP_FAILED)){
       perror("Error while mapping memory");
       exit(1);
       }
    waitroom = (struct Queue*) space;


    for(i = 0; i< clientsAmount; i++){
        if(fork() == 0){
            int isCut = 0;

                 int id = getpid();
                 printf("%d entered the queue \n", id);
                    sem_wait(queue);

                    sem_post(queue);
                    if( push(waitroom, id)==-1){
                        printf("Queue is full, leaving...");
                        exit(0);
                    }else {
                    push(waitroom, id);
                    sem_wait(pillow);
                    printf("%d: Barber is sleeping, he needs to wake up", id);
                    int x;
                    sem_getvalue(barber, &x);
                    if(x==0){
                        sem_post(barber);
                        while(x!= 0){
                        sem_post(barber);
                        printf("Barber is waking up to cut %d", id);
                        }
                    }
                    sem_post(pillow);

                    _exit(0);
                    }

                }
            }






sem_close(barber);
    sem_unlink("/B");
    sem_close(queue);
    sem_unlink("/Q");
    sem_close(client);
    sem_unlink("/C");


}

void status(int f){
numberOfCuts--;
printf("Remaining cuts: %d", numberOfCuts);
isCut = 1;
while(!numberOfCuts)
{
    printf("Leaving the barber");
    isDone =1;
}
}

The barber code:

#include <stdio.h>
#include <stdlib.h>
#include <semaphore.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "functions.h"
#include <sys/mman.h>
#include <unistd.h>
#include <signal.h>
#define BARBER "Barber"
int main(int argc,char* argv[])
{
    if(argc < 2){
        printf("Error while executing program, invalid amount of arguments");
        return 0;
    }
    sem_t *barber;
    sem_t *queue;

    sem_t *client;
    int seats;
    int sharedmem, waitRoomSize;
    struct Queue* waitroom;


    queue = sem_open("/Q", O_CREAT | O_RDWR, 0666, 1);
     if((queue  == SEM_FAILED)) {
        printf("Error while creating semaphore for queue");
        exit(1);
      }
      barber= sem_open("/B", O_CREAT | O_RDWR, 0666, 1);
     if((barber == SEM_FAILED)){
        printf("Error while creating semaphore for barber");
        exit(1);
        }
     client = sem_open("/C", O_CREAT | O_RDWR, 0666, 0);
      if(client == SEM_FAILED){
        printf("Error while creating semaphore for pillow");
        exit(0);
      }




    seats = atoi(argv[1]);
    void *space;



    sharedmem = shm_open("Queue",O_CREAT | O_RDWR, 0666);
    if(sharedmem==-1){
       printf("Error while getting shared memory");
       exit(1);
    }
    waitRoomSize = ftruncate(sharedmem, sizeof(waitroom));
    if((waitRoomSize ==-1)){
       printf("Error while getting size");
       exit(1);
       }
    space = mmap(NULL, sizeof(struct Queue), PROT_READ | PROT_WRITE, MAP_SHARED, sharedmem, 0);
    if((space == MAP_FAILED)){
       printf("Bład podczas mapowania pamiêci");
       exit(1);
       }
    waitroom = (struct Queue*) space;

    queueinit(waitroom, seats);
    printf("semaphores created\n");




    while(1)
    {

     sem_post(queue);
     int x = isEmpty(waitroom);
     sem_wait(queue);
     if(x==1){
        printf("Queue empty, I fall asleep\n");
        sem_post(barber);
        sem_wait(barber);
        printf("I'm waking up\n");
     } else {
     sem_post(queue);

     int id = get(waitroom);
     sem_wait(queue);

     printf("%d, please sit on the chair\n", id);
     printf("Started cutting hair for %d\n", id);
     sleep(2);
     printf("Cutting done for :%d \n", id);

     kill(id, SIGUSR1);

     }
    }


    sem_close(barber);
    sem_unlink("/B");
    sem_close(queue);
    sem_unlink("/Q");
    sem_close(client);
    sem_unlink("/C");

    printf("senaphores unlinked");
    }

The code for queue:

#ifndef FUNCTIONS_H_INCLUDED
#define FUNCTIONS_H_INCLUDED
#include <stdio.h>
#include <stdlib.h>
#include <semaphore.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "functions.h"
#include <sys/mman.h>
#include <unistd.h>
#include <signal.h>

struct Queue{
    int elems[500];
    int size;
    int queueIn;
    int queueOut;
    int isAsleep;
    int mainPID;
    int countCurrent;
};
void queueinit(struct Queue* q, int size){
    q->size = size;
    q->queueIn = q->queueOut = 0;
    q->isAsleep = 0;
    q->countCurrent = 0;

}

int push(struct Queue* q, int e){
    if(q->queueIn == ((q->queueOut -1 + q->size) % q->size)){
        return -1; //Queue full
    }

    q->elems[q->queueIn] = e;
    q->queueIn = (q->queueIn + 1) % q->size;
    return 0;
}

int get(struct Queue* q){
    int e = q->elems[q->queueOut];
    q->queueOut = (q->queueOut + 1) % q->size;
    return e;
}

int isEmpty(struct Queue* q){
    if(q->queueIn == q->queueOut)
        return 1;
    return 0;
}


void lock(sem_t* sem){
if(sem_wait(sem) == -1){
    printf("Error while lockin semaphore");
    exit(1);
}

}

void free_sem(sem_t* sem){
if(sem_post(sem) == -1){
    printf("Error while releasing semaphore");
    exit(1);
}

}

#endif // FUNCTIONS_H_INCLUDED

Any suggestion would be greatly appreciated

EDIT As of now, few changed have been added:

  1. Checking the return value of sem_wait and sem_post
  2. Removed the second call for push(waitroom, id) in client
  3. Took care of locking and unlocking semaphores, mainly swapped sem_wait with sem_post and vice versa
  4. Got rid of the pillow semaphore

Now the program was working nearly fine, however the client code did not exit after all its children finished their work. Pressing [ENTER] works. So I took the given advice and created a new semaphore - p, which I lock instead of using pause() in client code, and I unlock it in signal handler status(). I also cahnged the value with which barber semaphore was initialized - no more double locking or freeing a semaphore. I also tried to use abort() function instead of exit but it didn't work.

What happens now:

  1. Barber code does start but nothing happens.
  2. Client code starts and adds some values to queue, and immediately exits.

The updated code for barber:

#include <stdlib.h>
#include <semaphore.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "functions.h"
#include <sys/mman.h>
#include <unistd.h>
#include <signal.h>
#define BARBER "Barber"
int main(int argc,char* argv[])
{
    if(argc < 2){
        printf("Error while executing program, invalid amount of arguments");
        return 0;
    }
    sem_t *barber;
    sem_t *queue;
    sem_t *p;
    int seats;
    int sharedmem, waitRoomSize;
    struct Queue* waitroom;


    queue = sem_open("/Q", O_CREAT | O_RDWR, 0666, 1);
     if((queue  == SEM_FAILED)) {
        printf("Error while creating semaphore for queue");
        exit(1);
      }
      barber= sem_open("/B", O_CREAT | O_RDWR, 0666, 0);
     if((barber == SEM_FAILED)){
        printf("Error while creating semaphore for barber");
        exit(1);
        }
    p= sem_open("/P", O_CREAT | O_RDWR, 0666, 0);
     if((p == SEM_FAILED)){
        printf("Error while creating semaphore for barber");
        exit(1);
        }



    seats = atoi(argv[1]);
    void *space;
    sharedmem = shm_open("QueueMem",O_CREAT | O_RDWR, 0666);

    if(sharedmem==-1){
       printf("Error while getting shared memory");
       exit(1);
    }
    waitRoomSize = ftruncate(sharedmem, sizeof(waitroom));
    if((waitRoomSize ==-1)){
       printf("Error while getting size");
       exit(1);
       }
    space = mmap(NULL, sizeof(struct Queue), PROT_READ | PROT_WRITE, MAP_SHARED, sharedmem, 0);
    if((space == MAP_FAILED)){
       printf("Error while mapping memory");
       exit(1);
       }
    waitroom = (struct Queue*) space;

    queueinit(waitroom, seats);





    while(1)
    {

        lock(queue);
        if(isEmpty(waitroom)==1){
                printf("Queue empty, I fall asleep\n");
                waitroom->isAsleep = 1;
                free_sem(queue);
                lock(barber);

                printf("I'm waking up\n");

        } else {
            int id = get(waitroom);
            free_sem(queue);

            printf("%d, please sit on the chair\n", id);
            printf("Started cutting hair for %d\n", id);
            sleep(2);
            printf("Cutting done for :%d \n", id);

            kill(id, SIGUSR1);

     }
    }


    sem_close(barber);
    sem_unlink("/B");
    sem_close(queue);
    sem_unlink("/Q");
    sem_close(p);
    sem_unlink("/P");
    //exit(0);
    //printf("senaphores unlinked");
    }

The updated code for client:

#include <stdio.h>
#include <stdlib.h>
#include <semaphore.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "functions.h"
#include <sys/mman.h>
#include <unistd.h>
#include <signal.h>
int numberOfCuts;

#include "functions.h"
int id;
sem_t *barber;
sem_t *queue;
sem_t *p;
int clientsAmount;

int sharedmem, waitRoomSize;
struct Queue* waitroom;
void *space;
void status(int f);
void handler(int f);



int main(int argc,char* argv[])
{

     if(argc < 3){
        printf("Error while executing program, invalid amount of arguments");
        return 0;
    }

    int i;
    signal(SIGUSR1, status);
    signal(SIGINT, handler);
    int pid = getpid();
    clientsAmount = atoi(argv[1]);
    numberOfCuts = atoi(argv[2]);

    barber = sem_open("/B", O_RDWR);
     if((barber == SEM_FAILED)){
        perror("Error while getting semaphore for barber");
        exit(1);
        }

    queue = sem_open("/Q", O_RDWR);
    if((queue  == SEM_FAILED)) {
        perror("Error while creating semaphore for queue");
        exit(1);
      }
    p = sem_open("/P", O_RDWR);
    if((p  == SEM_FAILED)) {
        perror("Error while creating semaphore for queue");
        exit(1);
      }


    sharedmem = shm_open("QueueMem", O_RDWR, 0666);
   if(sharedmem==-1){
       perror("Error while getting shared memory");
       exit(1);
    }
    space = mmap(NULL, sizeof(waitroom), PROT_READ | PROT_WRITE, MAP_SHARED, sharedmem, 0);
   if((space == MAP_FAILED)){
       perror("Error while mapping memory");
       exit(1);
       }
    waitroom = (struct Queue*) space;


    for(i = 0; i< clientsAmount; i++) {
        if(fork() == 0) {
            id = getpid();
            printf("%d entered the barbershop \n", id);
            while(1) {

                lock(queue);
                if( push(waitroom, id)==-1 ) {
                   free_sem(queue);
                    printf("Queue is full, %d leaving...\n", id);
                    exit(0);
                } else {
                    free_sem(queue);
                    printf("%d has entered the queue \n", id);
                    lock(queue);
                    int x;
                    x = waitroom->isAsleep;
                    if(x==1){
                        printf("%d: Barber is sleeping, he needs to wake up\n", id);
                        waitroom->isAsleep = 0;
                        free_sem(queue);
                        free_sem(barber);
                        printf("Barber is waking up to cut %d\n", id);
                    } else {
                        printf("Barber is cutting someone else, %d waiting for its turn", id);
                        free_sem(queue);

                    }

                }
                lock(p);
            }

            break;
        }
    }


    //exit(0);
    sem_close(barber);
    sem_close(queue);
    sem_close(p);
    munmap(space, waitRoomSize);

    exit(0);

}


void handler(int f) {
    printf("Closing");
    sem_close(barber);
    sem_close(queue);

    munmap(space, waitRoomSize);

    exit(0);
}

void status(int f) {

    numberOfCuts--;
    free_sem(p);
    printf("Remaining cuts for %d: %d\n", id, numberOfCuts);

    if(!numberOfCuts) {

        printf("%d is done cutting \n", id);

        exit(0);
    }

}

Solution

  • There are several mistakes in your program:

    1. In the client code, you are calling sem_wait(pillow) and sem_post(pillow);, although the variable pillow has not been initialized. This causes undefined behavior. In order to initialize a semaphore, you can use the functions sem_init or sem_open.
    2. In the client code, you release the mutex queue immediately after acquiring it. Instead, you should only release it when you are finished with the queue operation.
    3. In the client code, you call push(waitroom, id) twice, the second call being immediately after the first call. This does not make sense.
    4. In the barber's main loop, you are releasing the mutexes queue and barber without acquiring them beforehand, then acquire them afterwards. A mutex should normally first be acquired, then released, not the other way around. Use sem_wait for acquiring the mutex, sem_post for releasing it. EDIT: Meanwhile, I believe that you are using the semaphore barber for signalling purposes, not as a mutex. In that case, it is correct to call sem_post without having called sem_wait beforehand.
    5. You are not checking the return value of sem_wait. For example, it is possible that the function fails due to being interrupted by a signal handler.
    6. It is not safe to use the function printf in a signal handler. See this link for more information.
    7. You are not waiting for the child processes to finish, before terminating the parent process.