oved mani
oved mani

Reputation: 19

C program iterates too much when using fork inside while

I'm reading lines of text from file, and for each line I'm processing it using several { fork() --> child process invokes execvp(), and parent invokes wait() } . at the end of process I'm writing the results to a file.

Problem is: the while loop seems to iterate too much and also the writing to the file.

The results.csv file contains 6 lines instead of just 2 (the while iteration iterates a text file with 2 lines, but also when I use printf it seems like the last line is read twice).

What am I missing here?

The code example is:

FILE* results = fopen("results.csv", "w");
if (results == NULL){
    fclose(fp);
    perror("Failed opening results file");
    exit(-1);
}
fdIn = open(inputPath, O_RDONLY);
if (fdIn < 0){
    perror("Failed opening input file");
    exit(-1);
}
while (fgets(student, sizeof(student), fp) != NULL) {
    // override end line char of unix ('\n') with '\0'
    student[strlen(student)-1] ='\0';
    pid = fork();
    if (pid < 0){
        close(fdIn);
        perror("Failed creating process for executing student's program");
        exit(-1);
    }
    if (pid == 0) {// son process code
        fdOut = open("tempOutput.txt", (O_WRONLY | O_CREAT | O_TRUNC), 0666);
        if (fdOut < 0){
            perror("Failed opening temporary output file");
            exit(-1);
        }
        close(1);
        dup(fdOut);
        close(fdOut);
        close(0);
        dup(fdIn);
        close(fdIn);
        char studProgPath[bufSize];
        strcpy(studProgPath,studentsFolderPath);
        strcat(studProgPath,"/");
        strcat(studProgPath,student);
        strcat(studProgPath,"/");
        strcat(studProgPath,"a.out");
        char * args[] = {"a.out", NULL};
        ret_code = execvp(studProgPath,args);
        if (ret_code == -1){
            perror("Failed executing student program");
            exit(-1);
        }
    }
    waited = wait(&stat);
    if (stat == -1){ // need to grade 0
        printf("%s,0\n",student);
    }else{ // open process to compare the output with the expected
        pid = fork();
        if (pid < 0){
            perror("Failed opening process for comparing outputs");
            exit(-1);
        }
        if(pid == 0) { // son process
            char * args[] = {"comp.exe",outputPath,"tempOutput.txt",NULL};
            ret_code = execvp("comp.exe",args);
            exit(ret_code);
        }
        waited = wait(&stat);
        if (stat == -1) {
            perror("Failed executing comparing program");
            exit(-1);
        } else if (stat == 0 || stat == 1) { // if outputs are not the same
            fprintf(results,"%s,0\n",student);
        } else { // matching outputs grade 100
            fprintf(results,"%s,100, pid: %d\n",student,getpid());
        }
    }
}

Upvotes: 1

Views: 94

Answers (1)

Michael Veksler
Michael Veksler

Reputation: 8475

The file which gets triple entries gets opened here:

FILE* results = fopen("results.csv", "w");

The following lines write to this results file, slightly before the function calls fork():

} else if (stat == 0 || stat == 1) { // if outputs are not the same
  fprintf(results,"%s,0\n",student); 
} else { // matching outputs grade 100 
  fprintf(results,"%s,100, pid: %d\n",student,getpid()); 
}

This file should be flushed with fflush(results) before the fork, otherwise the buffer of results might be flushed three times: in the parent, and in the two copies in the children.

Also, results and student should be closed with fclose(results) and student, before calling execvp. If the files are not closed, then the a.out might manipulate the results file. I assume that a.out is an external code which you don't control.

while (fgets(student, sizeof(student), fp) != NULL) {
    // override end line char of unix ('\n') with '\0'
    student[strlen(student)-1] ='\0';
    fflush(results); // otherwise each child may flush the same chars
    pid = fork();
    if (pid < 0){
        fclose(results); // otherwise ./a.out might write to this file
        fclose(fp); // better also close it.
        close(fdIn);

Upvotes: 2

Related Questions