// -*- Mode: C++; -*-
//                              File      : RDIOplocksMacros.h
//                              Package   : omniNotify-Library
//                              Created on: 6-Nov-2000
//                              Authors   : gruber
//
//    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: RDIOplocksMacros.h,v $
Revision 1.4  2001/06/26 20:01:09  alcfp
updated copyright notices, added support for omniORB4, switched default to POA

Revision 1.3  2001/06/22 07:00:28  alcfp
moved to new logging scheme

Revision 1.2  2000/11/16 05:32:12  alcfp
fixes for aCC

Revision 1.1  2000/11/15 21:18:17  alcfp
large number of changes to switch to use of RDIOplocks for safe object disposal support.  also reduced code duplication a little, and tried hard to make all the proxy code consistent

*/
 
#ifndef _RDI_OPLOCKS_MACROS_H_
#define _RDI_OPLCOKS_MACROS_H_

// --------------------------------------------------------------------------------------
//     HELPER MACROS
// --------------------------------------------------------------------------------------

// These macros can be used in a method implementation of any class
// with the following member:
//
//     RDIOplockEntry*   _oplockptr;
//
// This member should be initialized by the constructor using:
//
// RDI_OPLOCK_INIT;
//
// And it should be freed as follows (e.g., by a destroy method):
//
// RDI_OPLOCK_DISPOSE_IMPL_AND_RELEASE;
//
// Where the lock _oplockptr->oplock should be held before calling free_entry.
//
// Examples of using return statement:
//  . If the result type is void, use RDI_OPLOCK_ACQUIRE(return).
//  . If the result type is CORBA::Boolean and a true result
//       means error, use RDI_OPLOCK_ACQUIRE(return 1)
//
// At the start of a method that implements a CORBA interface call,
//     RDI_OPLOCK_ACQUIRE(RDI_THROW_INV_OBJREF)
// can be used to throw a CORBA::INV_OBJREF exception if the oplock
// cannot be acquired because it no longer belongs to the invoked object.
//
// In addition to return or throw code, one can use something like this:
//     CORBA::Boolean lockfailed = 0;
//     RDI_OPLOCK_ACQUIRE(lockfailed = 1)
//     if (lockfailed) {
//         // lock is not held ... 
//     } else {
//         // lock is held...
//     }

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

// We use the term BUMP to mean incrementing _oplockptr->inuse, and
// DEBUMP to mean decrementing _oplockptr->inuse.

// All acquire macros assume that if the entry referred to be _oplockptr
// is no longer 'owned' by 'this' then they should not acquire the lock
// and should execute some exception_code

// The ACQUIRE macros also assume that if _oplockptr->disposed is true then
// the lock should not be acquired and the exception_code should be executed.

// The REACQUIRE macros obtain the lock as long as 'this' owns the entry,
// even if disposed is true.  Code that uses these macros should check
// the disposed flag : once disposed has been set to true, the inuse
// counter can be decremented, but should not be incremented.  The idea
// behind the REACQUIRE name is that only code that is doing a DEBUMP
// after previously doing a BUMP should use REACQUIRE and it should
// check the disposed flag prior to doing any further BUMP.

// --------------------------------------------------------------------------------------
// ACQUIRE macros

#define RDI_OPLOCK_ACQUIRE(exception_code) \
  do {if ( !_oplockptr || !_oplockptr->acquire(&_oplockptr) ) {\
    exception_code; \
  } } while (0)

// like RDI_OPLOCK_ACQUIRE except executes normal_code if lock is acquired
#define RDI_OPLOCK_ACQUIRE_USE(normal_code, exception_code) \
  do { if ( !_oplockptr || !_oplockptr->acquire(&_oplockptr) ) {\
    exception_code; \
  } else { \
    normal_code; \
  } } while (0)

// Sometimes you only need a lock to do a small amount of work
#define RDI_OPLOCK_ACQUIRE_USE_RELEASE(normal_code, exception_code) \
  do { if ( !_oplockptr || !_oplockptr->acquire(&_oplockptr) ) {\
    exception_code; \
  } else { \
    normal_code; \
    _oplockptr->release(); \
  } } while (0)

// --------------------------------------------------------------------------------------
// REACQUIRE macros

#define RDI_OPLOCK_REACQUIRE(exception_code) \
  do {if ( !_oplockptr || !_oplockptr->reacquire(&_oplockptr) ) {\
    exception_code; \
  } } while (0)

// like RDI_OPLOCK_REACQUIRE except executes normal_code if lock is acquired
#define RDI_OPLOCK_REACQUIRE_USE(normal_code, exception_code) \
  do { if ( !_oplockptr || !_oplockptr->reacquire(&_oplockptr) ) {\
    exception_code; \
  } else { \
    normal_code; \
  } } while(0)

// Sometimes you only need a lock to do a small amount of work
#define RDI_OPLOCK_REACQUIRE_USE_RELEASE(normal_code, exception_code) \
  do { if ( !_oplockptr || !_oplockptr->reacquire(&_oplockptr) ) {\
    exception_code; \
  } else { \
    normal_code; \
    _oplockptr->release(); \
  } } while (0)

// --------------------------------------------------------------------------------------
// Useful variants of the ACQUIRE macros

#define RDI_OPLOCK_ACQUIRE_BUMP(exception_code) \
  RDI_OPLOCK_ACQUIRE_USE(_oplockptr->bump(), exception_code)

#define RDI_OPLOCK_ACQUIRE_DEBUMP(exception_code) \
  RDI_OPLOCK_ACQUIRE_USE(_oplockptr->debump(), exception_code)

#define RDI_OPLOCK_ACQUIRE_BUMP_RELEASE(exception_code) \
  RDI_OPLOCK_ACQUIRE_USE_RELEASE(_oplockptr->bump(), exception_code)

#define RDI_OPLOCK_ACQUIRE_DEBUMP_RELEASE(exception_code) \
  RDI_OPLOCK_ACQUIRE_USE_RELEASE(_oplockptr->debump(), exception_code)

// --------------------------------------------------------------------------------------
// Useful variants of the REACQUIRE macros

#define RDI_OPLOCK_REACQUIRE_BUMP(exception_code) \
  RDI_OPLOCK_ACQUIRE_USE(_oplockptr->bump(), exception_code)

#define RDI_OPLOCK_REACQUIRE_DEBUMP(exception_code) \
  RDI_OPLOCK_ACQUIRE_USE(_oplockptr->debump(), exception_code)

#define RDI_OPLOCK_REACQUIRE_BUMP_RELEASE(exception_code) \
  RDI_OPLOCK_ACQUIRE_USE_RELEASE(_oplockptr->bump(), exception_code)

#define RDI_OPLOCK_REACQUIRE_DEBUMP_RELEASE(exception_code) \
  RDI_OPLOCK_ACQUIRE_USE_RELEASE(_oplockptr->debump(), exception_code)

// --------------------------------------------------------------------------------------
// Lock RELEASE macros
// ** For each of these calls,
//       _oplockptr->oplock MUST be held

// Effect: releases oplock
#define RDI_OPLOCK_RELEASE \
  _oplockptr->release()

// Effect: releases oplock after bump
#define RDI_OPLOCK_BUMP_RELEASE \
  do {_oplockptr->bump(); _oplockptr->release();} while (0)

// Effect: releases oplock after debump
#define RDI_OPLOCK_DEBUMP_RELEASE \
  do {_oplockptr->debump(); _oplockptr->release();} while (0)


// --------------------------------------------------------------------------------------
// Initialization / Disposal Macros
//    ** For dispose macros, _oplockptr->oplock MUST be held

#define RDI_OPLOCK_INIT \
  do {_oplockptr = RDIOplocks::alloc_entry(&_oplockptr); \
           RDI_AssertAllocThrowNo(_oplockptr, "Failed to allocate RDIOplockEntry"); } while (0)

// Effect: triggers immediate or deferred disposal (depending on inuse)
//         and releases oplock; also sets _oplockptr to NULL which
//         speeds up (rejection of) future ACQUIRE/REACQUIRE calls
#define RDI_OPLOCK_DISPOSE_IMPL_AND_RELEASE \
  do { RDIOplockEntry* tmp_entry = _oplockptr; \
           _oplockptr = 0; \
           RDIOplocks::free_entry(tmp_entry, (&_oplockptr), WRAPPED_IMPL2DISPOSEINFO(this)); \
         } while (0)


// --------------------------------------------------------------------------------------
// CONDITION VARIABLE Macros
//
// The entry has a condition variable associated with oplock, and
// the following macros do the obvious calls on this variable.
// ** For SIGNAL and BROADCAST,
//      _oplockptr->oplock *should* be held to ensure entry is owned by 'this'
// ** For WAIT and TIMEDWAIT,
//      _oplockptr->oplock MUST be held
// Note use of bump/debump around calls to wait and timedwait.
// (You should not use these calls after disposed is set.)

#define RDI_OPLOCK_SIGNAL \
  _oplockptr->signal()

#define RDI_OPLOCK_BROADCAST \
  _oplockptr->broadcast()

#define RDI_OPLOCK_WAIT \
  _oplockptr->wait()

#define RDI_OPLOCK_TIMEDWAIT(s,n) \
  _oplockptr->timedwait(s,n)

#define RDI_OPLOCK_INUSEZERO_WAIT \
  _oplockptr->inusezero_wait()

#define RDI_OPLOCK_INUSEZERO_TIMEDWAIT(s,n) \
  _oplockptr->inusezero_timedwait(s,n)

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

// Macros that can be used as the exception_code argument

#define RDI_THROW_INV_OBJREF \
  throw CORBA::INV_OBJREF(0, CORBA::COMPLETED_NO)

#define RDI_THREAD_EXIT(flags, flags_nm, desc) \
  do { RDIDbgLog(flags, flags_nm, desc << " in obj " << (void*)this << " : thread exits\n"); omni_thread::exit(); } while (0)

#define RDI_METHOD_RETURN(flags, flags_nm, desc, val) \
  do { RDIDbgLog(flags, flags_nm, desc << " in obj " << (void*)this << " : method return\n"); return val; } while (0)

#define RDI_METHOD_RETURN_NULL(flags, flags_nm, desc) \
  do { RDIDbgLog(flags, flags_nm, desc << " in obj " << (void*)this << " : method return\n"); return; } while (0)

// --------------------------------------------------------------------------------------
// DESTROY_CHECK verifies that one of these conditions holds
//    1. _oplockptr is zero (no longer associated with an entry)
//    2. _oplockptr->ptr() is zero (the entry has been freed)
//    3. _oplockptr->ptr() is not equal to this (the entry freed + reassigned)

#define RDI_OPLOCKS_DESTROY_CHECK(desc) \
  do { if ( _oplockptr && _oplockptr->ptr() && (_oplockptr->ptr() == (&_oplockptr) ) ) { \
    RDIDbgForceLog("** Internal error: RDI_OPLOCK_DESTROY_CHECK : " << desc << " " << (void*)this << \
		   " allocated OplockEntry has not been freed properly\n"); \
  } } while (0)


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

#endif
