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

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

This example is to show the basic functioning of barrier, which is
used to block processes until all have reached a certain point in 
the execution.

If a read is attempted before it is written to, the reader blocks.

Note: Every thread must get its own separate key to the barrier.

Note: mutex used to protect cout sections

***********************************************************************/
HPCxx_Mutex outLock;  // 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_Barrier *barrier_boy;
  int bar_key;

public:

  MyThread(HPCxx_Barrier *_barrier, int _myID): 
    barrier_boy(_barrier), myID(_myID),HPCxx_Thread(){}

  // Output, sleep, barrier
  void run() {

    bar_key = barrier_boy->getKey();
    outLock.lock();
    cout << "thread " << myID << " sleeping " << 10-myID
    	 << " bar_key: " << bar_key
	 << " then calling barrier" << endl << flush;
    outLock.unlock();

    sleep(100*(10-myID),0);
    barrier_boy->barrier(bar_key);

    outLock.lock();
    cout << "thread " << myID << " past barrier" << endl << flush;
    outLock.unlock();
    barrier_boy->barrier(bar_key);
  }
};

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

  HPCxx_Barrier *barrier_boy;

  int i,n;

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

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

  // create barrier with a height equal to number of threads
  barrier_boy = new HPCxx_Barrier(n);

  cout << "from main(): barrier size is  " << barrier_boy->size
       << endl << flush;
  cout << "from main(): spawning " << n << " threads" << endl << flush;

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

  
  // join threads into single thread of execution
  for(i=0;i<n;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);
}
