/****************************************/
/*                                      */
/*  Prof. Dr. Carsten Vogt              */
/*  Fachhochschule Koeln                */ 
/*  Fakultaet 07, Nachrichtentechnik    */
/*  http://www.nt.fh-koeln.de/vogt      */
/*                                      */
/*  Linux-C-Schnittstelle mit pthreads: */
/*  Erzeuger-Verbraucher-Problem        */
/*   mit Mutex und Bedingungsvariablen  */
/*                                      */
/****************************************/

// Unter Linux zu uebersetzen mit 'gcc -pthread ...' oder 'cc -pthread ...'

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>

#define PUFFERKAP 3
#define ERZZEIT 1 
#define VBRZEIT 3
#define GESAMTLAUFZEIT 30

pthread_mutex_t mutex;

pthread_cond_t cond_voll, cond_leer;

int puffer[PUFFERKAP+1], lese_index, schreib_index;

void print_puffer() {
 int i;
 if (lese_index==schreib_index) {
   printf(" Puffer leer\n");
   return;
 }
 printf(" Pufferinhalt:");
 if (lese_index<schreib_index)
   for (i=lese_index;i<schreib_index;i++)
    printf(" %d",puffer[i]);
  else {
   for (i=lese_index;i<PUFFERKAP+1;i++)
    printf(" %d",puffer[i]);
   for (i=0;i<schreib_index;i++)
    printf(" %d",puffer[i]);
   }
   printf("\n");
}

void incr_index(int *index) {
 *index = (*index+1)%(PUFFERKAP+1);
}

int puffer_voll() {
 return (schreib_index+1)%(PUFFERKAP+1) == lese_index;
}

int puffer_leer() {
 return schreib_index == lese_index;
}

void *erzeuger_loop(void *p)
{
   int wert = 10;
   while (1) {
    sleep(ERZZEIT);
    pthread_mutex_lock(&mutex);
    while (puffer_voll()) {
     printf("Puffer voll => Erzeuger blockiert\n");
     pthread_cond_wait(&cond_voll,&mutex);
    }
    printf("Erzeuger: Schreiben beginnt\n");
    sleep(1);
    puffer[schreib_index] = wert;
    wert += 10;
    incr_index(&schreib_index);
    print_puffer();
    printf("Erzeuger: Schreiben endet\n");
    pthread_cond_signal(&cond_leer);
    pthread_mutex_unlock(&mutex);
   }
}

void *verbraucher_loop(void *p)
{
   while (1) {
    pthread_mutex_lock(&mutex);
    while (puffer_leer()) {
     printf("Puffer leer => Verbraucher blockiert\n");
     pthread_cond_wait(&cond_leer,&mutex);
    }
    printf("Verbraucher: Lesen beginnt\n");
    sleep(1);
    printf(" gelesen: %d\n",puffer[lese_index]);
    incr_index(&lese_index);
    print_puffer();
    printf("Verbraucher: Lesen endet\n");
    pthread_cond_signal(&cond_voll);
    pthread_mutex_unlock(&mutex);
    sleep(VBRZEIT);
   }
}

int main (int argc, char *argv[])
{
   pthread_t erzeuger, verbraucher;
   pthread_mutex_t mutex;
   int rc;

   rc = pthread_mutex_init(&mutex, NULL);
   printf("mutex_init(): return code = %d\n",rc);

   rc = pthread_cond_init(&cond_voll, NULL);
   printf("cond_init(cond_voll): return code = %d\n",rc);

   rc = pthread_cond_init(&cond_leer, NULL);
   printf("cond_init(cond_leer): return code = %d\n",rc);

   lese_index = 0;
   schreib_index = 0;

   pthread_create(&erzeuger, NULL, erzeuger_loop, NULL); 
   pthread_create(&verbraucher, NULL, verbraucher_loop, NULL); 

   sleep(GESAMTLAUFZEIT);

   pthread_cancel(erzeuger);
   pthread_cancel(verbraucher);
   pthread_mutex_destroy(&mutex);
   pthread_cond_destroy(&cond_voll); 
   pthread_cond_destroy(&cond_leer);

}


