/* * Labyrinthe.c * * Main labyrinthe file * * Gouverneur Th. - Cuisinier Gi. */ #include #include #include #include #include #include #include #include #include #include "log.h" #include "Ecran.h" #include "Laby.h" #include "Labyrinthe.h" #include "self.h" #include "pathfinder.h" #define GOTOXY(X,Y) printf("\033[%d;%dH",Y,X); fflush(stdout) extern int Laby[NB_LIGNES][NB_COLONNES]; pthread_t ThreadPion[NB_MAX_PIONS]; pthread_t ThreadChrono; pthread_t ThreadCompteur; pthread_t ThreadMaitre; pthread_t ThreadManual; pthread_mutex_t MutexMap; pthread_mutex_t MutexNb; pthread_mutex_t MutexManual; pthread_cond_t CondNb; pthread_cond_t CondManual; pthread_key_t PKey; /* compteurs: */ unsigned int NbPion; unsigned int NbPionSortis; /* autres: */ const Pos end = { LIG_SORTIE, COL_SORTIE }; const Pos begin = { LIG_ENTREE, COL_ENTREE }; const Pos bad = { -1, -1 }; unsigned int tBegin; int ld; // Laby.log int sd; // Sorties.log /* * Fonction principale. * */ extern "C" { int main() { /* init var&mutexes */ NbPion = NbPionSortis = 0; tBegin = time(0); pthread_mutex_init(&MutexMap, NULL); pthread_mutex_init(&MutexNb, NULL); pthread_mutex_init(&MutexManual, NULL); pthread_cond_init(&CondNb, NULL); pthread_cond_init(&CondManual, NULL); pthread_key_create(&PKey, NULL); ld = OpenLog("Laby.log"); sd = OpenLog("Sorties.log"); sigset_t sset; AfficheLaby(); /* avant les threads sinon ca bug :) */ if (pthread_create(&ThreadManual, NULL, pthread_manual, NULL) != 0) { WriteLog(ld, "Cannot start thread for manual.. exiting.."); return -1; } if (pthread_create(&ThreadChrono, NULL, pthread_chrono, NULL) != 0) { WriteLog(ld, "Cannot start thread for chrono.. exiting.."); return -1; } if (pthread_create(&ThreadCompteur, NULL, pthread_compteur, NULL) != 0) { WriteLog(ld, "Cannot start thread for compteur.. exiting.."); return -1; } if (pthread_create(&ThreadMaitre, NULL, pthread_maitre, NULL) != 0) { WriteLog(ld, "Cannot start thread for maitre.. exiting.."); return -1; } sigfillset(&sset); pthread_sigmask(SIG_SETMASK, &sset, NULL); pthread_join(ThreadMaitre, NULL); exit(0); } } /* * But: Gerer l'ajout d'un thread pion. * */ int addPion(void) { pthread_mutex_lock(&MutexNb); if (NbPion >= NB_MAX_PIONS) { pthread_mutex_unlock(&MutexNb); return -1; } pthread_mutex_unlock(&MutexNb); while(1) { pthread_mutex_lock(&MutexMap); if (isFree(begin)) { pthread_mutex_unlock(&MutexMap); pthread_mutex_lock(&MutexNb); if (pthread_create(&ThreadPion[NbPion], NULL, pthread_pion, NULL) != 0) { WriteLog(ld, "Cannot create pthread..."); return -1; } pthread_detach(ThreadPion[NbPion]); NbPion++; pthread_mutex_unlock(&MutexNb); pthread_cond_signal(&CondNb); return 0; } else pthread_mutex_unlock(&MutexMap); MicroWait(20000000); } } /* * But: Faire voyager le pion de l'entree vers la sortie. * */ extern "C" { void * pthread_pion(void *arg) { Pos cur = begin; Pos old = begin; Pos next; IThread *ident; sigset_t sset; pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL); sigfillset(&sset); pthread_sigmask(SIG_SETMASK, &sset, NULL); ident = (IThread *) malloc(sizeof(IThread)); ident->Num = pthread_self(); ident->Entree = time(0) - tBegin; ident->State = SNOK; #ifdef PATHFINDER ident->savPos = bad; ident->count = -1; ident->pf = NULL; #endif pthread_setspecific(PKey, ident); pthread_cleanup_push(pthread_clean, NULL); WriteLog(ld, "Thread lance"); pthread_mutex_lock(&MutexMap); changePos(NULL, &cur); pthread_mutex_unlock(&MutexMap); while (42) { #ifdef DEBUG WriteLog(ld, "[%d] TOP", ident->Num); #endif old = cur; if (!posCmp(cur, end)) break; #ifdef DEBUG_MUTEX WriteLog(ld, "[%d] Try to Lock map", ident->Num); #endif pthread_mutex_lock(&MutexMap); #ifdef DEBUG_MUTEX WriteLog(ld, "[%d] Locked MAP", ident->Num); #endif if (!posCmp((next = getNextPos(cur)), bad)) { #ifdef DEBUG WriteLog(ld, "[%d] Cannot move for now...", ident->Num); #endif pthread_mutex_unlock(&MutexMap); } else { changePos(&old, &next); cur = next; pthread_mutex_unlock(&MutexMap); #ifdef DEBUG_MUTEX WriteLog(ld, "[%d] UnLocked MAP", ident->Num); #endif #ifdef DEBUG WriteLog(ld, "[%d] MOVE EFFECTUE", ident->Num); #endif } MicroWait(200000000); } pthread_mutex_lock(&MutexMap); EffacePion(cur); Laby[cur.lig][cur.col] = 0; pthread_mutex_unlock(&MutexMap); pthread_mutex_lock(&MutexNb); NbPionSortis++; pthread_mutex_unlock(&MutexNb); pthread_cond_signal(&CondNb); ident->State = SOK; WriteLog(ld, "Thread exit"); pthread_exit(0); pthread_cleanup_pop(0); /* MANDATORY, this is a macro function like push one.. and they should be here together... */ return NULL; } } /* * But: Effacer l'ancienne position a l'ecran, * replacer la nouvelle, et effectuer ces * modification dans la grille en memoire. */ void changePos(Pos *old, Pos *niouw) { if (niouw) { if (old) { unsetPos(*old); EffacePion(*old); GOTOXY(0,11); } setPos(*niouw); DessinePion(*niouw); GOTOXY(0,11); } } /* * But: renvoie 1 si la pos est libre, 0 sinon * */ int isFree(Pos p) { if (p.lig < 0 || p.lig > NB_LIGNES) return 0; if (p.col < 0 || p.col > NB_COLONNES) return 0; if (Laby[p.lig][p.col] == 0) return 1; else return 0; } /* * But: Set la position a "occupe" * */ void setPos(Pos p) { if (p.lig < 0 || p.lig > NB_LIGNES) return; if (p.col < 0 || p.col > NB_COLONNES) return; Laby[p.lig][p.col] = 4; } /* * But: Set la position a "libre" * */ void unsetPos(Pos p) { if (p.lig < 0 || p.lig > NB_LIGNES) return; if (p.col < 0 || p.col > NB_COLONNES) return; Laby[p.lig][p.col] = 0; } /* * But: Compare deux positions. * */ int posCmp(const Pos p1, const Pos p2) { return (p1.lig - p2.lig) + (p1.col - p2.col); } /* * But: Retourne la position suivante. * */ Pos getNextPos(Pos cur) { #ifdef PATHFINDER IThread *ident = (IThread *) pthread_getspecific(PKey); Pos ret = bad; if (!ident->pf) ident->pf = new Trajet(); if (ident->count == -1) { WriteLog(ld, "[%d] Trying to find path...", pthread_self()); if (ident->pf->FindPath(&Laby[0][0], NB_COLONNES, NB_LIGNES, COL_SORTIE, LIG_SORTIE, cur.col, cur.lig) == -1) { WriteLog(ld, "[%d] Cannot found a good path, trying later dude...", pthread_self()); return bad; } ident->pf->ResetMove(); ident->count = ident->pf->GetNb(); WriteLog(ld, "[%d] Path finded... %d moves", pthread_self(), ident->count); } if (ident->count > 0) { if (posCmp(ident->savPos, bad)) { WriteLog(ld, "[%d] Return old move...", pthread_self()); ret = ident->savPos; ident->savPos = bad; ident->count--; return ret; } ident->pf->GetMove(&ret.col, &ret.lig); WriteLog(ld, "[%d] Move get...%d-%d", pthread_self(), cur.lig, cur.col); if (isFree(ret)) { WriteLog(ld, "[%d] we can go to this move...", pthread_self()); ident->count--; return ret; } else { WriteLog(ld, "[%d] we cannot go to this move...", pthread_self()); ident->savPos = ret; return bad; } } else if (ident->count == 0) { ident->count = -1; } // delete pf; return ret; #else #ifdef DEBUG IThread *ident = pthread_getspecific(PKey); #endif Pos ret = bad; int Valid[4] = { 0,0,0,0 }, NbValid = 0; cur.col++; if (isFree(cur)) Valid[NbValid++] = DROITE; cur.col-=2; if (isFree(cur)) Valid[NbValid++] = GAUCHE; cur.col++; cur.lig--; if (isFree(cur)) Valid[NbValid++] = HAUT; cur.lig+=2; if (isFree(cur)) Valid[NbValid++] = BAS; cur.lig--; if (!NbValid) { #ifdef DEBUG WriteLog(ld, "[%d] 0 choices of moves...", ident->Num); #endif return ret; } ret = cur; srand(time(0)); switch (Valid[rand()%(NbValid)]) { case DROITE: ret.col++; #ifdef DEBUG WriteLog(ld, "[%d] MOV DROITE", ident->Num); #endif break; case GAUCHE: ret.col--; #ifdef DEBUG WriteLog(ld, "[%d] MOV GAUCHE", ident->Num); #endif break; case HAUT: ret.lig--; #ifdef DEBUG WriteLog(ld, "[%d] MOV HAUT", ident->Num); #endif break; case BAS: ret.lig++; #ifdef DEBUG WriteLog(ld, "[%d] MOV BAS", ident->Num); #endif break; default: cur = bad; break; } //WriteLog(ld, "test"); return ret; #endif } /* * But: Attendre un nombre de nanoseconde donne * */ void MicroWait(unsigned int w) { struct timespec tm; tm.tv_sec = 0; tm.tv_nsec = w; nanosleep(&tm, NULL); } /* * But: Afficher le temps écoulé depuis le début du programme. * */ extern "C" { void * pthread_chrono (void *arg) { sigset_t sset; struct sigaction sa; sa.sa_handler = handler_alrm; sigemptyset(&(sa.sa_mask)); sigaction(SIGALRM, &sa, NULL); sigfillset(&sset); sigdelset(&sset, SIGALRM); pthread_sigmask(SIG_SETMASK, &sset, NULL); #ifdef DEBUG_CHRONO WriteLog(ld, "[%d] Debut thread chrono", pthread_self()); #endif GOTOXY(50, 2); printf("Temps d'execution: "); fflush(stdout); while ( 42 ) { alarm(1); pause(); } return NULL; } } /* * But: Mise a jour du temps d'execution. * */ extern "C" { void handler_alrm(int s) { #ifdef DEBUG_CHRONO WriteLog(ld, "[%d] Handler alarm...", pthread_self()); #endif GOTOXY(69,2); printf("%d", time(0) - tBegin); GOTOXY(0,0); #ifdef DEBUG_CHRONO WriteLog(ld, "[%d] Fin handler alarm...", pthread_self()); #endif } } /* * But: Afficher les compteurs et mettre a jour leur affichage. * */ extern "C" { void * pthread_compteur(void *arg) { sigset_t sset; sigfillset(&sset); pthread_sigmask(SIG_SETMASK, &sset, NULL); pthread_mutex_lock(&MutexNb); while(1) { GOTOXY(25, 3); printf("Entres: %2d", NbPion); fflush(stdout); GOTOXY(25, 4); printf("Sortis: %2d", NbPionSortis); fflush(stdout); GOTOXY(25, 5); printf("Restant: %2d", NbPion - NbPionSortis); fflush(stdout); GOTOXY(0,0); pthread_cond_wait(&CondNb, &MutexNb); } return NULL; } } /* * But: Gere le labyrinthe. * */ extern "C" { void * pthread_maitre(void *arg) { int fd; char buf[10]; sigset_t sset; struct sigaction sa; sa.sa_handler = handler_int; sigemptyset(&(sa.sa_mask)); sigaction(SIGINT, &sa, NULL); sa.sa_handler = handler_quit; sigemptyset(&(sa.sa_mask)); sigaction(SIGQUIT, &sa, NULL); sigfillset(&sset); sigdelset(&sset, SIGINT); sigdelset(&sset, SIGQUIT); pthread_sigmask(SIG_SETMASK, &sset, NULL); /* ecriture du pid */ fd = open("Laby.pid", O_CREAT | O_TRUNC | O_APPEND | O_WRONLY, 0700); sprintf(buf, "%d\n", getpid()); write(fd, &buf, strlen(buf)+1); close(fd); while(42) pause(); return NULL; } } /* * But: Gere le SIGINT * */ extern "C" { void handler_int(int s) { #ifdef DEBUG_MAITRE WriteLog(ld, "[%d] Handler SIGINT...", pthread_self()); #endif addPion(); } /* * But: Gere le SIGQUIT * */ void handler_quit(int s) { int i; #ifdef DEBUG_MAITRE WriteLog(ld, "[%d] Handler SIGQUIT...", pthread_self()); #endif for (i=0;iSortie = time(0) - tBegin; WriteLog(sd, "Tread [%d]\t:: entree = %4d, sortie = %4d, temps = %4d, %s", ident->Num, ident->Entree, ident->Sortie, ident->Sortie - ident->Entree, (ident->State==SOK)?"Termine correctement":"A ete kille"); } else { WriteLog(sd, "Thread [%d]\t:: Unable to get specific... end at %d", pthread_self(), tBegin); } #ifdef DEBUG_CLEAN WriteLog(ld, "[%d] Clean called...", pthread_self()); #endif } } /* * But: Thread de controle de pion manuel. * */ extern "C" { void * pthread_manual(void *arg) { sigset_t sset; int sig, fd, pid; struct sigaction sa; char buf[10]; Pos cur = begin; Pos old = begin; Pos next; State *SThread = (State *) malloc(sizeof(State)); SThread->cur = &cur; SThread->old = &old; pthread_setspecific(PKey, SThread); #ifdef DEBUG_MANUAL WriteLog(ld, "[%d] Enter manual thread", pthread_self()); #endif /* ecriture du pid */ fd = open("Manual.pid", O_CREAT | O_TRUNC | O_APPEND | O_WRONLY, 0700); sprintf(buf, "%d\n", getpid()); write(fd, &buf, strlen(buf)+1); close(fd); sa.sa_handler = handler_move; sigemptyset(&(sa.sa_mask)); sigaction(SIGGAUCHE, &sa, NULL); sa.sa_handler = handler_move; sigemptyset(&(sa.sa_mask)); sigaction(SIGDROITE, &sa, NULL); sa.sa_handler = handler_move; sigemptyset(&(sa.sa_mask)); sigaction(SIGHAUT, &sa, NULL); sa.sa_handler = handler_move; sigemptyset(&(sa.sa_mask)); sigaction(SIGBAS, &sa, NULL); sigemptyset(&(sa.sa_mask)); sa.sa_handler = handler_start; sigaction(SIGSTART, &sa, NULL); sigfillset(&sset); sigdelset(&sset, SIGGAUCHE); sigdelset(&sset, SIGDROITE); sigdelset(&sset, SIGHAUT); sigdelset(&sset, SIGBAS); sigdelset(&sset, SIGSTART); pthread_sigmask(SIG_SETMASK, &sset, NULL); #ifdef DEBUG_MANUAL WriteLog(ld, "[%d] Mask & signal set", pthread_self()); #endif pthread_mutex_lock(&MutexManual); pthread_cond_wait(&CondManual, &MutexManual); #ifdef DEBUG_MANUAL WriteLog(ld, "[%d] Starting listen...", pthread_self()); #endif while(1) { pthread_mutex_lock(&MutexMap); if (isFree(begin)) { // pthread_mutex_unlock(&MutexMap); /* pthread_mutex_lock(&MutexNb); pthread_detach(ThreadPion[NbPion]); NbPion++; pthread_mutex_unlock(&MutexNb); pthread_cond_signal(&CondNb); */ // pthread_mutex_lock(&MutexMap); changePos(NULL, &cur); pthread_mutex_unlock(&MutexMap); break; } else pthread_mutex_unlock(&MutexMap); MicroWait(20000000); } while (42) { pause(); if (!posCmp(cur, end)) { pthread_mutex_lock(&MutexMap); EffacePion(cur); Laby[cur.lig][cur.col] = 0; pthread_mutex_unlock(&MutexMap); WriteLog(sd, "Tread [%d]\t:: Manual thread Exits..", pthread_self()); fd = open("Control.pid", 0); read(fd, &buf, sizeof(buf)); pid = atoi(buf); kill(pid, SIGHUP); break; } } pthread_exit(0); return NULL; } } /* * But: Gere le SIGSTART et lance un thread de pion manuel. * */ extern "C" { void handler_start(int s) { #ifdef DEBUG_MANUAL WriteLog(ld, "[%d] Entering handler_start, launching manual thread", pthread_self()); #endif pthread_cond_signal(&CondManual); } /* * But: tout les handlers de directions: * */ void handler_move (int move) { Pos next; State *SThread = (State *)pthread_getspecific(PKey); #ifdef DEBUG_MANUAL WriteLog(ld, "[%d] Received %d move...", pthread_self(), move); #endif next = *SThread->cur; switch(move) { case SIGGAUCHE: WriteLog(ld, "[%d] Move left.", pthread_self()); next.col--; break; case SIGDROITE: WriteLog(ld, "[%d] Move right.", pthread_self()); next.col++; break; case SIGHAUT: WriteLog(ld, "[%d] Move up.", pthread_self()); next.lig--; break; case SIGBAS: WriteLog(ld, "[%d] Move down.", pthread_self()); next.lig++; break; default: WriteLog(ld, "[%d] Unknown move...", pthread_self()); break; } pthread_mutex_lock(&MutexMap); if (isFree(next)) { *SThread->old = *SThread->cur; changePos(SThread->old, &next); *SThread->cur = next; } pthread_mutex_unlock(&MutexMap); } }