/*!
 * \file    LVC_RWLockManager.hpp
 * \author  IvanS
 * \brief   Reader-writer lock manager.
 */
/*

    ========== licence begin  GPL
    Copyright (c) 2002-2005 SAP AG

    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.
    ========== licence end


*/

#include "liveCache/LVC_RWLockManager.hpp"
#include "liveCache/LVC_LockRequest.hpp"

/*-----------------------------------------------------*/

LVC_RWLockManager::LVC_RWLockManager(SAPDBMem_IRawAllocator &alloc, RTESync_SpinlockPool &spinlockPool)
  : m_alloc(alloc), m_spinlock_pool(spinlockPool),
    m_rw_locks(NULL), m_hash_size(0), m_limit(0), m_count(0)
{
}

/*-----------------------------------------------------*/

LVC_RWLockManager::~LVC_RWLockManager()
{
}

/*-----------------------------------------------------*/

RTESync_IRWRegion *LVC_RWLockManager::getLock(int id, bool createNew)
{
  RTESync_LockedScope lck(m_lock);

  int bucket;
  Entry *start;
  // search for entry
  if (m_hash_size > 0) {
    bucket = id % m_hash_size;
    start = m_rw_locks[bucket];
    while (start != NULL) {
      if (start->m_id == id) {
        // found it
        return start->m_lock;
      }
      start = start->m_next;
    }
  }

  if (createNew) {
    // create new entry
    if (m_hash_size == 0) {
      // create new hash
      void *space = m_alloc.Allocate(sizeof(Entry*) * 11);
      if (space == NULL) {
        // sorry, no memory
        return NULL;
      }
      memset(space, 0, sizeof(Entry*) * 11);
      m_rw_locks = (Entry**) space;
      m_hash_size = 11;
      m_count = 0;
      m_limit = 7;
      bucket = id % m_hash_size;
    }
    void *space = m_alloc.Allocate(sizeof(Entry));
    if (space == NULL) {
      // sorry, no memory
      return NULL;
    }
    RTESync_IRWRegion *lock = RTESync_CreateRWRegion(id, m_spinlock_pool, m_alloc);
    if (lock == NULL) {
      // sorry, no memory
      m_alloc.Deallocate(space);
      return NULL;
    }

    // chain in the new entry
    start = new(space) Entry;
    start->m_id = id;
    start->m_lock = lock;
    start->m_next = m_rw_locks[bucket];
    m_rw_locks[bucket] = start;

    if (++m_count >= m_limit) {
      // try to increase hash size
      int newsize = m_hash_size * 2 + 1;
      void *space = m_alloc.Allocate(sizeof(Entry*) * newsize);
      if (space != NULL) {
        // have new block
        Entry **newentry = (Entry**) space;
        memset(newentry, 0, sizeof(Entry*) * newsize);
        for (int i = 0; i < m_hash_size; ++i) {
          Entry *e = m_rw_locks[i];
          while (e != NULL) {
            Entry *n = e->m_next;
            int newbucket = e->m_id % newsize;
            e->m_next = newentry[newbucket];
            newentry[newbucket] = e;
            e = n;
          }
        }
        space = m_rw_locks;
        m_rw_locks = newentry;
        m_hash_size = newsize;
        m_limit = (m_hash_size * 7) / 10;
        m_alloc.Deallocate(space);
      }
    }
    return start->m_lock;
  }
  return NULL;
}

