/*
 *  ZThreads, a platform-independant, multithreading and 
 *  synchroniation library
 *
 *  Copyright (C) 2000-2002, Eric Crahen, See LGPL.TXT for details
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 *
 *  SUNY @ Buffalo, hereby disclaims all copyright interest in the
 *  ZThreads library written by Eric Crahen
 */

#ifndef __ZTBARRIER_H__
#define __ZTBARRIER_H__

#include "zthread/ZThread.h"

#include "zthread/Condition.h"
#include "zthread/Guard.h"
#include "zthread/Lockable.h"
#include "zthread/Mutex.h"

namespace ZThread {

/**
 * @class Barrier
 *
 * @author Eric Crahen <crahen@cse.buffalo.edu>
 * @date <2002-03-05T07:38:35-0500> 
 * @version 1.3.2
 *
 * A barrier keeps a group of threads synchronized with one another in a
 * medium grained control. A Barrier is created with a specific 
 * threshold {n} As threads try to acquire a Barrier they are blocked until
 * the n threads have made the attempt. As the nth thread makes its 
 * attempt the other blocked threads are released.
 *
 * This provides a simple mechanism for making sure all threads working on
 * some data have reached a specific point before the program continues.
 *
 * After enough threads have reached a Barriers threshold, the Barrier will
 * reset and can be used again.
 *
 * An example:
 *
 *   This very brief example attempts to illustrate several threads working
 *  on a shared set of data. These tasks may reach some critical point 
 *  at which they want to be certain that no other threads are still working
 *  on that data.
 *
 * @code
 *
 * Barrier ready(4), restart(4)
 *
 * class RED : public Runnable {
 * public:
 *
 * virtual ~RED() {}
 *
 * virtual void run() {
 *
 *   try {
 *
 *     // Start doing something to some shared data
 *
 *     cout << "RED is waiting..." << endl;
 *     Thread::sleep(2000);
 *
 *     ready.acquire(); // critical point
 *     restart.acquire();
 *
 *  } catch(...) {
 *     cerr << "RED Unexcpected Exception!" << endl;
 *   }
 *
 *  }
 *
 * };
 *
 *
 * // BLUE() & GREEN() have similar definitions
 *
 * int main() {
 *
 *   Thread th[3];
 *
 *   RED r, BLUE b, GREEN g;
 *
 *   th.run(&r);
 *   th.run(&b);
 *   th.run(&g);
 *
 *   ready.acquire(); // After all the workers reach the barrier execution
 *                   // will continue
 *   
 *   // switch the data set or someother operation you need to be 
 *   // sure the working threads will not interrupt
 *   ...
 *   restart.acquire(); // Unblock the threads waiting on this barrier
 *
 * }
 *
 * @endcode
 *
 * NOTE: Barriers are generally more useful in SMP architectures
 */
template <class LOCK=Mutex>
class ZTHREAD_API Barrier : public Lockable {
    
  int _nCount;
  int _nThreshold;
  bool _resets;

  LOCK _lock;
  Condition _cvThreshold;

  // Disabled
  Barrier(const Barrier&);
  Barrier& operator=(const Barrier&);

  public:

  /**
   * Create a new Barrier that waits for n threads. This is the 
   * threshold of the Barrier.
   *
   * @param int - number of threads to wait for (threshold)
   * @param bool - reset flag, when the n'th thread releases all
   * blocked threads, a Barrier with flag set at creation will reset
   * the internal count of the Barrier so it can be reused. A false
   * reset flag causes the Barrier to become a 'dead' object; after 
   * it's first used (After meeting its threashold once and releasing
   * waiters) it can not be reused and it will not block any threads
   *
   * @post Barrier will block until 'n' acquire()s have been 
   * performed. The n'th acquire will release all blocked threads
   */
  Barrier(int thr, bool resets=true) throw(Synchronization_Exception)
   : _nCount(thr), _nThreshold(thr), _resets(resets) {

    if(thr < 0)
      throw Initialization_Exception("Invalid parameters");
      
  }


  /**
   * Destroy the Barrier
   */
  virtual ~Barrier() throw() {
  }
  
  /**
   * A thread attempting to invoke the acquire method of a Barrier will
   * be blocked until enough threads have invoked the method and the 
   * threshold is met.
   *
   * @exception Synchronization_Exception
   */
  virtual void acquire() 
    throw(Synchronization_Exception) {
  
      Guard<LOCK> guard(_lock);

      if(--_nCount == 0) { // Decrement the Blocked count
        ZTDEBUG("WAIT\n");
        _cvThreshold.broadcast(); // Wake all 

        if(_resets)
          _nCount = _nThreshold;    // Reset
        ZTDEBUG("OUT\n"); 
      } else 
        _cvThreshold.wait(_lock); // Block if count >=0 
       
    }


  /**
   * This method can be used to quickly test weather or not the Barrier
   * is at it's threshold. 
   *
   * If you chose a lock, such as a FastMutex, that does not support 
   * the tryAcquire method then this will block until the Barrier has
   * reached its threshold.
   *
   * @param long - time out
   *
   * @return bool - If true, this means that one more thread acquire()ing
   * the Barrier will release all blocked threads. If false, then the 
   * test is indeterminate. 
   * 
   * @exception Synchronization_Exception
   */
  virtual bool tryAcquire(unsigned long timeout) 
    throw(Synchronization_Exception) {
      
      bool result = false;
      
      if(_lock.tryAcquire(timeout)) {
        
        result = (_nCount <= 1);
        _lock.release();
        
      }
      
      return result;      
      
    }


  /**
   * The release method has no effect for a Barrier. A thread is simply
   * blocked by an acquire() attempt or not. 
   * 
   * @exception Synchronization_Exception
   */ 
  virtual void release() 
    throw(Synchronization_Exception) { }

  
}; /* Barrier */ 

  
};

#endif
