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

#define MAXNITEMS	2000000
#define MAXNTHREADS	50

int nitems;
int buff[MAXNITEMS];
struct {
     pthread_mutex_t mutex;
     int nput;
     int nval;
} prod_shared = {
     PTHREAD_MUTEX_INITIALIZER
};

struct {
     pthread_mutex_t mutex;
     pthread_cond_t cond;
     int nready;
} cons_shared = {
     PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER
};

void *prod(void *), *cons(void *);

int main (int argc, char *argv[])
{
     int i, nthreads, count[MAXNTHREADS], totalsum = 0;
     pthread_t tid_prod[MAXNTHREADS], tid_cons;

     if (argc != 3) {
	  fprintf(stderr, "Usage: mutexes #items #threads\n");
	  exit(1);
     }

     if ((nitems = atoi (argv[1])) > MAXNITEMS)
	  nitems = MAXNITEMS;

     if ((nthreads = atoi (argv[2])) > MAXNTHREADS)
	  nthreads = MAXNTHREADS;

     for (i=0; i<nthreads; i++) {
	  count[i] = 0;
	  pthread_create(&tid_prod[i], NULL, prod, &count[i]);
     }
     pthread_create(&tid_cons, NULL, cons, NULL);

     for (i=0; i<nthreads; i++) {
	  pthread_join(tid_prod[i], NULL);
	  printf("count[%d] = %d\n", i, count[i]);
	  totalsum += count[i];
     }
     printf("TOTAL: %d\n", totalsum);

     pthread_join(tid_cons, NULL);

     exit(0);
}

void *prod(void *arg)
{
     for ( ; ; ) {
       pthread_mutex_lock(&prod_shared.mutex); 
	  if (prod_shared.nput >= nitems) {
	    pthread_mutex_unlock(&prod_shared.mutex); 
	       return (NULL);
	  }

	  buff[prod_shared.nput] = prod_shared.nval;
	  prod_shared.nput++;
	  prod_shared.nval++;
	  pthread_mutex_unlock(&prod_shared.mutex); 

	  pthread_mutex_lock(&cons_shared.mutex);
	  if (cons_shared.nready == 0)
	    pthread_cond_signal(&cons_shared.cond);
	  cons_shared.nready++;
	  pthread_mutex_unlock(&cons_shared.mutex);

	  *((int *) arg) += 1;
     }
}

void *cons(void *arg)
{
     int i;

     for (i=0; i<nitems; i++) {
       pthread_mutex_lock(&cons_shared.mutex);
	  if (cons_shared.nready == 0) 
	    pthread_cond_wait(&cons_shared.cond, &cons_shared.mutex);
	  cons_shared.nready--;
	  pthread_mutex_unlock(&cons_shared.mutex);

	  if (buff[i] != i)
	       printf("buff[%d] = %d\n", i, buff[i]);
     }

     return(NULL);
}
