/*
 *  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 __ZTLATCH_H__
#define __ZTLATCH_H__

#include "zthread/ZThread.h"

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


namespace ZThread {
  
/**
 * @class Latch
 *
 * @author Eric Crahen <crahen@cse.buffalo.edu>
 * @date <2002-02-24T09:25:44-0500>
 * @version 1.3.2
 *
 * A Latch object is a non-thread specific one time switch.
 * That is to say that the same thread can release the latch
 * and then acquire it. Also, it is a basically one time switch, 
 * once it has been activated its state will remain the same. 
 *
 * It is created with an initial count {n}. Before a section can
 * be entered that is <Guard>ed by a Latch, that Latch must be released
 * n times.
 *
 * A simple example: This telephone simulation won't start until someone 
 * Ring()s (makes a call) and someone Answer()s
 *
 * @code
 * 
 * Latch enoughCallers(2);
 *
 * void Ring() {
 *   enoughCallers.release();
 * }
 *
 * void Answer() {
 *   enoughCallers.release();
 * }
 *
 * void MainTelephoneSimulationLoop() {
 *
 *   Guard<Latch> g(enoughCallers);
 *
 *   for(;;) {
 *
 *   ... finally enough callers ...
 *
 *   }
 *
 * }
 *
 * @endcode
 */
template <class LOCK=Mutex>
class ZTHREAD_API Latch : public Lockable {
  
  LOCK _lock;
  LOCK _control;
  
  int _initialCount;
  int _count;
  
  // Disable copy constructors
  Latch(const Latch&);
  Latch& operator=(const Latch&);
    
  public:
  
  /**
   * Create a new Latch with the given count
   *
   * @param int - initial count (default 1)
   *
   * @exception Initialization_Exception
   */
  Latch(int initialCount = 1) throw(Synchronization_Exception) 
   : _initialCount(initialCount), _count(initialCount) {

    _control.acquire();
   
  }


  /**
   * Destroy this Latch
   */
  virtual ~Latch() throw() {

    try {
      _control.release();
    } catch(...) {}
    
  }  

  /**
   * Acquire()ing a Latch will block the calling thread until the
   * Latch has been released enough times. The number of times a 
   * Latch must be released is determined during construction
   *
   * @exception Synchronization_Exception
   */
  virtual void acquire() 
    throw(Synchronization_Exception) {


      if(_count >= 0)
        _control.acquire();
      
    }

  /**
   * Functions in a manner similar to the acquire() method, but can
   * timeout and fail after a given amount of time
   *
   * The behavior of this method depends on your choice of the LOCK.
   * Lockable objects, like the FastMutex, do not support the tryAcquire
   * method and will cause this method to block longer than expected
   *
   * @param long - timeout (milliseconds)
   *
   * @exception Synchronization_Exception
   */
  virtual bool tryAcquire(unsigned long timeout) 
    throw(Synchronization_Exception) {

      return _control.tryAcquire(timeout);      
      
    }

  /**
   * Releasing a Latch decrements the internal count and brings 
   * it that much closer to being acquire()able
   *
   * @exception Synchronization_Exception
   */
  virtual void release() 
    throw(Synchronization_Exception) {
      
      Guard<LOCK> guard(_lock);
      _count--;
      
      // Allow access to the protected section when count reaches 0
      if(_count == 0)
      _control.release();      

    }
  
}; /* Latch */
  
};

#endif

