/* * Programme simulant la gestion d'une imprimante * * Paradigme de producteur/consommateur avec terminaison de l'application * NB : cette version faire en sorte qu'à la fin de l'application, chaque * thread consommateur appelle pthread_exit() * */ #include #include #include #include #include #include #include #define NOMFICHIERMAX 80 /* Nom signifiant a un thread consommateur qu'il doit faire pthread_exit. * NB : la valeur "" a ete choisie de maniere a etre sur qu'en temps normal, * ce nom ne peut pas etre communique au thread consommateur. */ #define NOM_SIGNIFIANT_ARRET_CONSOMMATEUR "" #define NBPOOL 3 #define NBTAMPON 2 /* les zones tampons sont stockees a des endroits differents pour ne pas partager une meme zone entre plusieurs threads */ char tampon[NBTAMPON][NOMFICHIERMAX]; pthread_mutex_t mutex_imprimante; pthread_mutex_t mutex_tampon; int iExtrait = 0; int iDepot = 0; sem_t placeDispo; sem_t infoPrete; int nbImpressionsEffectuees = 0; void impression(char *nomFic){ pthread_mutex_lock(&mutex_imprimante); printf("Debut d'impression de \"%s\"...\n", nomFic); sleep(5); /* Simule la lenteur de l'impression */ printf("...Fin d'impression de \"%s\"...\n", nomFic); nbImpressionsEffectuees++; pthread_mutex_unlock(&mutex_imprimante); } void P(sem_t *semaphore) { int retcode = sem_wait(semaphore); if (retcode == -1) { /* errno ERANGE, EBUSY */ perror("sem_wait"); exit(EXIT_FAILURE); } } void V(sem_t *semaphore) { int retcode; retcode = sem_post(semaphore); if (retcode == -1) { /* errno ERANGE, EBUSY */ perror("sem_post"); exit(EXIT_FAILURE); } } int producteur() { char nomFic[NOMFICHIERMAX]; printf("Nom du fichier a imprimer (taper '0' pour terminer) ? "); scanf("%s", nomFic); if (strcmp(nomFic,"0") != 0) { /* calcul de l'information */ P(&placeDispo); /* depot de l'information */ /* pas de protection avec mutex car un seul producteur * et acces producteur/consommateur proteges avec * semaphore. */ strncpy(tampon[iDepot], nomFic, NOMFICHIERMAX); iDepot = (iDepot + 1) % NBTAMPON ; V(&infoPrete); return 1; } else return 0; } void *consommateur(void *args) { char nomFic[NOMFICHIERMAX]; while (1) { P(&infoPrete); /* extraction de l'information */ /* protection entre consommateurs avec mutex */ pthread_mutex_lock(&mutex_tampon); strcpy(nomFic, tampon[iExtrait]); iExtrait = (iExtrait + 1) % NBTAMPON; pthread_mutex_unlock(&mutex_tampon); V(&placeDispo); /* Utilisation */ if (strcmp(nomFic, NOM_SIGNIFIANT_ARRET_CONSOMMATEUR) == 0) break; impression(nomFic); } pthread_exit(0); } void attenteImpression() { int nbImpressions = 0; do { if (!producteur()) break; nbImpressions ++; } while (1); printf("%d demandes d'impressions recues\n", nbImpressions); } int main(){ int i; pthread_t threadPool[NBPOOL]; sem_init(&placeDispo, 0, NBTAMPON); sem_init(&infoPrete, 0, 0); pthread_mutex_init(&mutex_imprimante, NULL); pthread_mutex_init(&mutex_tampon, NULL); for (i = 0; i < NBPOOL ; i++) { int retcode; retcode = pthread_create(&(threadPool[i]), NULL, consommateur, NULL); if (retcode != 0) { perror("pthread_create"); exit(EXIT_FAILURE); } } attenteImpression(); /* On indique a tous les threads consommateurs qu'ils doivent se terminer. * NB : comme on passe par tampon pour faire ca et que tampon est FIFO, * nous n'avons pas besoin d'attendre d'etre sur qu'il n'y a plus aucun * fichier dont l'impression n'est pas encore lancee, i.e. qu'il n'y a * plus aucun nom de fichier dans tampon. Or, ce sera le cas pour la * version 2. */ printf("Debut signal a tous les processus consommateurs qu'ils doivent s'arreter\n"); for (i = 0 ; i < NBPOOL ; i++) { P(&placeDispo); /* depot de l'information */ /* pas de protection avec mutex car un seul producteur * et acces producteur/consommateur proteges avec * semaphore. */ strncpy(tampon[iDepot], NOM_SIGNIFIANT_ARRET_CONSOMMATEUR, NOMFICHIERMAX); iDepot = (iDepot + 1) % NBTAMPON ; V(&infoPrete); } printf("OK : tous les processus consommateur ont recu ou vont recevoir l'information qu'ils doivent s'arreter\n"); for (i = 0; i < NBPOOL; i ++) { int retcode; void * thread_return; retcode = pthread_join(threadPool[i], &thread_return); if (retcode != 0) { perror("pthread_join"); exit(EXIT_FAILURE); } if (thread_return == 0) printf("thread consommateur a fait pthread_exit(0)\n"); else printf("thread_return != 0\n"); } pthread_mutex_destroy(&mutex_imprimante); pthread_mutex_destroy(&mutex_tampon); sem_destroy(&placeDispo); sem_destroy(&infoPrete); printf("Au revoir (%d impressions effectuees)\n", nbImpressionsEffectuees); return EXIT_SUCCESS; }