// Programmet bruker ISO C 2011 (ISO/IEC 9899:2011) og Pthreads (IEEE Std 1003.1c-1995). -*- coding: utf-8 -*- // Løsning laget av Trond Endrestøl , 2014-11-06. // $Ximalas$ #include #include #include #include #include #include #include #if __STDC_VERSION__ >= 201112L #include #else #define noreturn /**/ #endif #define ANTALL_LIVSTIDSFANGER 19U static unsigned antallBesok[ANTALL_LIVSTIDSFANGER]; static size_t tellendeLivstidsfange; static unsigned antallLivstidsfanger; static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t cond = PTHREAD_COND_INITIALIZER; // Ingen må forandre på bryterne uten å ha låst mutex først. static unsigned brytere[2U]; noreturn void *livstidsfange(void *arg); void visResultater(void); void signalhandler(int sig); noreturn int main (int argc, char **argv) { size_t i; pthread_t tid = 0; atexit(visResultater); signal(SIGINT, signalhandler); srandomdev(); memset((void *)&antallBesok, 0, sizeof(antallBesok)); brytere[0] = random() & 1; brytere[1] = random() & 1; tellendeLivstidsfange = random() % ANTALL_LIVSTIDSFANGER; puts("maintråden venter på å få låst mutex"); pthread_mutex_lock(&mutex); puts("mutex er låst av maintråden"); for (i = 0; i < ANTALL_LIVSTIDSFANGER; i++) { printf("maintråden oppretter livstidsfange %2zu\n", i + 1); if ( ( errno = pthread_create(&tid, NULL, livstidsfange, (void *)i)) != 0) { fprintf(stderr, "%s: pthread_create(&tid, NULL, livstidsfange, (void *)%2zu) = %s (%d)\n", argv[0], i, strerror(errno), errno); exit(1); } // if } // for // maintråden påtar seg rolla som fengselsdirektøren. while (1) { puts("fengselsdirektøren låser opp mutex"); pthread_mutex_unlock(&mutex); puts("mutex er låst opp av fengselsdirektøren"); puts("fengselsdirektøren signalerer livstidsfangene"); pthread_cond_signal(&cond); puts("signal er sendt fra fengselsdirektøren"); puts("fengselsdirektøren venter på å få låst mutex"); pthread_mutex_lock(&mutex); puts("mutex er låst av fengselsdirektøren"); } // while } // main() noreturn void *livstidsfange(void *arg) { size_t i = (size_t)arg; unsigned harVippetBryter1 = 0U; unsigned bryter1HarVartOppe = 0U; unsigned forrigeGangErGyldig = 0U; unsigned forrigeGang = 0U; // Den tellende livstidsfangen kan allerede nå telle seg selv. if (i == tellendeLivstidsfange) { antallLivstidsfanger++; } // if while (1) { // Vente på tur. printf("livstidsfange %2zu venter på å få låst mutex\n", i + 1); pthread_mutex_lock(&mutex); printf("livstidsfange %2zu har låst mutex\n", i + 1); printf("livstidsfange %2zu venter på signal fra fengselsdirektøren\n", i + 1); pthread_cond_wait(&cond, &mutex); printf("livstidsfange %2zu har fått signal fra fengselsdirektøren og har låst mutex\n", i + 1); // Utføre selve simuleringen ved å sjekke brytere, m.m. antallBesok[i]++; printf("livstidsfange %2zu har besøkt rommet %u ganger\n", i + 1, antallBesok[i]); if (i == tellendeLivstidsfange) { if (brytere[0] == 0U) { printf("livstidsfange %2zu ser at bryter 1 er nede og vipper opp bryter 1\n", i + 1); brytere[0] = 1U; } // if else { brytere[0] = !brytere[0]; printf("livstidsfange %2zu vipper %s bryter 1\n", i + 1, brytere[0] == 1U ? "opp" : "ned"); if (forrigeGangErGyldig == 1U && forrigeGang == 0U) { antallLivstidsfanger++; printf("livstidsfange %2zu har talt opp %u livstidsfanger\n", i + 1, antallLivstidsfanger); if (antallLivstidsfanger == ANTALL_LIVSTIDSFANGER) { printf("livstidsfange %2zu avslutter simuleringen\n", i + 1); exit(0); } // if } // if forrigeGangErGyldig = 1U; forrigeGang = brytere[0]; } // else } // if else { // Vanlig livstidsfange. if (brytere[0] == 1U) { printf("livstidsfange %2zu ser at bryter 1 er oppe, mens bryter 2 er %s\n", i + 1, brytere[1] == 1U ? "oppe" : "nede"); bryter1HarVartOppe = 1U; brytere[1] = !brytere[1]; printf("livstidsfange %2zu vipper %s bryter 2\n", i + 1, brytere[1] == 1U ? "opp" : "ned"); } // if else { printf("livstidsfange %2zu ser at bryter 1 er nede, mens bryter 2 er %s\n", i + 1, brytere[1] == 1U ? "oppe" : "nede"); if (harVippetBryter1 == 0U && bryter1HarVartOppe == 1U) { brytere[0] = !brytere[0]; printf("livstidsfange %2zu vipper %s bryter 1\n", i + 1, brytere[0] == 1U ? "opp" : "ned"); harVippetBryter1 = 1U; } // if else { brytere[1] = !brytere[1]; printf("livstidsfange %2zu vipper %s bryter 2\n", i + 1, brytere[1] == 1U ? "opp" : "ned"); } // else } // else } // else // Signalere fengselsdirektøren (eller en vilkårlig livstidsfange). printf("livstidsfange %2zu låser opp mutex\n", i + 1); pthread_mutex_unlock(&mutex); printf("livstidsfange %2zu har låst opp mutex\n", i + 1); printf("livstidsfange %2zu signalerer de andre trådene\n", i + 1); pthread_cond_signal(&cond); printf("livstidsfange %2zu har gitt signal til de andre trådene\n", i + 1); } // while } // livstidsfange() void visResultater(void) { size_t i; fflush(stderr); fflush(stdout); fflush(stderr); fflush(stdout); puts(""); printf("livstidsfange %2zu er den tellende livstidsfangen\n\n", tellendeLivstidsfange + 1); for (i = 0; i < ANTALL_LIVSTIDSFANGER; i++) { printf("livstidsfange %2zu: antall besøk = %u\n", i + 1, antallBesok[i]); } // for fflush(stdout); fflush(stdout); } // visResultater() void signalhandler(int sig) { if (sig == SIGINT) { visResultater(); _exit(1); } // if } // signalhandler() // livstidsfanger.c