0

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);
    }

}
Andreas Wenzel
  • 22,760
  • 4
  • 24
  • 39
krysznys
  • 79
  • 1
  • 6

1 Answers1

1

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.
Andreas Wenzel
  • 22,760
  • 4
  • 24
  • 39
  • I took into account all the suggestions you gave and made some progress with the program. But one thing is still bugging me - the client program does not exit at all, even after all the clients get their cuts done. I have `status()` function which should check the number of cuts for each client, It prints them correctly, it also prints the message "814 is done cutting", and after few seconds I can press enter and program ends, but not on its own as it should. – krysznys Jan 20 '21 at 18:58
  • @krysznys: You are not checking the return value of `sem_wait` to see if the mutex was successfully acquired. For example, if the function fails because it is interrupted by a signal handler, your program will incorrectly assume that the mutex was successfully acquired. – Andreas Wenzel Jan 21 '21 at 07:30
  • I did this but the client code still does not exit after doing all it is supposed to do. I updated the code in the post. – krysznys Jan 21 '21 at 18:13
  • @krysznys: The behavior of the function `signal` is very platform-specific, even on POSIX-compliant systems. Are you using Linux with glibc? – Andreas Wenzel Jan 22 '21 at 17:56
  • @krysznys: If you are using Linux, then it is not safe to use the function `printf` in an asynchronous signal handler. See [this link](https://man7.org/linux/man-pages/man7/signal-safety.7.html) for further information. – Andreas Wenzel Jan 22 '21 at 18:31
  • @krysznys: Why are you locking and unlocking the semaphore `barber` twice every time? I believe this should not be necessary, if you initialize the semaphore to `0` instead of `1`. – Andreas Wenzel Jan 22 '21 at 18:42
  • @krysznys: Instead of using the function `pause`, I suggest that you create another semaphore that is initialized to the value `0` and replace the `pause` with a `sem_wait` on this new semaphore. Your signal handler should then do nothing else than signal this semaphore using `sem_post`. Note that `sem_post` is on [the list of functions that are safe to use in an asynchronous signal handler](https://man7.org/linux/man-pages/man7/signal-safety.7.html). That way, you will be able to safely use `printf` after the `sem_wait` returns successfully (you should always check for errors). – Andreas Wenzel Jan 22 '21 at 19:34
  • Fair point, I initialized the semaphores with 0 instead of 1. I also added the semaphore to get rid of `pause()` but it did not change anything – krysznys Jan 22 '21 at 21:01
  • @krysznys: Can you please show me your latest code? It would be nice if you would take over my fixes to your indentation before overwriting them, because your code is very hard to read without proper indentation. – Andreas Wenzel Jan 23 '21 at 06:56
  • @krysznys: It would also be nice if you restored the original question, so that my answer is not invalidated, and if you only added the updates to the end of the question, so that it is clear what is your original code and what is your updated code. – Andreas Wenzel Jan 23 '21 at 07:04
  • @krysznys: It is also not allowed to call `exit` from inside an asynchronous signal handler, as the cleanup code may fail, due to the library being in an inconsistent state. You can however call `abort`. See the link in one of my previous comments for further information. However, as already stated, it is probably better for the signal handler to do nothing else than call `sem_post`. – Andreas Wenzel Jan 23 '21 at 07:44
  • @krysznys: Note that some of the links I am sending you are specific to the Linux platform. Since you have not yet answered my question what operating system you are using, I am making the assumption that you are using Linux. – Andreas Wenzel Jan 23 '21 at 07:48
  • Uodated the code, sorry for not answering the question about OS - yes, I use linux through WSL on windows 10. Calling `abort()` did not work, all I got was a message from one client about his remaining cuts. – krysznys Jan 23 '21 at 15:44
  • You are still calling `printf` and `exit` in both signal handlers, although I pointed out that calling these functions in an asynchronous signal handler is forbidden. You should also not be calling `free_sem` from inside the signal handler, because that function will call `printf` if `sem_post` fails. Instead, you should be calling `sem_post` directly and call `abort` if it fails. You should be aware that your program can be in an inconsistent state when the signal handler is invoked, so what you are allowed to do is very limited. See the link I already gave you for further information. – Andreas Wenzel Jan 23 '21 at 16:18
  • @krysznys: If you really need to output something from inside a signal handler, you can use `write` with `1` (stdout) as the file descriptor, as that function is in the list of functions that are safe to call from inside an asynchronous signal handler. However, this will bypass the output buffer of the C standard library, so the output of `write` will appear before any `printf` output that hasn't been flushed yet. Therefore, this function should only be used immediately before calling `abort`. – Andreas Wenzel Jan 23 '21 at 16:20
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/227726/discussion-between-andreas-wenzel-and-krysznys). – Andreas Wenzel Jan 23 '21 at 16:26