Drawing with ncurses, sockets and fork

769 Views Asked by At

So I'm doing the barbershop problem but now I want to make it 'visual' and network-friendly. When the server starts up, awaits for clients to connect, each time a client connects it draws a 'P' that travels around the screen up to the barber position. I do this with NCurses and I have no problem with that.

However I only managed to draw one client (one 'P') at a time. I'd like to see many clients ('P's) in the screen. Because the way I have it now I only use 1 chair at a time, the client then gets attended and exits, then the next client in the accept() queue enters the screen and so on. It gives the impression that there is no actual concurrence thought there is.

I have a very extense code but the fork/socket part is here:

pid_t client;
int p;
RandSeed=8;
 listen(connection,90);
 while(1){
     remote_dir_size = sizeof(remote_dir);
    //"Awaiting connection...
     if((connection_client=accept(connection,(struct sockaddr *)&remote_dir,&remote_dir_size))<0){
         console_write("CONNECTION REJECTED!");
         exit(-1);
     }
    //"Connection accepted!
     client=fork();
     switch(client)
     {
         case -1:
            console_write("Error Forking!!");
            exit(1);
         case 0:
            close(connection); //So that another client can come.
            recvs = recv(connection_client,petition,sizeof(petition),0);
            
             //console_write(petition);
            // console_write(" moviendose.");
             move_client();
            
             //Check for avialable chairs
            
             //waiting_client_count++;
             sem_wait(waitingRoom); //wait until available
             move_client_to_chairs();
             sitting_client_count++;                
             redraw_chairs();  //redraw chair <--Useless since only 1 chair is used at a time :/
             //waiting for barber
              
             sem_wait(barberChair);
             
             //barber available, chair occupied is now free
             sem_post(waitingRoom);
             sitting_client_count--;
             redraw_chairs();
             
             move_client_to_barber(); //Move 'P' towards barber chair
             sit_client_barber();
             //Wake barber
             sem_post(barberPillow);
            
             //Wait until barber ends
            
             sem_wait(seatBelt);
             //release chair
             sem_post(barberChair);
             exit_client();
   
             exit(0);
         default:
            
            //barber sleeps until someone wakes him
                             
            sem_wait(barberPillow);
            randwait(5);
            //barber cutting hair
            randwait(5);
            //barber finished
            //free client
            sem_post(seatBelt);
            wait(&p);        
     }
}

Complete version of the code is here

My problem is: Server starts up good. Then when I run a ./client the server screen starts drawing the P along the screen and it moves it correctly, the client gets its haircut and exits. But when I run 2 or more ./clients the server screen draws the process once at a time, there's only one client at a time inside the barbershop; as if it's waiting for that one client to exit() in order to start the next forked process.

I find this very odd, what am I missing here? Is it a problem of accept queue? Should I try a different perspective?

1

There are 1 best solutions below

0
On BEST ANSWER

The problem is that your parent process is trying to do two different things that need waiting:

  • "barber sleeps until someone wakes him"

  • accept()

Without some kind of joint waiting, this architecture does not work. Both pieces of code sleeps waiting for a single kind of event, disregarding the other type of event. For example, when a client comes in, the program will not accept another client until "barber finished".

There are two directions to solve the problem:

  1. Join the two kinds of waiting: for accept(), you can turn the socket into non-blocking mode, and use select() or poll() to wait until an event comes in, for the semaphore, this is not so trivial, but if you open a socket between the parent and each child, and turn the semaphore handling into network communications, you can have a single select() or poll() waiting for all the sockets (one for receiving new client connections, and one per client for communicating with them) at a single place. You'd also have to redesign the semaphore handling - and get rid of the sleep there, something like keeping a list of active timers, and go back to select() or poll() until the next timer times out (their last parameter can put a limit on the maximum wait time).
  2. Separate the accept() and the semaphore handling into two processes, ie the parent is handling the new incoming connections, and a child process "simulates" the barber. Actually, the way you tried to implement the "barber", you'd need one child process per barber, because the process is sleeping while simulating the job done by the barber. Or you redesign the "barber" simulation as mentioned in the first part.