/*
 *  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
 */

#ifndef __ZTWIN32MONITOR_H__
#define __ZTWIN32MONITOR_H__

#include "zthread/FastMutex.h"
#include "StdAfx.h"
#include <assert.h>

#ifndef NDEBUG
#include <algorithm>
#include <vector>
#endif

namespace ZThread { 

/**
 * @class Monitor
 * @author Eric Crahen <crahen@cse.buffalo.edu>
 * @date <2002-03-06T19:50:51-0500>
 *
 * The Monitor template can be used to wrap an object with a 
 * simple non-recursive, mesa-style monitor.
 *
 * Monitors can be acquire()d and release()d by no more than
 * one thread at a given time.
 *
 * A thread that has acquire()d the lock can wait() on the Monitor.
 * The lock is released and the thread wait()ing will be blocked 
 * until notify() is invoked by some other thread, 'setting' the monitor.
 *
 * Being 'set' is a sticky property, (making this object behave somewhat 
 * like a Win32 auto-reset event object) meaning a notify() is garunteed
 * to unblock a thread. If there is no thread waiting on this monitor, 
 * the next wait() will not block.
 */
template <class T>
class Monitor {

  //! Event used to block thread
  HANDLE _handle;

  //! Underlying lock to serialze access to this monitor.
  FastMutex _lock;

  //! Waiting flag, to avoid uneccessary signals
  volatile bool _waiting;

  //! Object guarded by this Monitor 
  T _instance;

#ifndef NDEBUG
  typedef std::vector<DWORD> WaiterList;
  WaiterList _waiters;

  FastMutex _waiterLock;
#endif

public:

  Monitor();
  ~Monitor();

  /**
   * Acquire the lock for this monitor. This will block the caller
   * until the lock can be acquired.
   */
  void acquire();

  /**
   * Try to acquire the lock for this monitor. This will not block the caller,
   * but rather acquire the lock or not and return an indication of weather or
   * not that operator succeeded.
   *
   * @bool
   */
  bool tryAcquire();

  /**
   * Release the lock for this monitor. This will fail if
   * the lock is released without first having been acquire()d.
   */
  void release();

  /**
   * When the lock is has been acquire()d, a thread can wait on
   * this monitor object for an indefinent amount of time. This will block
   * the calling thread until this monitor is 'set', releasing the lock
   * while it waits. Once set, the caller is unblocked and the lock 
   * will be re-acquire()d.
   */
  inline void wait() {
    wait(0);
  }

  /**
   * When the lock is has been acquire()d, a thread can wait on
   * this monitor object for a given amount of time. This will block
   * the calling thread until this monitor is 'set', releasing the lock
   * while it waits. Once set, the caller is unblocked and the lock 
   * will be re-acquire()d.
   *
   * @param ms Maximum milliseconds to wait for a signal. '0' will
   * wait forever.
   * @return true unless the maximum wait time expired.
   */
  bool wait(unsigned long);

  /**
   * Set this monitor. If there is a thread blocked on this monitor object
   * it will be signaled and released. If there is no waiter, the monitors
   * status is 'set' and the next attempt to wait() will return w/o blocking.
   */
  void notify();

  /**
   * Test the Monitor to check if the monitor is blocking a thread.
   *
   * @return bool
   */
  inline bool hasWaiter() const {
    return _waiting;
  }

  /**
   * Overload the casting operator on this Monitor object to expose the 
   * object that it is guarding.
   *
   * @return T
   */
  inline operator T() {
    return _instance;
  }

  /**
   * Provide access to the object guarded by this Monitor
   *
   * @return T*
   */
  inline T* operator->() {
    return &_instance;
  }

};

template <class T>
Monitor<T>::Monitor() : _waiting(false) {
  
  // Create a manual reset monitor
  if((_handle = ::CreateEvent(0, TRUE, FALSE, 0)) == NULL)
    throw Initialization_Exception();

}

template <class T>
Monitor<T>::~Monitor() {
  
  ZTCHECK(::CloseHandle(_handle));
  

}

template <class T>
  inline void Monitor<T>::acquire() {

#ifndef NDEBUG
  _waiterLock.acquire();
  
  DWORD self = ::GetCurrentThreadId();
  assert(std::find(_waiters.begin(), _waiters.end(), self) == _waiters.end());
  _waiters.push_back(self);

  _waiterLock.release();
#endif
  
  _lock.acquire();

}

template <class T>
  inline bool Monitor<T>::tryAcquire() {
  
  return _lock.tryAcquire();

}

template <class T>
  inline void Monitor<T>::release() {

#ifndef NDEBUG
  _waiterLock.acquire();
  
  DWORD self = ::GetCurrentThreadId();
  WaiterList::iterator i = std::find(_waiters.begin(), _waiters.end(), self);
  assert(i != _waiters.end()); 
  _waiters.erase(i);

  _waiterLock.release();
#endif

  _lock.release();

#ifdef USE_STRESS
  // Simulate some stress with an uninterruptable wait
  ::Sleep(100*rand()%10);
#endif

}

template <class T>
  inline bool Monitor<T>::wait(unsigned long timeout) {
  
#ifndef NDEBUG
  _waiterLock.acquire();
  
  DWORD self = ::GetCurrentThreadId();
  WaiterList::iterator i = std::find(_waiters.begin(), _waiters.end(), self);
  assert(i != _waiters.end()); 

  _waiterLock.release();

  assert(this == &ThreadImpl::current()->getMonitor());
  assert(!_waiting);

#endif

  _waiting = true;
  
  _lock.release();

  // Here there is a lack of atomicity on this object. Using a
  // manual reset monitor, makes any SetMonitor() that happenes between
  // here and the wait 'sticky'.

  // Block until the monitor is set.  
  DWORD dwResult = ::WaitForSingleObject(_handle, (timeout == 0) ? INFINITE : (DWORD)timeout);
  assert(dwResult == WAIT_OBJECT_0 || dwResult == WAIT_TIMEOUT);

  // Here there is a lack of atomicity on this object. Because this Monitor
  // object is only used to block one thread, this is really not a problem.

  _lock.acquire();

  // Reset the status
  _waiting = false;
  ZTCHECK(::ResetEvent(_handle));
  
  return (WAIT_OBJECT_0 == dwResult);

}

template <class T>
  void Monitor<T>::notify() {

#ifndef NDEBUG
  _waiterLock.acquire();
  
  DWORD self = ::GetCurrentThreadId();
  WaiterList::iterator i = std::find(_waiters.begin(), _waiters.end(), self);
  assert(i != _waiters.end()); 

  _waiterLock.release();
#endif

  bool waiting = false;

  // Determine if a SetEvent() is needed, but more importantly, 
  // disallow 'setting' of the monitor until the lock can be acquired.
  waiting = _waiting;

  // Set the monitor if there is waiter
  if(waiting)
    ZTCHECK(::SetEvent(_handle));
    
}

};

#endif
