/* Copyright (C) 2003 MySQL AB

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program 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 General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

/**
  @section ndbPool              Pooling of NDB objects
  This class implements pooling of NDB objects to support MySQL, ODBC and
  any application with a great number of threads.

  The general idea is to let the NdbPool class administer all Ndb objects.
  When a thread needs a Ndb object it request a Ndb object from the Pool.
  This interface contains some hints to ensure that the proper Ndb object
  is returned.

  The object contains an array of references to all Ndb objects together with
  an indication of whether the object is free or not.

  The idea is that the thread should keep track of the Ndb object it used the
  last time. If this object is still free it will simply get this object
  back. If the number of threads do not exceed the number of Ndb objects this
  will always be successful. In certain situations the number of threads will
  be much greater than the number of Ndb objects. In this situation the Pool
  will attempt to provide an object that is attached to the same schema
  as the thread is connected to. If this is not possible it will attempt to
  get any free Ndb object. If not even this is possible the Pool will wait
  until an Ndb object becomes free. If an Ndb object becomes available in
  time it will deliver this Ndb object. In the worst case the call will
  time-out and return NULL to indicate no free Ndb object was found in time.

  The implementation uses an array of structs which contain a reference to a
  Ndb object, whether it is in use currently and a number of references to
  set up linked lists of
  1) Free objects on a schema
  2) Free objects in LIFO order

  Usage:
  The class is a singleton.
  The first step is to call create_instance(..). If successful this will
  create the NdbPool object and return a reference to it. When completed
  drop_instance is called to remove the NdbPool object and all memory and
  other resources attached to it.

  After initialising the NdbPool object all threads can now start using the
  NdbPool. There are two methods in normal usage mode. The first
  get_ndb_object gets a Ndb object and the second return_ndb_object returns
  an Ndb object. The user of the NdbPool must keep track of the identity
  of the Ndb object. The idea is that this identity can also be used to
  find the object quickly again unless another thread have taken it. If the
  user wants any Ndb object it requests identity 0 which means any here.

  When constructing the NdbPool one can set the number of NdbConnection
  objects which are allowed in all Ndb objects. For use in synchronous
  applications such as the MySQL server 4 objects should be enough. When
  using the NdbPool for asynchronous applications one should use 1024 to
  enable a high level of parallelism. It is also possible to set the
  maximum number of Ndb objects in the pool and the initial number of
  Ndb objects allocated.
*/

#ifndef NdbPool_H
#define NdbPool_H

#include <Ndb.hpp>
#include <NdbMutex.h>
#include <NdbCondition.h>
#include <NdbOut.hpp>

class NdbPool {
#define NULL_POOL 0
#define NULL_HASH 0xFF
#define POOL_HASH_TABLE_SIZE 32
#define MAX_NDB_OBJECTS 240
  struct POOL_STRUCT {
    Ndb* ndb_reference;
    bool in_use;
    bool free_entry;
    Uint16 next_free_object;
    Uint16 prev_free_object;
    Uint16 next_db_object;
    Uint16 prev_db_object;
  };
  public:
    static NdbPool* create_instance(Ndb_cluster_connection*,
				    Uint32 max_ndb_objects = 240,
                                    Uint32 no_conn_obj = 4,
                                    Uint32 init_no_ndb_objects = 8);
    static void drop_instance();
    Ndb* get_ndb_object(Uint32 &hint_id,
                        const char* a_catalog_name,
                        const char* a_schema_name);
    void return_ndb_object(Ndb* returned_object, Uint32 id);
  private:
    bool init(Uint32 initial_no_of_ndb_objects = 8);
    void release_all();
    static bool initPoolMutex();
    NdbPool(Ndb_cluster_connection*, 
	    Uint32 max_no_of_ndb_objects, Uint32 no_conn_objects);
    ~NdbPool();
  /*
  We have three lists:
  1) A list for entries not in use
  2) A list for free entries
  3) A hash table with schema name and database name as key

  These lists are all initialised in the init call.
  The list for entries not in use is very simple since the current
  implementation have not yet any handling of dropping Ndb objects
  until all Ndb objects are dropped.
  */
    void add_free_list(Uint32 id);
    void remove_free_list(Uint32 id);
    Ndb* get_free_list(Uint32 &id, Uint32 hash_entry);

    void add_db_hash(Uint32 id);
    void remove_db_hash(Uint32 id, Uint32 hash_entry);
    Ndb* get_db_hash(Uint32 &id,
                     Uint32 hash_entry,
                     const char* a_catalog_name,
                     const char* a_schema_name);

    bool allocate_ndb(Uint32 &id,
                      const char* a_catalog_name,
                      const char* a_schema_name);
    Ndb* get_hint_ndb(Uint32 id, Uint32 hash_entry);
    Ndb* wait_free_ndb(Uint32 &id);
    Uint32 compute_hash(const char *a_schema_name);
    void add_wait_list(Uint32 id);
    void remove_wait_list();
    void switch_condition_queue();

    static NdbMutex     *pool_mutex;
    struct NdbCondition *input_pool_cond;
    struct NdbCondition *output_pool_cond;

    POOL_STRUCT *m_pool_reference;
    Uint8       *m_hash_entry;

    bool        m_inited;
    Uint32      m_no_of_conn_objects;

    Uint16      m_no_of_objects;
    Uint16      m_max_ndb_objects;
    Uint16      m_first_free;
    Uint16      m_last_free;
    Uint16      m_first_not_in_use;
    Uint16      m_waiting;
    Uint16      m_first_wait;
    Uint16      m_input_queue;
    Uint16      m_output_queue;
    Uint16      m_signal_count;

  Ndb_cluster_connection * m_cluster_connection;
};
#endif
