#define _MULTI_THREADED
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void checkResults(string, val) {
        if (val) {
             printf("Failed with %d at %s", val, string);
             exit(1);
          }
      }

#define NUMTHREADS 2
pthread_mutex_t dataMutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t dataPresentCondition = PTHREAD_COND_INITIALIZER;
int dataPresent=0;
int sharedData=0;

void *theThread(void *parm)
{
  int rc;
  int retries=2;

  printf("Consumer Thread %.8x : Entered\n", pthread_self());
  rc = pthread_mutex_lock(&dataMutex);
  checkResults("pthread_mutex_lock()\n", rc);

  while (retries--) {
     while (!dataPresent) {
         printf("Consumer Thread %.8x : Warte auf Daten zum bearbeiten\n",pthread_self());
         rc = pthread_cond_wait(&dataPresentCondition, &dataMutex);
         if (rc) {
             printf("Consumer Thread %.8x : condwait failed, rc=%d\n",rc,pthread_self());
             pthread_mutex_unlock(&dataMutex);
             exit(1);
         }
     }
   printf("Consumer Thread %.8x : Daten wurden gemeldet, "
   " - Bearbeite die Daten sollange sie geschützt sind (lock)\n",pthread_self());

   if (sharedData==0) {dataPresent=0;}
  }//Ende while
 printf("Consumer Thread %.8x : Alles erledigt\n",pthread_self());
 rc = pthread_mutex_unlock(&dataMutex);
 checkResults("pthread_mutex_unlock()\n", rc);
 return NULL;
}

int main(int argc, char **argv)
{
  pthread_t thread[NUMTHREADS];
  int rc=0;
  int amountOfData=4;
  int i;

  printf("Enter Testcase - %s\n", argv[0]);

  printf("Create/start threads\n");
  for (i=0; i <NUMTHREADS; ++i) {
      rc = pthread_create(&thread[i], NULL, theThread, NULL);
      checkResults("pthread_create()\n", rc);
   }//Ende for

/* The producer loop */
  while (amountOfData--) {
     printf("Producer: Daten gefunden\n");
     sleep(3);

     rc = pthread_mutex_lock(&dataMutex); /* Protect shared data and flag */
     checkResults("pthread_mutex_lock()\n", rc);
     printf("Producer: Sperre die Daten und gib eine Meldung an Consumer\n");
     ++sharedData; /* Add data */
     dataPresent=1; /* Set boolean predicate */

     rc = pthread_cond_signal(&dataPresentCondition); /* wake up a consumer */
     if (rc) {
         pthread_mutex_unlock(&dataMutex);
         printf("Producer: Failed to wake up consumer, rc=%d\n", rc);
         exit(1);
      }

     printf("Producer: Gib die gesperrten Daten und das Flag wieder frei\n");
     rc = pthread_mutex_unlock(&dataMutex);
     checkResults("pthread_mutex_lock()\n",rc);
    } //Ende while

  printf("Wartet bis die Threads beendet sind und Ihre Resourcen freigibt\n");
  for (i=0; i <NUMTHREADS; ++i) {
      rc = pthread_join(thread[i], NULL);
      checkResults("pthread_join()\n", rc);
   }

  printf("Clean up\n");
  rc = pthread_mutex_destroy(&dataMutex);
  rc = pthread_cond_destroy(&dataPresentCondition);
  printf("Main completed\n");
  return 0;
}
