/* Solution with coarse-grained locks. */

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

#define SLEEPTIME1 1000000
#define SLEEPTIME2 4000000
#define SLEEPTIME3 1

void producer();
void consumer(int);
void walk_thru();

struct listelt {
  int oldval;
  int newval;
  struct listelt *next;
  struct listelt *prev;
};

/* There are four task queues (one for each consumer) and a head and
   tail pointer for each of these queues. These are global variables
   visible to each thread. */
struct listelt *head[4];
struct listelt *tail[4];

/* A single lock variable is associated with the entire task queue
   data structure. You could get higher parallelism and performance
   by having a separate lock for each task queue, but this will
   suffice for now. */
pthread_mutex_t c_lock = PTHREAD_MUTEX_INITIALIZER;

void list_init()
/* Function to initialize the task queues. */
{
  int i;
  for (i=0;i<4;i++) {
    head[i] = NULL;
    tail[i] = NULL;
  }
}

main()
{
	int i;
        pthread_t threadp, threadc[4];

	list_init();

	/* Create one producer thread that executes the producer() function
	   and four consumer threads that execute the consumer() function.
	   The consumer threads are provided an argument i that indicates
	   their consumer-id number. */
        pthread_create(&threadp, NULL, (void*) producer, NULL);
	for (i=0;i<4;i++) {
          pthread_create(&threadc[i], NULL, (void*) consumer, (void*)i);
	}

	/* While the producers and consumers are working away, the master
	   thread walks through all the elements in the queues and keeps
	   incrementing the stored values. This is similar to some
	   banking application: while transactions are being handled, the bank
	   balances are being updated with the recently accrued interest. */
	walk_thru();
        
	/* Make sure that all the threads are done and finish. */
        pthread_join(threadp, NULL);
	for (i=0;i<4;i++) {
          pthread_join(threadc[i], NULL);
	}
}


void producer()
{

   /* Function to read the input file and insert new tasks into the queue. */
}


void consumer(int id)
{

   /* Function to take tasks out of the queue. */
}

void walk_thru()
{
  int i, j;
  struct listelt *p;
  struct timespec sleeptime;

  sleeptime.tv_sec=0; /* sleeptime is used to designate how long the
                         function pauses between successive updates. */
  srand48(300);       /* Initialize the random number generator. */

  for (j=0;j<1000;j++) {  /* For now, just walk-thru 1000 times. */
    for (i=0;i<4;i++) {

 pthread_mutex_lock(&c_lock);   /* Acquire the lock before updates. */
      p = head[i];
      while (p) {               /* Walk the queue and increment each newval. */
        p->newval++;
        p=p->next;
      }
 pthread_mutex_unlock(&c_lock); /* Release the lock after the update. */

      /* Sleep for a random number of nanoseconds and walk thru again. */
      sleeptime.tv_nsec = (long)((double)SLEEPTIME3 * (double)drand48());
      nanosleep(&sleeptime,NULL);
    }
  }
}

