// -*- Mode: C++; -*-
//                              File      : RDIMutex.h
//                              Package   : omniNotify-Library
//                              Created on: 1-Jan-1998
//                              Authors   : gruber&panagos
//
//    Copyright (C) 1998-2001 AT&T Laboratories -- Research
//
//    This file is part of the omniNotify library
//    and is distributed with the omniNotify release.
//
//    The omniNotify library is free software; you can redistribute it and/or
//    modify it under the terms of the GNU Library General Public
//    License as published by the Free Software Foundation; either
//    version 2 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
//    Library General Public License for more details.
//
//    You should have received a copy of the GNU Library 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
//
//
// Description:
//    proprietary interface
//
 
/*
$Log: RDIMutex.h,v $
Revision 1.9  2001/08/03 17:54:15  alcfp
added support for AttNotification

Revision 1.8  2001/06/26 20:01:08  alcfp
updated copyright notices, added support for omniORB4, switched default to POA

Revision 1.7  2001/06/22 07:00:27  alcfp
moved to new logging scheme

Revision 1.6  2000/08/22 18:23:51  alcfp
added description to each file

Revision 1.5  2000/08/16 20:19:18  alcfp
Added licensing notice to each .h and .cc file where library files get GLPL notice and daemon file gets GPL notice -- examples do not claim any license but point out that the library and daemon code does have a license notice

*/
 
#ifndef _RDI_MUTEX_H_
#define _RDI_MUTEX_H_

#include <omnithread.h>
#include "RDIstrstream.h"

/** RDI_RWMutex
  *
  * In many cases, we want to support concurrent read accesses and
  * exclusive write access to a given object.  The following class
  * provides such an interface  using the OMNI mutex and condition
  * variables.
  */

class RDI_RWMutex {
public:
  RDI_RWMutex() : _lock(), _gate(&_lock), _wait(0), _read(0) {;}
  ~RDI_RWMutex()  {;}

  inline void lock(int toread=1);
  inline void unlock();
  inline void weaken();
         void acquire(int toread=1)	{ lock(toread); }
         void release(void)		{ unlock(); }

  RDIstrstream& log_output(RDIstrstream& str) const {
    return str << "wait " << (_wait ? "T " : "F ") << "read " << _read; 
  }

private:
  omni_mutex     _lock;
  omni_condition _gate;	
  int            _wait;		// TRUE when writer active
  unsigned int   _read;		// # of concurrent readers

  RDI_RWMutex(const RDI_RWMutex&);
  RDI_RWMutex& operator=(const RDI_RWMutex&);
};

// ------------------------------------------------------------- //

inline void RDI_RWMutex::lock(int toread)
{
  _lock.lock();
  // If a writer is already present, we have to wait.  The same is
  // true when we are the only writer and there are active readers
  while ( _wait || ((toread == 0) && (_read != 0)) )
	_gate.wait();
  if ( toread )
	_read += 1;
  else 
	_wait  = 1;
  _lock.unlock();
}

inline void RDI_RWMutex::unlock()
{
  _lock.lock();
  if ( _wait )
	_wait = 0;
  else  
	_read -= 1;
  if ( _read == 0 ) 
	_gate.signal();
  _lock.unlock();
}

inline void RDI_RWMutex::weaken()
{
  _lock.lock();
  // We should be a writer wanting to replace our exclusive lock
  // with a shared lock
  if ( _wait ) {
	_wait  = 0;
	_read += 1;
	_gate.signal();		// Wake up any blocked threads
  }
  _lock.unlock();
}

/** READ-WRITE MUTEX HELPER CLASS
  * 
  * This class provides a convenient and safe way to deal with the
  * RDI_RWMutex lock() and unlock() operations.
  */

class RDI_RWMutexLock {
public:
  RDI_RWMutexLock(RDI_RWMutex& rwmutex, int toread=1) :
			_rwmutex(rwmutex) { _rwmutex.lock(toread); }
  ~RDI_RWMutexLock()    { _rwmutex.unlock(); }
private:
  RDI_RWMutex& _rwmutex;
  RDI_RWMutexLock(const RDI_RWMutexLock&);
  RDI_RWMutexLock& operator=(const RDI_RWMutexLock&);
};

////////////////////////////////////////
// Logging

inline RDIstrstream& operator<< (RDIstrstream& str, const RDI_RWMutex& m) { return m.log_output(str); }

#endif
