// -*- Mode: C++; -*-
//                            Package   : omniORBpy
// pyCallDescriptor.cc        Created on: 2000/02/02
//                            Author    : Duncan Grisby (dpg1)
//
//    Copyright (C) 2000 AT&T Laboratories Cambridge
//
//    This file is part of the omniORBpy library
//
//    The omniORBpy 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
//
//
// Description:
//    Implementation of Python call descriptor object

// $Id: pyCallDescriptor.cc,v 1.1.4.3 2005/01/07 00:22:32 dgrisby Exp $
// $Log: pyCallDescriptor.cc,v $
// Revision 1.1.4.3  2005/01/07 00:22:32  dgrisby
// Big merge from omnipy2_develop.
//
// Revision 1.1.4.2  2003/07/10 22:17:02  dgrisby
// Track orb core changes, fix bugs.
//
// Revision 1.1.4.1  2003/03/23 21:51:57  dgrisby
// New omnipy3_develop branch.
//
// Revision 1.1.2.11  2003/01/27 11:56:57  dgrisby
// Correctly handle invalid returns from application code.
//
// Revision 1.1.2.10  2002/01/18 15:49:44  dpg1
// Context support. New system exception construction. Fix None call problem.
//
// Revision 1.1.2.9  2001/09/24 10:48:25  dpg1
// Meaningful minor codes.
//
// Revision 1.1.2.8  2001/09/20 14:51:24  dpg1
// Allow ORB reinitialisation after destroy(). Clean up use of omni namespace.
//
// Revision 1.1.2.7  2001/08/15 10:37:14  dpg1
// Track ORB core object table changes.
//
// Revision 1.1.2.6  2001/06/29 15:11:12  dpg1
// Fix for clients using GIOP 1.0.
//
// Revision 1.1.2.5  2001/06/29 09:53:56  dpg1
// Fix for clients using GIOP 1.0.
//
// Revision 1.1.2.4  2001/06/01 11:09:26  dpg1
// Make use of new omni::ptrStrCmp() and omni::strCmp().
//
// Revision 1.1.2.3  2001/05/10 15:16:02  dpg1
// Big update to support new omniORB 4 internals.
//
// Revision 1.1.2.2  2001/01/10 12:00:07  dpg1
// Release the Python interpreter lock when doing potentially blocking
// stream calls.
//
// Revision 1.1.2.1  2000/10/13 13:55:24  dpg1
// Initial support for omniORB 4.
//

#include <omnipy.h>
#include <pyThreadCache.h>
#include <omniORB4/IOP_C.h>

#ifdef HAS_Cplusplus_Namespace
namespace {
#endif
  class cdLockHolder {
  public:
    inline cdLockHolder(omniPy::Py_omniCallDescriptor* cd) : cd_(cd) {
      cd->reacquireInterpreterLock();
    }
    inline ~cdLockHolder() {
      cd_->releaseInterpreterLock();
    }
  private:
    omniPy::Py_omniCallDescriptor* cd_;
  };
#ifdef HAS_Cplusplus_Namespace
};
#endif


OMNI_USING_NAMESPACE(omni)


omniPy::Py_omniCallDescriptor::~Py_omniCallDescriptor()
{
  OMNIORB_ASSERT(!tstate_);
  Py_XDECREF(args_);
  Py_XDECREF(result_);
}


void
omniPy::Py_omniCallDescriptor::initialiseCall(cdrStream&)
{
  // initialiseCall() is called with the interpreter lock
  // released. Reacquire it so we can touch the descriptor objects
  // safely
  cdLockHolder _l(this);

  for (int i=0; i < in_l_; i++)
    omniPy::validateType(PyTuple_GET_ITEM(in_d_,i),
			 PyTuple_GET_ITEM(args_,i),
			 CORBA::COMPLETED_NO);
}


void
omniPy::Py_omniCallDescriptor::marshalArguments(cdrStream& stream)
{
  int i;
  if (in_marshal_) {
    omniORB::logs(25, "Python marshalArguments re-entered.");

    // marshalArguments can be re-entered when using GIOP 1.0, to
    // calculate the message size if the message is too big for a
    // single buffer. In that case, the interpreter lock has been
    // released by the PyUnlockingCdrStream, meaning the call
    // descriptor does not have the lock details. We have to use the
    // thread cache lock.

    omnipyThreadCache::lock _t;

    for (i=0; i < in_l_; i++)
      omniPy::marshalPyObject(stream,
			      PyTuple_GET_ITEM(in_d_,i),
			      PyTuple_GET_ITEM(args_,i));
    if (ctxt_d_)
      omniPy::marshalContext(stream, ctxt_d_, PyTuple_GET_ITEM(args_, i));
  }
  else {
    cdLockHolder _l(this);

    in_marshal_ = 1;
    PyUnlockingCdrStream pystream(stream);

    try {
      for (i=0; i < in_l_; i++)
	omniPy::marshalPyObject(pystream,
				PyTuple_GET_ITEM(in_d_,i),
				PyTuple_GET_ITEM(args_,i));
      if (ctxt_d_)
	omniPy::marshalContext(pystream, ctxt_d_, PyTuple_GET_ITEM(args_, i));
    }
    catch (...) {
      in_marshal_ = 0;
      throw;
    }
    in_marshal_ = 0;
  }
}


void
omniPy::Py_omniCallDescriptor::unmarshalReturnedValues(cdrStream& stream)
{
  if (out_l_ == -1) return;  // Oneway operation

  cdLockHolder _l(this);

  if (out_l_ == 0) {
    Py_INCREF(Py_None);
    result_ = Py_None;
  }
  else {
    PyUnlockingCdrStream pystream(stream);

    if (out_l_ == 1)
      result_ = omniPy::unmarshalPyObject(pystream,
					  PyTuple_GET_ITEM(out_d_, 0));
    else {
      result_ = PyTuple_New(out_l_);
      if (!result_)
	OMNIORB_THROW(NO_MEMORY, 0,
		      (CORBA::CompletionStatus)stream.completion());

      for (int i=0; i < out_l_; i++) {
	PyTuple_SET_ITEM(result_, i,
			 omniPy::unmarshalPyObject(pystream,
						   PyTuple_GET_ITEM(out_d_,
								    i)));
      }
    }
  }
}


void
omniPy::Py_omniCallDescriptor::userException(cdrStream& stream,
					     IOP_C* iop_client,
					     const char* repoId)
{
  reacquireInterpreterLock();

  PyObject* d_o = PyDict_GetItemString(exc_d_, (char*)repoId);

  if (d_o) { // class, repoId, exc name, name, descriptor, ...

    try {
      PyUserException ex(d_o);
      
      ex <<= stream;
      ex._raise();
    }
    catch (...) {
      releaseInterpreterLock();
      if (iop_client) iop_client->RequestCompleted();
      throw;
    }
    OMNIORB_ASSERT(0); // Never reach here
  }
  else {
    releaseInterpreterLock();
    if (iop_client) iop_client->RequestCompleted(1);
    OMNIORB_THROW(UNKNOWN, UNKNOWN_UserException,
		  (CORBA::CompletionStatus)stream.completion());
  }
}


void
omniPy::Py_localCallBackFunction(omniCallDescriptor* cd, omniServant* svnt)
{
  Py_omniCallDescriptor* pycd = (Py_omniCallDescriptor*)cd;
  Py_omniServant*        pyos =
    (Py_omniServant*)svnt->_ptrToInterface(omniPy::string_Py_omniServant);

  // Unfortunately, we can't use the call descriptor's
  // reacquireInterpreterLock() function, since this call-back may be
  // running in a different thread to the creator of the call
  // descriptor.

  if (cd->is_upcall()) {
    omnipyThreadCache::lock _t;
    pyos->remote_dispatch(pycd);
  }
  else {
    omnipyThreadCache::lock _t;
    pyos->local_dispatch(pycd);
  }
}


void
omniPy::Py_omniCallDescriptor::unmarshalArguments(cdrStream& stream)
{
  OMNIORB_ASSERT(args_ == 0);

  omnipyThreadCache::lock _t;

  if (ctxt_d_)
    args_ = PyTuple_New(in_l_ + 1);
  else
    args_ = PyTuple_New(in_l_);


  PyUnlockingCdrStream pystream(stream);

  int i;
  for (i=0; i < in_l_; i++) {
    PyTuple_SET_ITEM(args_, i,
		     omniPy::unmarshalPyObject(pystream,
					       PyTuple_GET_ITEM(in_d_, i)));
  }
  if (ctxt_d_)
    PyTuple_SET_ITEM(args_, i, omniPy::unmarshalContext(pystream));
}

void
omniPy::Py_omniCallDescriptor::setAndValidateReturnedValues(PyObject* result)
{
  OMNIORB_ASSERT(result_ == 0);
  result_ = result;

  if (out_l_ == -1 || out_l_ == 0) {
    if (result_ != Py_None)
      OMNIORB_THROW(BAD_PARAM,
		    BAD_PARAM_WrongPythonType,
		    CORBA::COMPLETED_MAYBE);
  }
  else if (out_l_ == 1) {
    omniPy::validateType(PyTuple_GET_ITEM(out_d_,0),
			 result,
			 CORBA::COMPLETED_MAYBE);
  }
  else {
    if (!PyTuple_Check(result) || PyTuple_GET_SIZE(result) != out_l_)
      OMNIORB_THROW(BAD_PARAM,
		    BAD_PARAM_WrongPythonType,
		    CORBA::COMPLETED_MAYBE);

    for (int i=0; i < out_l_; i++) {
      omniPy::validateType(PyTuple_GET_ITEM(out_d_,i),
			   PyTuple_GET_ITEM(result,i),
			   CORBA::COMPLETED_MAYBE);
    }
  }
}

void
omniPy::Py_omniCallDescriptor::marshalReturnedValues(cdrStream& stream)
{
  omnipyThreadCache::lock _t;
  PyUnlockingCdrStream pystream(stream);

  if (out_l_ == 1) {
    omniPy::marshalPyObject(pystream,
			    PyTuple_GET_ITEM(out_d_, 0),
			    result_);
  }
  else {
    for (int i=0; i < out_l_; i++) {
      omniPy::marshalPyObject(pystream,
			      PyTuple_GET_ITEM(out_d_,i),
			      PyTuple_GET_ITEM(result_,i));
    }
  }
}


syntax highlighted by Code2HTML, v. 0.9.1