void update_private(int pos, int start, int end, int index) { if(start == end && start == index) { return; } if(start > index || end < index) { return; } int middle = (start + end) / 2; int leftSon =(pos << 1); int rightSon = (pos << 1) + 1; update_private(leftSon, start, middle, index); update_private(rightSon, middle + 1, end, index); int leftIndex = segment_tree_array[leftSon]; int rightIndex = segment_tree_array[rightSon]; if(array[leftIndex] <= array[rightIndex]) { segment_tree_array[pos] = leftIndex; } else { segment_tree_array[pos] = rightIndex; } }
static void semwait(int sem) { struct sembuf sbuf = { .sem_num = 0, .sem_op = -1, .sem_flg = 0 }; assert (semop(sem, &sbuf, 1) != -1); } static void semsignal(int sem) { struct sembuf sbuf = { .sem_num = 0, .sem_op = 1, .sem_flg = 0 }; assert (semop(sem, &sbuf, 1) != -1); } static pid_t fork_generator( int sem_labyrinth, int shm_stats, int shm_labyrinth, int top_x, int top_y, int left_x, int left_y ) { pid_t child = fork(); if (child != 0) { // Parent assert (child != -1); return child; } else // Child exit(generator( sem_labyrinth, shm_stats, shm_labyrinth, top_x, top_y, left_x, left_y) ); } // Retourne la cellule qui partage le même mur. static CELL *cell_fellow(CELL *cell, WALL orientation); // Retourne l'orientation opposée d'un mur static WALL orientation_inv(WALL orientation); // Vérifie que certains murs privés ne partagent pas à présent les mêmes groupes // ou qu'ils ne sont pas devenus partagés lorsque on a lié les deux groupes. static void update_private( LABYRINTH labyrinth, CELL walls[], int *last_private, int *first_shared ); static int generator( int sem_labyrinth, int shm_stats, int shm_labyrinth, int top_x, int top_y, int left_x, int left_y ) { int size = LABYRINTH_SIZE / 2; // Récupère les mémoires partagées. PARAL_STATS *stats = (PARAL_STATS *) shmat(shm_stats, NULL, 0); assert (stats != (PARAL_STATS *) -1); LABYRINTH labyrinth = (LABYRINTH) shmat(shm_labyrinth, NULL, 0); assert (labyrinth != (LABYRINTH) -1); // Retiens les murs non ouverts dans la zone du générateur (indice de la // cellule avec orientation du mur, utilise les bits dédiés au groupe // pour encoder l'indice). // Ajoute les murs non partagés à partir de la gauche et ceux partagés à // partir de la droite du vecteur. int n_walls = 2 * size * size; int last_private = -1 , first_shared = n_walls; CELL walls[n_walls]; semwait(sem_labyrinth); // Accède en lecture à des cellules partagées. for (int i = 0; i < size; i++) { for (int j = 0; j < size; j++) { int top_index = (top_y + i) * LABYRINTH_SIZE + top_x + j , left_index = (left_y + i) * LABYRINTH_SIZE + left_x + j; CELL *top_cell = labyrinth + top_index , *left_cell = labyrinth + left_index; CELL top_fellow = *cell_fellow(top_cell, WALL_TOP) , left_fellow = *cell_fellow(left_cell, WALL_LEFT); // Ajoute les murs sur des cases partagées à droite, les autres // à gauche. if (is_shared(*top_cell) && is_shared(top_fellow)) walls[--first_shared] = WALL_TOP | (CELL) top_index; else walls[++last_private] = WALL_TOP | (CELL) top_index; if (is_shared(*left_cell) && is_shared(left_fellow)) walls[--first_shared] = WALL_LEFT | (CELL) left_index; else walls[++last_private] = WALL_LEFT | (CELL) left_index; } } semsignal(sem_labyrinth); // Boucle tant qu'il reste des murs à supprimer. for (;;) { semwait(sem_labyrinth); // Vérifie que les murs partagés n'ont pas été supprimés par un autre // processus et qu'ils départagent toujours deux groupes différents. for (int i = n_walls - 1; i >= first_shared; i--) { CELL wall = walls[i]; CELL index = wall & GROUP_MASK; WALL orientation = wall & WALLS_MASK; CELL *cell1 = labyrinth + index; CELL *cell2 = cell_fellow(cell1, orientation); bool can_be_removed = is_wall(*cell1, orientation) && cell_root(labyrinth, cell1) != cell_root(labyrinth, cell2); if (!can_be_removed) // Ne peut plus supprimer ce mur. walls[i++] = walls[first_shared++]; } // Sélectionne au hasard un mur dans les murs restants. int n_remaining = (last_private + 1) + (n_walls - first_shared); if (n_remaining == 0) { semsignal(sem_labyrinth); break; } int random_index = rand() % n_remaining; if (random_index <= last_private) { stats->hits++; semsignal(sem_labyrinth); // Cette partie de l'algorithme peut s'effectuer en même temps sur // différents processus : CELL wall = walls[random_index]; CELL index = wall & GROUP_MASK; WALL orientation = wall & WALLS_MASK; CELL *cell1 = labyrinth + index; CELL *cell2 = cell_fellow(cell1, orientation); CELL *root1 = cell_root(labyrinth, cell1) , *root2 = cell_root(labyrinth, cell2); // Attache toujours une cellule qui n'appartient pas à un groupe // partagé à l'autre cellule. De cette manière, la racine d'un // groupe dont au moins une cellule est partagée sera une cellule // partagée. Ceci évite également de modifier une forêt qui pourrait // être modifiée par un autre processus au même moment. if (is_shared(*root1)) cell_attach_group(root2, *root1); else cell_attach_group(root1, *root2); // Supprime le mur de la liste d'attente et des deux cellules. walls[random_index] = walls[last_private--]; // Ces deux instructions sont équivalentes à ces deux instructions: // *cell1 &= ~orientation; // *cell2 &= ~orientation_inv(orientation); // A l'exception que le compilateur garantit que ces instructions // s'exécutent de manière atomique, et permet ainsi d'éviter que // deux processus suppriment deux murs différents d'une même cellule // au même instant, ce qui engendrait une perte de l'une des deux // modifications. __sync_fetch_and_and(cell1, ~orientation); __sync_fetch_and_and(cell2, ~orientation_inv(orientation)); // Une alternative aurait été d'acquérir la sémaphore sem_labyrinth // lorsque l'on change l'orientation d'une cellule qui est sur un // bord de la zone du générateur, mais cette méthode est // considérablement plus rapide. update_private(labyrinth, walls, &last_private, &first_shared); } else { stats->misses++; int wall_index = random_index - (last_private + 1) + first_shared; CELL wall = walls[wall_index]; CELL index = wall & GROUP_MASK; WALL orientation = wall & WALLS_MASK; CELL *cell1 = labyrinth + index; CELL *cell2 = cell_fellow(cell1, orientation); CELL *root1 = cell_root(labyrinth, cell1); cell_attach_group(root1, *cell2); // Supprime le mur de la liste d'attente et des deux cellules. walls[wall_index] = walls[first_shared++]; *cell1 &= ~orientation; *cell2 &= ~orientation_inv(orientation); semsignal(sem_labyrinth); } } return EXIT_SUCCESS; }
void update(int index, int value) { array[index] += value; update_private(1, 0, size - 1, index); }