2013-03-06 2 views
0

J'écris un programme pour une affectation où un parent joue connect quatre contre N processus enfants. Le programme utilise des tuyaux pour communiquer les mouvements de jeu entre les processus.Race Condition avec fork() et pipe()

J'ai cependant un problème pour corriger une condition de concurrence présente dans mon programme. Il y a une condition où le processus fils bloque son appel read() une fois le jeu terminé. Cela semble seulement se produire lorsqu'il y a plus d'un processus enfant.

J'ai essayé plusieurs choses, telles que les sémaphores nommés, mais je suis encore assez nouveau pour les forks, les pipes, et l'IPC. J'ai posté un point essentiel avec le code pertinent (j'ai essayé de nettoyer le mieux que je peux pour une meilleure lisibilité) ici:

Gist with relevant code

Toute aide serait grandement appréciée

EDIT

Voici la source pertinente de l'essentiel avec les déclarations ajoutées.

int main (int argc, char const *argv[]) 
{ 
    int dimension = 8, children = 2, i; 
    int child_play_to_win = 0; 
    int fd[children][4]; 
    pid_t childpid[children]; 
    Board** boards = (Board**) malloc(sizeof(Board*) * children); 
    GameMove* lastMove, *tmpMove; 
    char buf[80]; 
    for(i = 0; i < children; i++) { 
    generate_board(&(boards[i]), dimension); 
    int tmp[2]; 
    pipe(tmp); 

    // child read 
    fd[i][0] = dup(tmp[0]); 
    // parent write 
    fd[i][1] = dup(tmp[1]); 

    pipe(tmp); 
    // parent read 
    fd[i][2] = dup(tmp[0]); 
    // child write 
    fd[i][3] = dup(tmp[1]); 

     childpid[i] = fork(); 

    if(childpid[i] == -1) { 
     perror("fork"); 
     exit(1); 
    } 
    if(childpid[i] == 0) { 
     srand(getpid()); 
     close(fd[i][1]); 
     close(fd[i][2]); 
     while(!boards[i]->finished) { 
     // Read in move from parent 
     printf("child[%d] about to read\n", getpid()); 
     read(fd[i][0], &buf, sizeof(GameMove)); 

     // repeat parent move on this board 

     if(gameNotFinished) { 
      // make child move 

      // write move back to parent 

      write(fd[i][3], lastMove, sizeof(GameMove)); 

      // If the board is finished (there was a win), 
      if (!gameNotFinihsed) { 
      // Child wins 
      close(fd[i][0]); 
      close(fd[i][3]); 
      printf("child[%d] ending\n", getpid()); 
      break; 
      } 
     } 
     else { 
      // Parent won 
      close(fd[i][0]); 
      close(fd[i][3]); 
      break; 
     } 
     } 
    dealloc(boards[i]); 
    exit(0); 
    } 
} 

    // When this hits children amount, all games are done 
    int games_complete = 0; 
    // Make first move to all children 
    for (i = 0; i < children; i++) { 
    close(fd[i][0]); 
    close(fd[i][3]); 
    lastMove = placePieceAtBestPosition(boards[i], 1); 
    printf("parent writing to child[%d]\n", childpid[i]); 
    write(fd[i][1], lastMove, sizeof(GameMove)); 
    } 
    while (games_complete != children) { 
    for (i = 0; i < children; i++) { 
     // Read move from child 
     read(fd[i][2], &buf, sizeof(GameMove)); 

     // repeat child move 

     // Check for a child win... 
     if (!checkForWin(boards[i], 2)) { 
     // No win yet, place piece at best position 

     lastMove = placePieceAtBestPosition(boards[i], 1); 

     // check for win again 
     boards[i]->finished = checkForWin(boards[i], 1); 
     // Write move back to child 
     write(fd[i][1], lastMove, sizeof(GameMove)); 

     // If we won, close everything up and increment 
     // the games_complete counter. 
     if(boards[i]->finished) { 
      close(fd[i][1]); 
      close(fd[i][2]); 
      games_complete++; 
     } 
     } else { 
    // write back child move if there was a win 
     write(fd[i][1], lastMove, sizeof(GameMove)); 
     close(fd[i][1]); 
     close(fd[i][2]); 
     printf("Parent lost!):\n"); 
     games_complete++; 
     } 
    } 
    } 
+2

S'il vous plaît poster (parties pertinentes) code directement dans votre question, non pas par un lien. Merci. –

+0

La première chose qui me frappe est que vous n'avez pas besoin de ces appels 'dup' après avoir créé les tuyaux. Surtout que vous ne fermez pas les descripteurs de fichiers d'origine. Selon le nombre de processus enfants que vous créez, cela peut remplir la table des descripteurs de fichiers. –

+0

* Pourquoi associeriez-vous plus de 100 lignes de code et n'incluriez pas la déclaration de la fonction d'ouverture en haut du fichier source? – WhozCraig

Répondre

0

Je pense que je connais votre problème. Lorsque vous enfourchez chaque enfant, vous fermez le côté parent de ses tuyaux. Cependant, chaque enfant a toujours ouvert le côté parent des tuyaux pour tous les enfants précédents. Pour cette raison, seul le dernier enfant créé aura ses côtés parents de ses tuyaux fermés.

vous suggère de changer:

close(fd[i][1]); 
close(fd[i][2]); 

à quelque chose comme:

for (j = 0; j <=i; j++) { 
    close(fd[j][1]); 
    close(fd[j][2]); 
} 
+0

Bonne prise. Je me bloque toujours sur l'enfant lu, mais c'est certainement quelque chose que j'ai raté. –