/* 
   This file is part of Practical Distributed Processing
   Copyright (C) 2006-2007 Phillip J. Brooke and Richard F. Paige
*/

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

/* 
   Each worker thread runs through its work loop
   8 times. The busy-waiting/lazy thread waits 
   until the shared variable reaches the value 10,
   at which point it finishes. 
*/

#define RUN_WORK_LOOP 8
#define VALUE_OF_CONDITION 10

/*
   Global variables
*/

int shared_var = 0;
pthread_mutex_t shared_var_mutex;
pthread_cond_t shared_var_cond_var;

void *WorkerThread(void *thread_id) 
{
  int i = 0;
  int j;
  int sum = 0;

  while (i<RUN_WORK_LOOP)
  {
    pthread_mutex_lock(&shared_var_mutex);
    shared_var++;

    if(shared_var == VALUE_OF_CONDITION)
    {
      /* Signal waiting thread that condition is true */

      pthread_cond_signal(&shared_var_cond_var);
      printf("Thread %s has reached condition value.\n", (char *)thread_id);
    }

    pthread_mutex_unlock(&shared_var_mutex);

    for(j=0; j<85; j++)
      sum += j+(int)random();
    i++;
  }

  pthread_exit(NULL);
  return 0; /* Never reach this line. */
}

void *LurkerThread(void *thread_id) 
{
  printf("Thread %s is now waiting.\n", (char *)thread_id);

  pthread_mutex_lock(&shared_var_mutex);

  while(shared_var < VALUE_OF_CONDITION)
  {
    pthread_cond_wait(&shared_var_cond_var, &shared_var_mutex);
    printf("Lurking thread has received condition signal.\n");
  }

  pthread_mutex_unlock(&shared_var_mutex);
  pthread_exit(NULL);
  return 0; /* Never reach this line. */
}

int main (int argc, char *argv[])
{
  pthread_t worker1, worker2, lurker;
  pthread_attr_t patt;

  /* Initialize mutex and condition variables */

  pthread_mutex_init(&shared_var_mutex, NULL);
  pthread_cond_init (&shared_var_cond_var, NULL);

  /* Create worker and waiting threads in a joinable state */

  pthread_attr_init(&patt);
  pthread_attr_setdetachstate(&patt, PTHREAD_CREATE_JOINABLE);
  pthread_create(&worker1, &patt, WorkerThread, (void*)"Worker1");
  pthread_create(&worker2, &patt, WorkerThread, (void*)"Worker2");
  pthread_create(&lurker, &patt, LurkerThread, (void*)"Lurker");

  /* Wait for threads to complete */

  pthread_join(worker1, NULL);
  pthread_join(worker2, NULL);
  pthread_join(lurker, NULL);

  printf ("Done.\n");

  /* Clean up and exit */

  pthread_attr_destroy(&patt);
  pthread_mutex_destroy(&shared_var_mutex);
  pthread_cond_destroy(&shared_var_cond_var);
  pthread_exit(NULL);
  return 0; /* Never reach this line. */
}
