/* KInterbasDB Python Package - Header File for Central Concurrency Facilities
 *
 * Version 3.3
 *
 * The following contributors hold Copyright (C) over their respective
 * portions of code (see license.txt for details):
 *
 * [Original Author (maintained through version 2.0-0.3.1):]
 *   1998-2001 [alex]  Alexander Kuznetsov   <alexan@users.sourceforge.net>
 * [Maintainers (after version 2.0-0.3.1):]
 *   2001-2002 [maz]   Marek Isalski         <kinterbasdb@maz.nu>
 *   2002-2007 [dsr]   David Rushby          <woodsplitter@rocketmail.com>
 * [Contributors:]
 *   2001      [eac]   Evgeny A. Cherkashin  <eugeneai@icc.ru>
 *   2001-2002 [janez] Janez Jere            <janez.jere@void.si>
 */

/************** CONCEPTUAL NOTES (for Python client programmers) **************
 * See docs/usage.html#special_issue_concurrency
 */

/************* IMPLEMENTATION NOTES (for KInterbasDB maintainers) *************
 *
 * 1. The GDAL (which is active at Level 1 and inactive at Level 2) and the
 *   GCDL (which is inactive at Level 1 and active at Level 2) are conceptually
 *   separate, but can be implemented as a single lock object due to the fact
 *   that the two conceptual locks are never active at the same time.
 *     To avoid confusion in the bulk of the kinterbasdb code, and to prepare
 *   for the potential introduction of concurrency levels greater than 2, the
 *   macros for manipulating the GDAL and the GCDL are arranged to imply that
 *   the two conceptually separate locks are truly different objects.
 */

#ifndef _KILOCK_H
#define _KILOCK_H

#include "_kinterbasdb.h"

#ifdef ENABLE_CONCURRENCY
  #include "pythread.h"

  extern PyThread_type_lock _global_db_client_lock;
  /* The following aliases are defined due to factors discussed in
   * Implementation Note #1 (see above). */
  #define global_GDAL _global_db_client_lock
  #define global_GCDL _global_db_client_lock

  /*************************** Python GIL ************************/

  /* The next two macros are for manipulating the GIL using an explicit thread
   * state.  For example, a thread that's bootstrapped from C, and only enters
   * the GIL occasionally, might use these. */
  #define ENTER_GIL_USING_THREADSTATE(ts) \
    debug_print3("GIL-> ?ACQUIRE: %ld file %s line %d\n", \
        PyThread_get_thread_ident(), __FILE__, __LINE__ \
      ); \
    PyEval_RestoreThread(ts); \
    debug_print3("GIL-> !!ACQUIRED: %ld file %s line %d\n", \
        PyThread_get_thread_ident(), __FILE__, __LINE__ \
      );

  #define LEAVE_GIL_USING_THREADSTATE(ts) \
    /* Notice that ts is not actually used in the current implementation of \
     * this macro. */ \
    debug_print3("GIL-> ?RELEASE: %ld file %s line %d\n", \
        PyThread_get_thread_ident(), __FILE__, __LINE__ \
      ); \
    PyEval_SaveThread(); \
    debug_print3("GIL-> !RELEASED: %ld file %s line %d\n", \
        PyThread_get_thread_ident(), __FILE__, __LINE__ \
      );


  #define LEAVE_GIL_WITHOUT_AFFECTING_DB \
      debug_print3("GIL-> ?RELEASE: %ld file %s line %d\n", \
          PyThread_get_thread_ident(), __FILE__, __LINE__ \
        ); \
      Py_BEGIN_ALLOW_THREADS \
      debug_print3("GIL-> !RELEASED: %ld file %s line %d\n", \
          PyThread_get_thread_ident(), __FILE__, __LINE__ \
        );

  #define ENTER_GIL_WITHOUT_AFFECTING_DB \
      debug_print3("GIL-> ?ACQUIRE: %ld file %s line %d\n", \
          PyThread_get_thread_ident(), __FILE__, __LINE__ \
        ); \
      Py_END_ALLOW_THREADS \
      debug_print3("GIL-> !!ACQUIRED: %ld file %s line %d\n", \
          PyThread_get_thread_ident(), __FILE__, __LINE__ \
        );

  #define OPEN_LOCAL_GIL_MANIPULATION_INFRASTRUCTURE \
    { PyThreadState *_save = NULL;

  #define CLOSE_LOCAL_GIL_MANIPULATION_INFRASTRUCTURE }

  #define LEAVE_GIL_WITHOUT_AFFECTING_DB_AND_WITHOUT_STARTING_CODE_BLOCK \
    debug_print3("GIL-> ?RELEASE: %ld file %s line %d\n", \
        PyThread_get_thread_ident(), __FILE__, __LINE__ \
      ); \
    Py_UNBLOCK_THREADS \
    debug_print3("GIL-> !!RELEASED: %ld file %s line %d\n", \
        PyThread_get_thread_ident(), __FILE__, __LINE__ \
      );

  #define ENTER_GIL_WITHOUT_AFFECTING_DB_AND_WITHOUT_ENDING_CODE_BLOCK \
    debug_print3("GIL-> ?ACQUIRE: %ld file %s line %d\n", \
        PyThread_get_thread_ident(), __FILE__, __LINE__ \
      ); \
    Py_BLOCK_THREADS \
    debug_print3("GIL-> !!ACQUIRED: %ld file %s line %d\n", \
        PyThread_get_thread_ident(), __FILE__, __LINE__ \
      );


  /*************************** GDAL ************************/
  #define ENTER_GDAL \
    { \
      LEAVE_GIL_WITHOUT_AFFECTING_DB \
      ENTER_GDAL_WITHOUT_LEAVING_PYTHON

  #define LEAVE_GDAL \
      LEAVE_GDAL_WITHOUT_ENTERING_PYTHON \
      ENTER_GIL_WITHOUT_AFFECTING_DB \
    }

  #define ENTER_GDAL_WITHOUT_LEAVING_PYTHON \
    if (global_concurrency_level == 1) { \
      debug_print3("GDAL-> ?ACQUIRE: %ld file %s line %d\n", \
          PyThread_get_thread_ident(), __FILE__, __LINE__ \
        ); \
      PyThread_acquire_lock(global_GDAL, WAIT_LOCK); \
      debug_print3("GDAL-> !!ACQUIRED: %ld file %s line %d\n", \
          PyThread_get_thread_ident(), __FILE__, __LINE__ \
        ); \
    }

  #define LEAVE_GDAL_WITHOUT_ENTERING_PYTHON \
    if (global_concurrency_level == 1) { \
      debug_print3("GDAL-> ?RELEASE: %ld file %s line %d\n", \
          PyThread_get_thread_ident(), __FILE__, __LINE__ \
        ); \
      PyThread_release_lock(global_GDAL); \
      debug_print3("GDAL-> !RELEASED: %ld file %s line %d\n", \
          PyThread_get_thread_ident(), __FILE__, __LINE__ \
        ); \
    }

  #define LEAVE_GDAL_WITHOUT_ENDING_CODE_BLOCK \
    LEAVE_GDAL_WITHOUT_ENTERING_PYTHON \
    ENTER_GIL_WITHOUT_AFFECTING_DB_AND_WITHOUT_ENDING_CODE_BLOCK

  /*************************** GCDL ************************/
  #define ENTER_GCDL \
    { \
      LEAVE_GIL_WITHOUT_AFFECTING_DB \
      ENTER_GCDL_WITHOUT_LEAVING_PYTHON

  #define LEAVE_GCDL \
      LEAVE_GCDL_WITHOUT_ENTERING_PYTHON \
      ENTER_GIL_WITHOUT_AFFECTING_DB \
    }

  #ifdef ENABLE_FREE_CONNECTION_AND_DISCONNECTION
    /* Connection and disconnection are not serialized. */
    #define ENTER_GCDL_WITHOUT_LEAVING_PYTHON
    #define LEAVE_GCDL_WITHOUT_ENTERING_PYTHON
  #else
    /* Serialize connection and disconnection.  If we're operating at a
     * concurrency_level < 2, the GDAL already performs this serialization. */
    #define ENTER_GCDL_WITHOUT_LEAVING_PYTHON \
      if (global_concurrency_level > 1) { \
        debug_print3("GCDL-> ?ACQUIRE: %ld file %s line %d\n", \
            PyThread_get_thread_ident(), __FILE__, __LINE__ \
          ); \
        PyThread_acquire_lock(global_GCDL, WAIT_LOCK); \
        debug_print3("GCDL-> !!ACQUIRED: %ld file %s line %d\n", \
            PyThread_get_thread_ident(), __FILE__, __LINE__ \
          ); \
      }

    #define LEAVE_GCDL_WITHOUT_ENTERING_PYTHON \
      if (global_concurrency_level > 1) { \
        debug_print3("GCDL-> ?RELEASE: %ld file %s line %d\n", \
            PyThread_get_thread_ident(), __FILE__, __LINE__ \
          ); \
        PyThread_release_lock(global_GCDL); \
        debug_print3("GCDL-> !RELEASED: %ld file %s line %d\n", \
            PyThread_get_thread_ident(), __FILE__, __LINE__ \
          ); \
      }
  #endif /* ENABLE_FREE_CONNECTION_AND_DISCONNECTION */

#else /* ndef ENABLE_CONCURRENCY */

  #define ENTER_GDAL
  #define LEAVE_GDAL

  #define ENTER_GCDL
  #define LEAVE_GCDL

  #define ENTER_GIL_USING_THREADSTATE(ts)
  #define LEAVE_GIL_USING_THREADSTATE(ts)

  #define LEAVE_GIL_WITHOUT_AFFECTING_DB
  #define ENTER_GIL_WITHOUT_AFFECTING_DB
  #define ENTER_GIL_WITHOUT_AFFECTING_DB_AND_WITHOUT_ENDING_CODE_BLOCK

  #define ENTER_GDAL_WITHOUT_LEAVING_PYTHON
  #define LEAVE_GDAL_WITHOUT_ENTERING_PYTHON
  #define LEAVE_GDAL_WITHOUT_ENDING_CODE_BLOCK

  #define ENTER_GCDL_WITHOUT_LEAVING_PYTHON
  #define LEAVE_GCDL_WITHOUT_ENTERING_PYTHON

#endif /* def ENABLE_CONCURRENCY */

#endif /* not def _KILOCK_H */
