/* KInterbasDB Python Package - Header File for Events Support
 *
 * 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>            */

/* Throughout these declarations, note the use of the 'volatile' modifier to
 * ensure that an altered variable's value is written back to main memory
 * instead of mistakenly written only to one CPU's cache on a
 * multiprocessor. */

#ifndef _KIEVENTS_H
#define _KIEVENTS_H

#include "_kinterbasdb.h"

#ifdef ENABLE_DB_EVENT_SUPPORT

#include "_kinterbasdb_exception_functions_without_python.h"
#include "_kisupport.h"
#include "_kisupport_threadsafe_fifo_queue.h"

#ifdef FIREBIRD_2_0_OR_LATER
  #define EVENT_CALLBACK_FUNCTION ISC_EVENT_CALLBACK
#else
  #define EVENT_CALLBACK_FUNCTION isc_callback
#endif

#define DV_VOID(void_ptr) DEVOLATILE(void *, void_ptr)
#define DV_STR(char_ptr) DEVOLATILE(char *, char_ptr)
#define DV_STR_PTR(char_ptr_ptr) DEVOLATILE(char **, char_ptr_ptr)
#define DV_Q(q) DEVOLATILE(ThreadSafeFIFOQueue *, q)
#define DV_EOTC(eotc) DEVOLATILE(EventOpThreadContext *, eotc)
#define DV_ERB(erb) DEVOLATILE(EventRequestBlock *, erb)
#define DV_CALCTX(callctx) DEVOLATILE(EventCallbackThreadContext *, callctx)
#define DV_ISC_STATUS(ISC_STATUS_) DEVOLATILE(ISC_STATUS, ISC_STATUS_)
#define DV_STATVEC(ISC_STATUS_ptr) DEVOLATILE(ISC_STATUS *, ISC_STATUS_ptr)
#define DV_DB_HANDLE_PTR(isc_db_handle_ptr) DEVOLATILE(isc_db_handle *, isc_db_handle_ptr)
#define DV_LONG_PTR(long_ptr) DEVOLATILE(long *, long_ptr)
#define DV_ISC_LONG_PTR(ISC_LONG_ptr) DEVOLATILE(ISC_LONG *, ISC_LONG_ptr)
#define DV_THREADID_PTR(thread_id_ptr) \
  DEVOLATILE(PlatformThreadIdType *, thread_id_ptr)

/******************** HARD-CODED LIMITS:BEGIN ********************/

/* EVENT_BLOCK_SIZE is a limitation imposed by the DB client library, but
 * kinterbasdb transparently eliminates it from the Python programmer's
 * perspective. */
#define EVENT_BLOCK_SIZE 15

/******************** HARD-CODED LIMITS:END ********************/

/******************** MODULE TYPE DEFINITIONS:BEGIN ********************/

typedef enum {
  ECALL_UNINITIALIZED = 1,
  ECALL_DUMMY         = 2,
  ECALL_NORMAL        = 3,
  ECALL_DEAD          = 4
} EventCallbackThreadState;

typedef struct {
  PlatformMutexType lock;
  volatile EventCallbackThreadState state;

  volatile int block_number;

  volatile PlatformThreadIdType op_thread_id;

  /* op_q is a pointer to the operation request queue in the
   * EventOpThreadContext.  The queue's memory and member cleanup is managed
   * by the EventOpThreadContext, not by EventCallbackThreadContext. */
  volatile ThreadSafeFIFOQueue *op_q;
} EventCallbackThreadContext;


typedef struct {
  #define NULL_EVENT_ID -1
  volatile ISC_LONG event_id;

  volatile char *req_buf;
  volatile short req_buf_len;

  /* The EventOpThread never accesses the members of this structure.  The only
   * threads that do so are the thread that creates/destroys the
   * EventConduit, as well as (obviously) the respective EventCallbackThreads
   * themselves. */
  volatile EventCallbackThreadContext callback_ctx;
} EventRequestBlock;

typedef enum {
  /* Requested by the thread that creates the event conduit: */
  OP_CONNECT,
  OP_REGISTER,
  OP_DIE,

  /* Requested by the event callback (which runs for all except its final
   * iteration in a thread started by the database client library).
   * During this operation, the EventOpThread:
   *   1) Calculates and posts to the EventFiredQueue an EventFiredNode that
   *      contains the counts of the events that the EventOpNode indicates
   *      occurred.  The EventConduit.wait method is where EventFiredNodes are
   *      consumed from the EventFiredQueue.
   *   2) Re-registers the event callback to be called by the database client
   *      library the next time the server detects the occurence of any of the
   *      events that were specified by the client programmer in the
   *      event_names parameter to the EventConduit constructor. */
  OP_RECORD_AND_REREGISTER,

  /* Sent by the event callback thread in case of error: */
  OP_CALLBACK_ERROR
} EventOpThreadOpCode;

typedef enum {
  OPTHREADSTATE_NONE                           = 1,
  OPTHREADSTATE_WAITING_FOR_CONNECTION_REQUEST = 2,
  OPTHREADSTATE_WAITING_FOR_REGISTER_REQUEST   = 3,
  OPTHREADSTATE_READY                          = 4,
  OPTHREADSTATE_FATALLY_WOUNDED                = 5,
  OPTHREADSTATE_DEAD                           = 6
} EventOpThreadState;

typedef struct {
  PlatformMutexType lock;
  volatile EventOpThreadState state;
  volatile PlatformThreadIdType event_op_thread_id;

  volatile int n_event_blocks;

  /* error_info is used to transfer error information from one piece of code
   * that uses the EventOpThreadContext to another.  This member is always
   * manipulated by the EventOpThread, except during its initialization and
   * (potentially) its destruction. */
  NonPythonSQLErrorInfo *error_info;

  /* Members to support the database API calls: */
  volatile EventRequestBlock *er_blocks;

  volatile isc_db_handle db_handle;

  volatile ISC_STATUS sv[STATUS_VECTOR_SIZE];

  /* Members used to communicate with other threads: */
  /* Threads that want the EventOpThread to do something post EventOpNodes
   * bearing an EventOpThreadOpCode to this queue: */
  ThreadSafeFIFOQueue op_q;

  /* The thread(s) that make "administrative requests" of the EventOpThread
   * wait for acknowledgement on this queue; the EventOpThread provides those
   * acknowledgements in the form of AdminResponseNodes.  Although it's
   * possible for different threads to wait on this queue, they won't do it
   * simultaneously, so there's no risk of a waiting thread receiving
   * acknowledgement of an operation that another thread requested. */
  ThreadSafeFIFOQueue admin_response_q;

  /* event_q is a pointer to the EventConduit's event queue.  The EventOpThread
   * posts EventFiredNodes to this queue in reaction to EventOpNodes that it
   * receives via op_q from the EventCallbackThread.  The EventConduit.wait
   * method is where EventFiredNodes are consumed from event_q.*/
  volatile ThreadSafeFIFOQueue *event_q;
} EventOpThreadContext;


typedef enum {
  CONDUIT_STATE_CREATED,
  CONDUIT_STATE_OPEN,
  CONDUIT_STATE_CLOSED
} EventConduitState;

typedef struct {
  PyObject_HEAD /* Python API - infrastructural macro. */

  /* Since EventConduits are Python objects, access to them is implicitly
   * serialized by the GIL.  Destructive operations on those members of an
   * EventConduit that might be in use in multiple threads are not performed
   * until the destructor.  Therefore, no explicit lock is necessary. */

  EventConduitState state;

  PyObject *py_event_names;
  int n_event_names;
  int n_event_blocks;
  PyObject *py_event_counts_dict_template;

  ThreadSafeFIFOQueue event_q;

  /* The only time a thread other than the EventOpThread accesses the members
   * of op_thread_context is during the creation or destruction of the
   * EventConduit. */
  EventOpThreadContext op_thread_context;

  /* This thread reference is used during EventConduit destruction to ensure
   * that the EventOpThread has actually exited before the thread that's
   * destroying the EventOpThread's context pulls the rug out from under it. */
  PlatformThreadRefType op_thread_ref;
} EventConduit;


/* Node types used as elements of the various ThreadSafeFIFOQueues: */

typedef struct {
  volatile EventOpThreadOpCode op_code;

  #define NO_TAG -1
  volatile int tag;

  volatile void *payload; /* Might be NULL. */
} EventOpNode;

typedef struct {
  volatile EventOpThreadOpCode op_code;
  volatile long status;
  volatile ISC_STATUS sql_error_code;
  volatile char *message; /* Might be NULL. */
} AdminResponseNode;

#ifdef FIREBIRD_2_0_OR_LATER
  #define UPDATED_BUF_SIGNEDNESS unsigned
#else
  #define UPDATED_BUF_SIGNEDNESS
#endif

typedef struct {
  volatile int block_number;
  UPDATED_BUF_SIGNEDNESS char *updated_buf;
} EventCallbackOutputNode;

typedef struct {
  volatile int block_number;
  volatile long counts[EVENT_BLOCK_SIZE];
  #define COUNTS_BUF_SIZE (sizeof(long) * EVENT_BLOCK_SIZE)
} EventFiredNode;

typedef struct {
  volatile char *dsn;
  volatile short dsn_len;
  volatile char *dpb;
  volatile short dpb_len;
  volatile short dialect;
} ConnParamsNode;

/******************** MODULE TYPE DEFINITIONS:END ********************/

#endif /* ENABLE_DB_EVENT_SUPPORT */

#endif /* if not def _KIEVENTS_H */
