Search code examples
cforkdeadlocksemaphore

Forked Children Initialization Sempahore Blocking


My goal is to synchronize forked processes by a common int value.

I need to fork a number of children but also semaphore block them until all the children have been forked.

This variable (init_num) must first become equal to the number of processes I have and then the processes can continue with their functions.

My code :


    pid_t pid;
    key_t key;
    sem_t *sem;
    sem_t *sem2;
    int shmid;

    int main(int argc , char* argv[])
    {
        if (argc < 2){
            printf("Incorrect number of arguments given!\n");
            exit(-1);
        }

        int num_strings = argc - 2; 
        int reps = atoi (argv[1]);


        //  SHARED MEMORY  // 


        key = 9876;

        shmid = shmget( key , sizeof(int) , 0644 | IPC_CREAT);
        if ( shmid < 0 ){
            perror("shmget\n");
            exit(-1);
        }

        // SHARED MEMORY INITIATED //

        int *init_num;
        init_num = shmat ( shmid , NULL , 0);
        *init_num = 0;

        // SEMAPHORES TIME //

        sem = sem_open("TheSem", O_CREAT | O_EXCL , 0644 , 1);

        sem2 = sem_open("TheSem2", O_CREAT | O_EXCL , 0644 , 0);

        // SEMAPHORE DONE //

        int i;
        // FORKS //
        for (i=0; i<num_strings; i++){
            pid = fork();

            if (pid < 0) {
                        printf ("Fork error.\n");

                sem_unlink("TheSem");
                sem_close(sem);

                sem_unlink("TheSem2");
                sem_close(sem2);
                }
                else if ( pid == 0)
                        break;
            }

        // FATHER //
        if ( pid !=0 && pid!=-1 ){
            int status;
                    while(pid = waitpid (-1,NULL,0)){
                            if(errno == ECHILD)
                                    break;
                    }         
            shmdt (init_num);
            shmctl (shmid, IPC_RMID, 0);

            sem_unlink("TheSem");
            sem_close(sem);

            sem_unlink("TheSem2");
            sem_close(sem);
            }

        int n;
        //CHILDREN //
        if( pid == 0){
            sem_wait(sem);
                init();
                *init_num++;
            sem_post(sem);

            if (*init_num < num_strings ){
                sem_wait(sem2);
            }else{
                for(n=0 ; n <=num_strings-1; n++){
                    sem_post(sem2);
                }
            }

            // DISPLAY // 
            for(n=0; n < reps; n++){
                sem_wait(sem);
                    display(argv[2+i]);
                sem_post(sem);
            }

        }
    return(0);
    }

And *init_num is in shared memory space.

The problem is after all of the processes have done init() and *init_num should be equal to processes my program deadlocks and stops right there.

Output :

STARTING: pid 1268, tid 1157203712
STARTING: pid 1270, tid 1157203712
STARTING: pid 1269, tid 1157203712
STARTING: pid 1271, tid 1157203712
STARTING: pid 1272, tid 1157203712
STARTING: pid 1273, tid 1157203712
STARTING: pid 1274, tid 1157203712
STARTING: pid 1275, tid 1157203712  // these are init()

Solution

  • Enable warnings, and you will see

    warning: expression result unused [-Wunused-value]
            *init_num++;
            ^~~~~~~~~~~
    

    which immediately shows your problem. The children do not increment the value: they increment the pointer. Technically it is UB, but realistically they all read 0. Change it to

            (*init_num)++;