void *hydrogen_routine(void *arg) { thread_data *t_data = (thread_data *)arg; int thread_id = t_data->t_id; // acquire lock on mutex before accessing shared memory semwait(&mutex); // if enough C is waiting, continue past barrier printf("HYDROGEN %d reached barrier.\n", thread_id); if (waiting_h >= 3 && waiting_c >= 1) { printf("~~~~ Crossed barrier, created a Methane!\n\n"); // release 4 H int i; for(i = 0; i < 3; i++) { semsignal(&sem_h); } waiting_h -= 3; semsignal(&sem_c); waiting_c--; // release lock on mutex semsignal(&mutex); } else { // not enough H or C is waiting, so wait at barrier waiting_h++; printf("Waiting Atoms -- C: %d | H: %d\n", waiting_c, waiting_h); // release lock on mutex semsignal(&mutex); semwait(&sem_h); } pthread_exit(EXIT_SUCCESS); }
/* @ingroup mailbox * * Allocate a mailbox that allows up to the specified number of outstanding * messages. * * @param count * Maximum number of messages allowed for the mailbox. * * @return * The index of the newly allocated mailbox, or ::SYSERR if all mailboxes * are already in use or other resources could not be allocated. */ syscall mailboxAlloc(uint count) { static uint nextmbx = 0; uint i; struct mbox *mbxptr; int retval = SYSERR; /* wait until other threads are done editing the mailbox table */ wait(mboxtabsem); /* run through all mailboxes until we find a free one */ for (i = 0; i < NMAILBOX; i++) { nextmbx = (nextmbx + 1) % NMAILBOX; mbxptr = &mboxtab[nextmbx]; /* when we find a free mailbox set that one up and return it */ if (MAILBOX_FREE == mbxptr->state) { /* get memory space for the message queue */ mbxptr->msgs = memget(sizeof(int) * count); /* check if memory was allocated correctly */ if (SYSERR == (int)mbxptr->msgs) { pi_printf("error: fail to mailbox memory\r\n"); break; } /* initialize mailbox details and semaphores */ mbxptr->count = 0; mbxptr->start = 0; mbxptr->max = count; usb_sem_new(&mbxptr->sender, 1024); usb_sem_new(&mbxptr->receiver, 0); if ((SYSERR == (int)mbxptr->sender) || (SYSERR == (int)mbxptr->receiver)) { memfree(mbxptr->msgs, sizeof(int) * (mbxptr->max)); semfree(mbxptr->sender); semfree(mbxptr->receiver); pi_printf("error: fail to allocate mailbox\r\n"); break; } /* mark this mailbox as being used */ mbxptr->state = MAILBOX_ALLOC; /* return value is index of the allocated mailbox */ retval = nextmbx; break; } } /* signal this thread is done editing the mbox tab */ semsignal(mboxtabsem); /* return either SYSERR or the index of the allocated mailbox */ return retval; }
/** * @ingroup mailbox * * Free the specified mailbox. * * @param box * The index of the mailbox to free. * * @return * ::OK if the mailbox was successfully freed, or ::SYSERR if @p box did * not specify a valid allocated mailbox. */ syscall mailboxFree(mailbox box) { struct mbox *mbxptr; int retval; if (!(0 <= box && box < NMAILBOX)) { pi_printf("error: fail to mailbox free\r\n"); return SYSERR; } mbxptr = &mboxtab[box]; /* wait until other threads are done editing the mailbox table */ wait(mboxtabsem); if (MAILBOX_ALLOC == mbxptr->state) { /* mark mailbox as no longer allocated */ mbxptr->state = MAILBOX_FREE; /* free semaphores related to this mailbox */ semfree(mbxptr->sender); semfree(mbxptr->receiver); /* free memory that was used for the message queue */ memfree(mbxptr->msgs, sizeof(int) * (mbxptr->max)); retval = OK; } else { /* mailbox was not allocated */ pi_printf("error: mailbox was not allocated\r\n"); retval = SYSERR; } /* signal that this thread is done editing the mailbox table */ semsignal(mboxtabsem); return retval; }
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; }
LABYRINTH gen_labyrinth(PARAL_STATS *stats) { // Crée une sémaphore qui sera utilisée comme mutex pour empêcher plusieurs // processus générateurs de supprimer en même temps une bordure entre deux // groupes de cellules qui sont partagés par plusieurs processus. // Tous les accès aux cellules partagées (limitrophes) ainsi qu'à l'ensemble // de leurs groupes respectifs devront se faire en ayant acquis cette mutex. int sem_labyrinth = semget(IPC_PRIVATE, 1, 0600); assert (sem_labyrinth != -1); semsignal(sem_labyrinth); // Initialise à 1 la sémaphore. // Ouvre une mémoire partagée pour stocker les statistiques de l'exécution // parallèle. int shm_stats = shmget(IPC_PRIVATE, sizeof (PARAL_STATS), 0600); assert (shm_stats != -1); PARAL_STATS *sh_stats = (PARAL_STATS *) shmat(shm_stats, NULL, 0); assert (sh_stats != (PARAL_STATS *) -1); sh_stats->hits = 0; sh_stats->misses = 0; // Crée une mémoire partagée pour stocker le labyrinthe. int shm_labyrinth = shmget( IPC_PRIVATE, sizeof (CELL) * LABYRINTH_SIZE * LABYRINTH_SIZE, 0600 ); assert (shm_labyrinth != -1); LABYRINTH labyrinth = (LABYRINTH) shmat(shm_labyrinth, NULL, 0); assert (labyrinth != (LABYRINTH) -1); init_labyrinth(labyrinth); // Démarre les quatre processus de génération (ne démarre que 3 processus // supplémentaires). int m = LABYRINTH_SIZE / 2; fork_generator(sem_labyrinth, shm_stats, shm_labyrinth, 0, 1, 1, 0); fork_generator(sem_labyrinth, shm_stats, shm_labyrinth, m, 1, m, 0); fork_generator(sem_labyrinth, shm_stats, shm_labyrinth, 0, m, 1, m); generator (sem_labyrinth, shm_stats, shm_labyrinth, m, m, m, m); // Attend que toutes les cases des trois enfants soient toutes de la // même couleur. for (int i = 0; i < 3; i++) { int child_status; wait(&child_status); assert (child_status == EXIT_SUCCESS); } // Copie le résultat hors de la mémoire partagée. LABYRINTH ret_labyrinth = (LABYRINTH) malloc( sizeof (CELL) * LABYRINTH_SIZE * LABYRINTH_SIZE ); memcpy( ret_labyrinth, labyrinth, sizeof (CELL) * LABYRINTH_SIZE * LABYRINTH_SIZE ); // Récupère les statistiques. if (stats != NULL) *stats = *sh_stats; // Ferme les IPCs. assert (semctl(sem_labyrinth, 0, IPC_RMID) != -1); assert (shmctl(shm_stats, IPC_RMID, NULL) != -1); assert (shmctl(shm_labyrinth, IPC_RMID, NULL) != -1); return ret_labyrinth; }
int main(int argc, char** argv) { printf("bus %s started\n", argv[1]); fflush(stdout); int semid, shmid; struct Common* shared; semid = semget(getSemKey(), NUM_SEMS, 0777); shmid = shmget(getSemKey(), 0, 0); shared = (struct Common *)shmat(shmid, 0, 0); semwait(semid, SEM_GATE_EMPTY); printf("Bus %s has arrived\n", argv[1]); fflush(stdout); semsignal(semid, SEM_BUS_BOARDABLE); semwait(semid, SEM_MUTEX); time_t sleep_time = shared->departure_time - time(NULL); semsignal(semid, SEM_MUTEX); printf("Bus %s Sleeping until departure\n", argv[1]); fflush(stdout); sleep(sleep_time); semwait(semid, SEM_MUTEX); printf("Bus %s: last call for boarding\n", argv[1]); fflush(stdout); while(1) { semwait(semid, SEM_CAN_BOARD); if(shared->tickets_sold == shared->boarded) { semsignal(semid, SEM_CAN_BOARD); break; } semsignal(semid, SEM_CAN_BOARD); // wait another second to give customer // threads a chance to finish boarding sleep(1); } semwait(semid, SEM_BUS_BOARDABLE); // release passengers to the gate that were waiting for the next bus printf("Bus %s releasing next bus queue\n", argv[1]); fflush(stdout); int i; for(i=0; i<shared->next_bus_tickets; i++) { semsignal(semid, SEM_NEXT_BUS_QUEUE); } // reset the ticket variables for the next bus printf("Bus %s preparing shared variables for next bus\n", argv[1]); fflush(stdout); // if there were customers waiting for the next, next bus or beyond // release some of them for(i=0; i<shared->tickets_sold; i++) { semsignal(semid, SEM_MAX_CUSTOMERS); } shared->tickets_sold = shared->next_bus_tickets; shared->next_bus_tickets = 0; shared->departure_time += BUS_PERIOD; shared->boarded = 0; semsignal(semid, SEM_MUTEX); semsignal(semid, SEM_GATE_EMPTY); printf("Bus %s is departing\n", argv[1]); fflush(stdout); return 0; }