/******************************************************************************/ /** @file "Pthreads/C/Primer 6 - mutexin/dining_savages.c" @brief problem: "Ljudozderi za ruckom" Pleme ljudoždera jede zajedničku veceru iz kazana koji može da primi BROJ_PORCIJA kuvanih misionara. Kada ljudožder poželi da ruča onda se on sam posluži iz zajednickog kazana, ukoliko kazan nije prazan. Ukoliko je kazan prazan ljudožer budi kuvara i sačeka dok kuvar ne napuni kazan. Nije dozvoljeno buditi kuvara ukoliko se nalazi bar malo hrane u kazanu.@n @n @author Miloš Gligorić @author Andrija Bosnjaković */ /******************************************************************************/ #include #include #include /******************************************************************************/ /** @struct Caldron @brief Instanca ove strukture biće deljena izmedju ljudoždera i kuvara. Struktura koja opisuje kazan u kome se nalazi odgovarajući broj porcija. Instanca ove struktura biće deljena izmedju ljudoždera koji se iz tog kazana poslužuju i kuvara koji po potrebi dopuni kazan. */ typedef struct { /** @property portions @brief Broj porcija u kazanu. */ int portions; } Caldron; /******************************************************************************/ /** @def NUM_OF_PORTIONS @brief Broj porcija koliko može stati u kazan. */ #define NUM_OF_PORTIONS 10 /** @def NUM_OF_SAVAGES @brief Broj ljudoždera u sistemu. */ #define NUM_OF_SAVAGES 5 /** @def NUM_OF_PORTIONS_PER_LUNCH @brief Broj porcija za jednog ljudoždera po ručku. */ #define NUM_OF_PORTIONS_PER_LUNCH 20 /** @var caldron @brief Konkretan kazan. */ Caldron caldron = {0}; /** @var savages @brief Niti ljudoždera. */ pthread_t savages[NUM_OF_SAVAGES]; /** @var cook @brief Nit kuvara. */ pthread_t cook; /** @var mutex_caldron @brief Medjusobno isključenje pristupa kazanu. */ pthread_mutex_t mutex_caldron; /** @var cond_savage @brief Blokiranje ljudoždera ukoliko je prazan kazan */ pthread_cond_t cond_savage; /** @var cond_cook @brief Kuvar spava dok kazan ne postane prazan */ pthread_cond_t cond_cook; /******************************************************************************/ /** @brief Opis ručka ljudoždera */ void savage_eat(void) { int i; for (i = 0; i < 1000000; i++); } /** @brief Posao koji obavljaju ljudožderi. @param id Generički pokazivač na broj niti (id). @return void* Vraća rezultat izvršavanja rutine ptrhead_exit(). Funkcija Savage se poziva kada se nit ljudoždera aktivira. Pristup kazanu je preko promenljive caldron tipa Caldron. Pre pristupa kazanu potrebno je da svaki ljudožder traži ekskluzivno pravo pristupa. Ukoliko je kazan prazan ljudožder budi kuvara i čeka da kuvar završi sa pripremom narednih porcija. */ void* Savage(void *id) { int my_id = (int) id; int at_portions = 0; while (at_portions < NUM_OF_PORTIONS_PER_LUNCH) { // trazi pravo pristupa kazanu pthread_mutex_lock(&mutex_caldron); // proverava da li je kazan prazan, ako jestu budi kuvara i ceka while (caldron.portions == 0) { pthread_cond_signal(&cond_cook); pthread_cond_wait(&cond_savage, &mutex_caldron); } // uzima jednu porciju caldron.portions--; printf("savage %d at %d portions\n", my_id, at_portions); // oslobadja pristup kazanu pthread_mutex_unlock(&mutex_caldron); savage_eat(); at_portions++; } pthread_exit(NULL); } /** @brief Posao koji obavlja kuvar => kuvanje :) @param arg Generički pokazivač na broj niti. @return void* Vraća rezultat izvršavanja rutine ptrhead_exit(). Funkcija Cook se poziva kada se aktivira nit kuvara. Za pristup kazanu upotrebljava se deljena promenljva caldron tipa Caldron. Pre pristupa ovoj promenljivoj potrebno je obezbediti ekskluzivan pristup do iste. Za ove potrebe koristi se mutex_caldron mutex. Ukoliko kazan nije prazan kuvar treba da odmara. Nit se blokira na cond_cook uslovnoj promenljivoj. Kuvar završava svoj posao onog trenutka kada je skuvao dovoljno porcija za sve ljudoždere (NUM_OF_SAVAGES * NUM_OF_PORTIONS_PER_LUNCH). */ void* Cook(void *arg) { int cooked_portions = 0; // angazovan je sve dok neskuva dovoljno porcija za sve ljudozdere while (cooked_portions < NUM_OF_SAVAGES * NUM_OF_PORTIONS_PER_LUNCH) { // ekskluzivni pristup kazanu pthread_mutex_lock(&mutex_caldron); // ukoliko kazan nije prazna kuvar mora sacekati da se kazan isprazni if (caldron.portions > 0) pthread_cond_wait(&cond_cook, &mutex_caldron); // izvrsava kuvanje caldron.portions = NUM_OF_PORTIONS; printf("cooking new portions\n"); // javlja svim ljudozderima da je skuvao nove porcije pthread_cond_broadcast(&cond_savage); // oslobadja pristup kazanu pthread_mutex_unlock(&mutex_caldron); cooked_portions += NUM_OF_PORTIONS; } pthread_exit(NULL); } /******************************************************************************/ /** @brief Glavni program. @param argc Broj argumenata komandne linije. @param argv Argumenti komandne linije. @return int Rezultat izvršavanja programa. Glavni program kreira niti ljudoždera i kuvara. Takodje, vrši i inicijalizaciju mutex promenljive za pristup kazanu kao i uslovnih promenljivih. Sve niti se kreiraju kao joinable. Na kraju se vrši oslobadjanje resursa. */ int main(int argc, char *argv[]) { int i; void *status; pthread_attr_t attr; #ifdef WIN32 pthread_win32_process_attach_np(); #endif // vrsi inicijalizaciju mutex i cond promenljvih pthread_mutex_init(&mutex_caldron, NULL); pthread_cond_init(&cond_savage, NULL); pthread_cond_init(&cond_cook, NULL); // sve niti ce biti kreirane kao joinable pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); // kreira kuvara i ljudozdere pthread_create(&cook, &attr, Cook, NULL); for (i = 0; i < NUM_OF_SAVAGES; i++) { pthread_create(&savages[i], &attr, Savage, (void*)i); } // oslobadja resurse pthread_attr_destroy(&attr); // ceka sve niti da zavrse posao for (i = 0; i < NUM_OF_SAVAGES; i++) { pthread_join(savages[i], &status); } pthread_join(cook, &status); // oslobadja resurse pthread_mutex_destroy(&mutex_caldron); pthread_cond_destroy(&cond_savage); pthread_cond_destroy(&cond_cook); pthread_exit(NULL); #ifdef WIN32 pthread_win32_process_detach_np(); #endif }