// -*- Mode: C++; -*-
//                            Package   : omniORBpy
// pyThreadCache.h            Created on: 2000/05/26
//                            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:
//    Cached mapping from threads to PyThreadState and
//    threading.Thread objects

// $Id: pyThreadCache.h,v 1.1.4.4 2005/03/02 13:39:16 dgrisby Exp $

// $Log: pyThreadCache.h,v $
// Revision 1.1.4.4  2005/03/02 13:39:16  dgrisby
// Another merge from omnipy2_develop.
//
// Revision 1.1.4.3  2005/01/25 11:45:48  dgrisby
// Merge from omnipy2_develop; set RPM version.
//
// Revision 1.1.4.2  2005/01/07 00:22:33  dgrisby
// Big merge from omnipy2_develop.
//
// Revision 1.1.4.1  2003/03/23 21:51:57  dgrisby
// New omnipy3_develop branch.
//
// Revision 1.1.2.1  2000/10/13 13:55:27  dpg1
// Initial support for omniORB 4.
//

#if defined(__VMS)
#include <pythread.h>
#else
#include PYTHON_THREAD_INC
#endif

// Python 2.3 introduced functions to get and released the Python
// interpreter lock, and create thread state as necessary.
// Unfortunately, they are too inefficient since we would end up
// creating and destroying thread states (and Python threading.Thread
// objects) on every call. Even more unfortunately, we can't ignore
// the new functions and use our own scheme, because there are (debug)
// assertions in Python to check that the thread state is what is
// expected. So, we have to jump through all sorts of hoops to play
// nice, and it's still slower than the equivalent code in Python <=
// 2.2...


class omnipyThreadCache {
public:

  static omni_mutex* guard;

  static void init();
  static void shutdown();

  struct CacheNode {
    long             id;
    PyThreadState*   threadState;
    PyObject*        workerThread;

    CORBA::Boolean   used;
    CORBA::Boolean   can_scavenge;
    CORBA::Boolean   reused_state;
    int              active;

#if PY_VERSION_HEX >= 0x02030000
    PyGILState_STATE gilstate;
#endif

    CacheNode*       next;
    CacheNode**      back;
  };

  // Fixed-size open hash table of cacheNodes
  static const unsigned int tableSize;
  static CacheNode**        table;

  // Time in seconds between runs of the node scavenger
  static unsigned int       scanPeriod;

  // Class lock acquires the Python interpreter lock when it is
  // created, and releases it again when it is deleted.
  class lock {
  public:
    inline lock() {
#if PY_VERSION_HEX >= 0x02030000
      PyThreadState* tstate = PyGILState_GetThisThreadState();
      if (tstate) {
	cacheNode_ = 0;
	PyEval_AcquireLock();
	PyThreadState_Swap(tstate);
      }
      else
#endif
      {
	long id    = PyThread_get_thread_ident();
	cacheNode_ = acquireNode(id);
	PyEval_AcquireLock();
	PyThreadState_Swap(cacheNode_->threadState);
      }
    }

    inline ~lock() {
      PyThreadState_Swap(0);
      PyEval_ReleaseLock();
      if (cacheNode_)
	releaseNode(cacheNode_);
    }
  private:
    CacheNode* cacheNode_;
  };


  static inline CacheNode* acquireNode(long id) {
    unsigned int hash = id % tableSize; 
    CacheNode* cn;
    {
      omni_mutex_lock _l(*guard);
      OMNIORB_ASSERT(table);

      cn = table[hash];
      while (cn && cn->id != id) cn = cn->next;
      if (cn) {
	cn->used = 1;
	cn->active++;
	return cn;
      }
    }
    return addNewNode(id, hash);
  }

  static inline void releaseNode(CacheNode* cn) {
    omni_mutex_lock _l(*guard);
    cn->used = 1;
    cn->active--;
  }

  static CacheNode* addNewNode(long id, unsigned int hash);

  static void threadExit(CacheNode* cn);
};


syntax highlighted by Code2HTML, v. 0.9.1