/* * Programme simulant la gestion d'une imprimante * * Paradigme de producteur/consommateur avec terminaison de l'application * NB : cette version utilise le fait qu'un thread peut annuler un autre * thread. * */ #include #include #include #include #include #include #include #define NOMFICHIERMAX 80 #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) { int etat_prec; pthread_testcancel(); P(&infoPrete); pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &etat_prec); /* 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 */ impression(nomFic); pthread_setcancelstate(etat_prec, NULL); } } void attenteImpression() { int nbImpressions = 0; do { if (!producteur()) break; nbImpressions ++; } while (1); /* On attend 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. Cela revient a attendre que toutes les cases de tampon * sont disponibles pour y ecrire un nom de fichier a imprimer. */ printf("Debut attente qu'il n'y a plus aucun fichier dont l'impression n'est pas encore lancee\n"); int i; for (i = 0 ; i < NBTAMPON ; i++) { P(&placeDispo); } printf("OK : plus aucun fichier dont l'impression n'est pas encore lancee.\n"); 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(); printf("Annulation des threads d'impression\n"); for (i = 0; i < NBPOOL; i ++) { int retcode; retcode = pthread_cancel(threadPool[i]); if (retcode != 0) { perror("pthread_cancel"); exit(EXIT_FAILURE); } } 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 == PTHREAD_CANCELED) printf("thread consommateur annule\n"); else printf("thread_return != PTHREAD_CANCELED\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; }