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

#define TRUE  1
#define FALSE 0

#define ErrAbort(s,m)\
  if (s!=0){\
    fprintf(stderr,"%s: %s\n",strerror(s));\
    exit(1);\
  }

typedef struct {
  int             count ;
  pthread_mutex_t mutex ;
  pthread_cond_t  cond  ;
} PARMS ;

void *Producer(void *p)
{
  PARMS *theParms = (PARMS *)p ;
  int    status                ;
  int    done = FALSE          ;
  
  while (!done)
  {
    status = pthread_mutex_lock(&theParms->mutex);
    ErrAbort(status,"Producer mutex lock");
    if (theParms->count % 2 == 0)
    {
      status = pthread_cond_wait(&theParms->cond, &theParms->mutex);
      ErrAbort(status,"Producer cond wait");
      if (theParms->count %2 == 1)
      {
        theParms->count-- ;
        status = pthread_cond_signal(&theParms->cond);
        ErrAbort(status,"Producer cond signal");
      }
    }
    else // simply decrement
    {
      theParms->count-- ;
      status = pthread_cond_signal(&theParms->cond);
      ErrAbort(status,"Producer cond signal 2");
    }
    fprintf(stdout,"Producer, count = %d\n", theParms->count);
    fflush(stdout);
    if (theParms->count <= 0) done = TRUE ;
    status = pthread_mutex_unlock(&theParms->mutex);
    ErrAbort(status,"Producer mutex unlock");
  }
}

void *Consumer(void *p)
{
  PARMS *theParms = (PARMS *)p ;
  int    status                ;
  int    done = FALSE          ;
  
  while (!done)
  {
    status = pthread_mutex_lock(&theParms->mutex);
    ErrAbort(status,"Consumer mutex lock");
    if (theParms->count <= 0) done = TRUE ;
    if (theParms->count % 2 == 0)
    {
      theParms->count-- ;
      pthread_cond_signal(&theParms->cond);
    }
    else
    {
      status = pthread_cond_wait(&theParms->cond, &theParms->mutex);
      ErrAbort(status,"Consumer cond wait");
      if (theParms->count % 2 == 0 && theParms->count > 0)
      {
        theParms->count-- ;
        status = pthread_cond_signal(&theParms->cond);
        ErrAbort(status,"Consumer cond signal 2");
      } 
    }
    fprintf(stdout,"Consumer, count = %d\n", theParms->count);
    fflush(stdout);
    if (theParms->count <= 0) done = TRUE ;
    status = pthread_mutex_unlock(&theParms->mutex);
    ErrAbort(status,"Consumer mutex unlock");
  }
}

int main()
{
  PARMS shared = {51, PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER};
  
  int status ;
  pthread_t threadP, threadC ;
  
  status = pthread_create(&threadP, NULL, Producer, (void *)&shared);
  ErrAbort(status,"Error creating producer");

  status = pthread_create(&threadC, NULL, Consumer, (void *)&shared);
  ErrAbort(status,"Error creating consumer");
  
  pthread_join(threadP, NULL); printf("main() producer done.\n");
  pthread_join(threadC, NULL); printf("main() consumer done.\n");
}

