#include <hpcxx_rts.h>
#include <iostream.h>
#include <unistd.h>
#include <stdlib.h>

/***********************************************************************
tjs - 98.09.21

This example is to show the basic functioning of csem.  In the example
a set of tasks is run on a set of threads.  When the tasks are all done
everybody syncs using the csem.

Note: mutexes are used to protect critical sections

***********************************************************************/
HPCxx_Mutex outLock;  // note that scope is this file
HPCxx_Mutex taskLock;  // note that scope is this file

// MyThread::run() is the meat of the routine.  This is the code that
// will be run when MyThread::start() -- inherited from HPCxx_Thread
// -- is executed.

class MyThread : public HPCxx_Thread {
  int myID;
  HPCxx_CSem *task_start;
  HPCxx_CSem *task_end;

public:

  MyThread(HPCxx_CSem *_sem1, HPCxx_CSem *_sem2, int _myID):  
    task_start(_sem1), task_end(_sem2), myID(_myID),HPCxx_Thread(){}

  // Check for tasks to be done.  If yes, grab one, increment
  // task_start.  When finished increment task_end.
  // If task_start all done, then wait on task_end.

  void run() {

    int task_num = 1;

    while(task_num >= 0){
      // check for work
      taskLock.lock();
      if ((task_num = task_start->getStatus()+1) <= task_start->getCount()) {

	task_start->incr();

      } else {

	task_num = -1;

      }
      taskLock.unlock();
    
      // do the work or wait to finish
      if(task_num > 0) {

	outLock.lock();
	cout << "thread " << myID << " doing task " << task_num
	     << endl << flush;
	outLock.unlock();

	sleep((int)(5000.*drand48()),0);
	task_end->incr();

      }
    } // end while

    while(task_end->getStatus() < task_end->getCount());

    // wait() doesn't seem to work.  Threads pass through before the
    // csem is incremented to the value of count.
    //	task_end->wait();

    outLock.lock();
    cout << "thread " << myID << " finished"
	 << endl << flush;
    outLock.unlock();
  }
};

int main(int argc, char **argv)
{
  // These two must come first
  HPCxx_Group *g;
  hpcxx_init(argc, argv, g);

  HPCxx_CSem *task_start;
  HPCxx_CSem *task_end;

  int
    i,
    n_threads,
    n_tasks;

  cout << "how many threads do you want? " << flush;
  cin >> n_threads;

  //Array of threads
  MyThread *t[n_threads];

  cout << "how many tasks do you want? " << flush;
  cin >> n_tasks;

  // initialize the csems
  task_start = new HPCxx_CSem(n_tasks);
  task_end = new HPCxx_CSem(n_tasks);


  // spawn the threads
  for(i=0;i<n_threads;i++) {
    t[i] = new MyThread(task_start, task_end, i);
    t[i]->start();
  }

  
  // join threads into single thread of execution
  for(i=0;i<n_threads;i++) {
    t[i]->join();
  }

  // Now back to 1 thread

  outLock.lock();
  cout << "from main(): DONE" << endl << flush;
  outLock.unlock();

  // clean up hpcxx.  This should be the last thing you do.
  return hpcxx_exit(g);
}
