/*
 *  ZThreads, a platform-independant, multithreading and 
 *  synchroniation library
 *
 *  Copyright (C) 2001, 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
 */

#include "SemaphoreImpl.h"

#include <algorithm>
#include <assert.h>
#include <errno.h>

using namespace ZThread;

/**
 * Create a new SemaphoreImpl. Initialzes one pthreads mutex for 
 * internal use.
 *
 * @exception Initialization_Exception thrown if resources could not be
 * properly allocated
 */
SemaphoreImpl::SemaphoreImpl(int count, unsigned int maxCount, bool checked) 
  throw(Synchronization_Exception) : _count(count), _maxCount(maxCount), _checked(checked) { }

/**
 * Destroy this SemaphoreImpl and release its resources. 
 */
SemaphoreImpl::~SemaphoreImpl()
  throw() {

#ifndef NDEBUG

  if(_waiters.size() > 0) { 

    ZTDEBUG("** You are destroying a semaphore which is blocking %d threads. **\n", _waiters.size());
    assert(0); // Destroyed semaphore while in use

  }

#endif

}

/**
 * Decrement the count, blocking when that count becomes 0 or less.
 *
 * Deals with lost notifications (notify same thread more than once), phantom 
 * notifications (interrupts before notify) and late state changes (interrutps after notify)
 * 
 * @exception Interrupted_Exception thrown when the callers state is INTERRUPTED
 * @exception Unexpected_Exception thrown when the callers state is KILLED
 * @exception InvalidOp_Exception thrown when the callers state is not RUNNING
 */
void SemaphoreImpl::acquire() 
  throw(Synchronization_Exception) {

  bool isWaiting = false;

  // Get the monitor for the current thread, and acquire() it. 
  // Placing focus of the synchronization on the monitor.
  MONITOR& m = getMonitor();
  m.acquire();

  // Check the state bits, if none are set then attempt to acquire this Semaphore
  unsigned int state = check(m);
        
  if(!state) {

    // Serialize access to the Semaphore, to safely update the state.
    _lock.acquire();

    // Update the count without waiting if possible.
    if(_count > 0 && _waiters.empty())
      _count--;

    // Otherwise, wait() for the lock by placing the waiter in the list
    else {
      
      _waiters.push_back(&m);
      isWaiting = true;
      
    }

    // Release the Semaphore lock
    _lock.release();
    
    // Block the thread when the count <= 0
    if(isWaiting) {     
    
      // Wait for a signal
      m.wait();

      // The SIGNAL flag will be set (and cleared here) if wait() was terminated 
      // by a release(). The state can be checked to avoid searching the list
      // of waiters if it is not neccessary
      state = check(m);
  
      m.release();

      // Here there is a lack of atomicity on the Semaphore and the monitor. 
      // Rely on the sticky nature of the monitor to stay in a consistent state

      // Serialize access to the Semaphore, update the state. The focus is really still
      // on the monitors.
      _lock.acquire();

      // If awoke due to release(), update count.
      if(state & State::SIGNAL) {

        _count--;
        state = 0;

        // The thread should not in the waiter list if it was SIGNALed.
        assert(std::find(_waiters.begin(), _waiters.end(), &m) == _waiters.end());

      } else {

        // Remove self from the waiter list.
        WaiterList::iterator i;
        i = std::find(_waiters.begin(), _waiters.end(), &m);
        assert(i != _waiters.end());
        _waiters.erase(i);

      }

      _lock.release();

    } else 
      m.release(); // Release monitor when no wait is needed    

  } else 
    m.release(); // Release monitor when state does not check out initially


  // Check the state and throw an exception if needed.
  if(state & State::KILLED)
    throw Unexpected_Exception();

  if(state & State::INTERRUPTED)
    throw Interrupted_Exception();

  if(state)
    throw InvalidOp_Exception();


}

/**
 * Decrement the count, blocking when it that count is 0 or less. If the timeout
 * expires before the count is raise above 0, the thread will stop blocking 
 * and return.
 *
 * Deals with lost notifications (notify same thread more than once), phantom 
 * notifications (interrupts before notify) and late state changes (interrupts after notify)
 *
 * @param timeout milliseconds
 * @return bool true if the acquire() suceeded before the timeout.
 * 
 * @exception Interrupted_Exception thrown when the callers state is INTERRUPTED
 * @exception Unexpected_Exception thrown when the callers state is KILLED
 * @exception InvalidOp_Exception thrown when the callers state is not RUNNING
 */
bool SemaphoreImpl::tryAcquire(unsigned long timeout) 
  throw(Synchronization_Exception) {
 
  bool isWaiting = false;
  bool wasOnTime = false;

  // Get the monitor for the current thread, and acquire() it. 
  // Placing focus of the synchronization on the monitor.
  MONITOR& m = getMonitor();
  m.acquire();

  // Check the state bits, if none are set then attempt to acquire this Semaphore
  unsigned int state = check(m);
  if(!state) {

    // Serialize access to the Semaphore, to safely update the state.
    _lock.acquire();
  
    // Update the count without waiting if possible.
    if(_count > 0 && _waiters.empty()) {

      _count--;
      wasOnTime = true;

    }

    // Otherwise, wait() for the lock by placing the waiter in the list
    // if there is a valid timeout
    else if(timeout > 0) {
      
      _waiters.push_back(&m);
      isWaiting = true;
      
    }

    // Release the Semaphore lock
    _lock.release();
    
    // Block the thread when the count <= 0
    if(isWaiting) {
  
      // Wait with a timeout
      wasOnTime = m.wait(timeout);

      // The SIGNAL flag will be set (and cleared here) if wait() was terminated 
      // by a release(). The state can be checked to avoid searching the list
      // of waiters if it is not neccessary
      state = check(m);
  
      m.release();

      // Here there is a lack of atomicity on the Semaphore and the monitor. 
      // Rely on the sticky nature of the monitor to stay in a consistent state

      // Serialize access to the Semaphore, update the state. The focus is really still
      // on the monitors.
      _lock.acquire();

      // Remove self from the waiter list.
      WaiterList::iterator i;
      i = std::find(_waiters.begin(), _waiters.end(), &m);
      assert(i != _waiters.end());
      _waiters.erase(i);

      // If awoke due to release(), update count
      if(state & State::SIGNAL) {

        _count--;
        state = 0;
        wasOnTime = true;

        // The thread should not in the waiter list if it was SIGNALed.
        assert(std::find(_waiters.begin(), _waiters.end(), &m) == _waiters.end());

      } else {

        // Remove self from the waiter list.
        WaiterList::iterator i;
        i = std::find(_waiters.begin(), _waiters.end(), &m);
        if(i != _waiters.end())
          _waiters.erase(i);

        else if(!state) {
          
          // Presumably, this should happen only if the thread timesout during
          // a wait and is signalled before it can remove itself from the waiter
          // list.

          assert(wasOnTime);

          // If the thread was signaled sometime between when it woke up
          // from a timeout & before it was able to attempt to remove itself
          // from the waiter list, then absorb the phantom signal

          state = State::SIGNAL;

        }

      }

      // Update the count if possible
      _lock.release();

    } else 
      m.release(); // Release monitor when no wait is needed    

  } else 
    m.release(); // Release monitor when state does not check out initially

  // Check the state and absorb phantom signal
  if(state & State::SIGNAL) {
    
    m.acquire();   
    
    // Only clear the monitor if the state is SIGNAL'd. If another flag
    // was set, then leave it and just continue.
    state = (check(m) & ~State::SIGNAL);

    // This won't block because we know there is a phantom signal
    if(!state) {
      
      m.wait(); 
      state = 0;

    }

    m.acquire();
        
  }

  // Check the state and throw an exception if needed.
  if(state & State::KILLED)
    throw Unexpected_Exception();

  if(state & State::INTERRUPTED)
    throw Interrupted_Exception();

  if(state)
    throw InvalidOp_Exception();

  return wasOnTime;

}

/**
 * Increment the count and release a waiter if there are any. If the semaphore
 * is checked, then an exception will be raised if the maximum count is about to
 * be exceeded. 
 *
 * @exception InvalidOp_Exception thrown if the maximum count is exceeded while
 * the checked flag is set.
 */
void SemaphoreImpl::release() 
  throw(Synchronization_Exception) {

  bool wasInvalid = false;
  MONITOR* m = 0;

  // Serialize access to the Semaphore
  _lock.acquire();

  // Check the count, and set the flag if this release() attempt
  // would exceed the maximum count.
  if(_checked && _count == _maxCount) 
    wasInvalid = true;

  else { 

    // Increment the count
    _count++;

    // To avoid notifying same monitor more than once (stolen signal)  
    // a signaled monitor is removed from the waiter list.
    
    for(WaiterList::iterator i = _waiters.begin(); i != _waiters.end(); ++i) {
      
      // Find a monitor that is still in the waiter list and has not 
      // been awaken.
      
      m = *i;
      
      // Acquire the monitor, preventing any interference from an 
      // interrupt() that might result in an extra notfy() being sent 
      // to that monitor (phantom signal)
      m->acquire();
      
      // If a monitor is selected, hold its lock and notify() it after
      // releasing the lock on this Semaphore. If successful then mark the 
      // monitor with the SIGNAL flag. 
      if(wake(*m)) {

        _waiters.erase(i);
        break;

      }

      // A thread may have been awakened by an interrupt() or kill() and
      // simply be waiting to acquire the lock for this Semaphore and has not 
      // yet been able to remove itself from the waiter list. In those
      // cases, there will be either an INTERRUPTED or KILLED flag set 
      // that can be check so those threads can be skipped (mis-signaled)
      
      m->release();
      m = 0;
 
    }
    
  }

  _lock.release();

  // notify() the selected monitor
  if(m) {

    m->notify();
    m->release();

  }

  if(wasInvalid)
    throw InvalidOp_Exception();

}

/**
 * Get the count for the Semaphore
 *
 * @return int
 */
int SemaphoreImpl::count() throw() {

  int count;

  // Lock to be sure the value of count isn't half updated
  // by another thread when its read.
  _lock.acquire();
  count = _count;
  _lock.release();

  return count;
}


