I'm writing a C program that parses input from STDIN into words, generates a number of sort processes specified by the numsorts variable, pipes the words in a round-robin fashion to each of the sort processes, and sends the output of the sorts to STDOUT.
My program works as desired and exits cleanly if the number of specified sort processes is 1, but the sort child processes don't die if the number of sort processes is greater than 1, and my program gets stuck waiting for them. The strangest part of this, to me, is that if I open up a separate terminal window and kill all of the children except 1, the last child immediately dies on its own and the program exits cleanly.
Here's my code for the parser (the pipe file descriptors are stored in a 2-dimensional array):
void RRParser(int numsorts, int **outPipe){ //Round Robin parser
int i;
char word[MAX_WORD_LEN];
//Close read end of pipes
for(i = 0; i < numsorts; i++){
closePipe(outPipe[i][0]);
}
//fdopen() all output pipes
FILE *outputs[numsorts];
for(i=0; i < numsorts; i++){
outputs[i] = fdopen(outPipe[i][1], "w");
if(outputs[i] == NULL)
printf("Error: could not create output stream.\n");
}
//Distribute words to them
i = 0;
while(scanf("%[^,]%*c,", word) != EOF){
strtoupper(word);
fputs(word, outputs[i % numsorts]); //round robin
fputs("\n", outputs[i % numsorts]); //sort needs newline
i++;
}
//Flush the streams:
for(i=0; i < numsorts; i++){
if(fclose(outputs[i]) == EOF)
printf("Error closing stream.\n");
}
}
Here's the code that generates the sort processes (PukeAndExit() just prints out the error message and exits):
int *spawnSorts(int numsorts, int **inPipe){
//returns an array containing all the PIDs of the child processes
//Spawn all the sort processes
pid_t pid;
int i;
int *processArray = (int *)malloc(sizeof(int) * numsorts);
for(i = 0; i < numsorts; i++){
switch(pid = fork()){
case -1: //oops case
PukeAndExit("Forking error\n");
case 0: //child case
//Bind stdin to read end of pipe
closePipe(inPipe[i][1]); //close write end of input pipe
if(inPipe[i][0] != STDIN_FILENO){ //Defensive check
if(dup2(inPipe[i][0], STDIN_FILENO) == -1)
PukeAndExit("dup2 0");
closePipe(inPipe[i][0]); //Close duplicate pipe
}
execlp("sort", "sort", (char *)NULL);
break;
default: //parent case
processArray[i] = pid;
}
}
return processArray;
}
At the end of main(), here's the code that waits for the sort processes to die:
for(i=0; i<numsorts; i++){ //wait for child processes to die.
wait(NULL);
}