/* Copyright (C) 2014 InfiniDB, Inc.

   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; version 2 of
   the License.

   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., 51 Franklin Street, Fifth Floor, Boston,
   MA 02110-1301, USA. */

/******************************************************************************
 * $Id: mastersegmenttable.h 1823 2013-01-21 14:13:09Z rdempsey $
 *
 *****************************************************************************/

/** @file
 *
 * class MasterSegmentTable
 *
 * The MasterSegmentTable regulates access to the shared memory segments
 * used by the BRM classes and provides the means for detecting when to resize
 * a segment and when it has been relocated (due to resizing).
 *
 * XXXPAT: We should make a cleanup class here also.
 */

#pragma once

#include <stdexcept>
#include <sys/types.h>
#include <boost/thread.hpp>
#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <boost/scoped_ptr.hpp>

#include "rwlock.h"
#include "shmkeys.h"

#define EXPORT

namespace BRM
{
struct MSTEntry
{
  key_t tableShmkey;
  int allocdSize;
  int currentSize;
  EXPORT MSTEntry();
};

class MasterSegmentTableImpl
{
 public:
  ~MasterSegmentTableImpl(){};
  static MasterSegmentTableImpl* makeMasterSegmentTableImpl(int key, int size);

  static void refreshShmWithLock()
  {
    boost::mutex::scoped_lock lk(fInstanceMutex);
    return refreshShm();
  }

  static void refreshShm()
  {
    if (fInstance)
    {
      delete fInstance;
      fInstance = NULL;
    }
  }

  boost::interprocess::shared_memory_object fShmobj;
  boost::interprocess::mapped_region fMapreg;

 private:
  MasterSegmentTableImpl(int key, int size);
  MasterSegmentTableImpl(const MasterSegmentTableImpl& rhs);
  MasterSegmentTableImpl& operator=(const MasterSegmentTableImpl& rhs);

  static boost::mutex fInstanceMutex;
  static MasterSegmentTableImpl* fInstance;
};

/** @brief This class regulates access to the BRM tables in shared memory
 *
 * This class regulates access to the BRM tables in shared memory
 */
class MasterSegmentTable
{
 public:
  /** @brief Constructor.
   * @note Throws runtime_error on a semaphore-related error.
   */
  EXPORT MasterSegmentTable();
  EXPORT ~MasterSegmentTable();

  /// specifies the Extent Map table
  static const int EMTable = 0;
  /// specifies the Extent Map's Freelist table
  static const int EMFreeList = 1;
  /// specifies the Version Buffer Block Map segment
  static const int VBBMSegment = 2;
  /// specifies the Version Substitution Structure segment
  static const int VSSSegment = 3;
  /// specifies the copy lock segment
  static const int CLSegment = 4;
  /// specifies the EM Index segment
  static const int EMIndex = 5;
  /// the number of tables currently defined
  static const int nTables = 6;

  /** @brief This function gets the specified table.
   *
   * This function gets the specified table and grabs the
   * associated read lock.
   * @param num EMTable, EMFreeList, or VBBMTable
   * @param block If false, it won't block.
   * @note throws invalid_argument if num is outside the valid range
   * and runtime_error on a semaphore-related error.
   * @return If block == true, it always returns the specified MSTEntry;
   * if block == false, it can also return NULL if it could not grab
   * the table's lock.
   */
  EXPORT MSTEntry* getTable_read(int num, bool block = true) const;

  /** @brief This function gets the specified table.
   *
   * This function gets the specified table and grabs the
   * associated write lock.
   * @param num EMTable, EMFreeList, or VBBMTable
   * @param block If false, it won't block.
   * @note throws invalid_argument if num is outside the valid range
   * and runtime_error on a semaphore-related error.
   * @return If block == true, it always returns the specified MSTEntry;
   * if block == false, it can also return NULL if it could not grab
   * the table's lock.
   */
  EXPORT MSTEntry* getTable_write(int num, bool block = true) const;

  /** @brief Upgrade a read lock to a write lock.
   *
   * Upgrade a read lock to a write lock.  This is not an atomic
   * operation.
   * @param num The table the caller holds the read lock to.
   */
  EXPORT void getTable_upgrade(int num) const;

  /** @brief Downgrade a write lock to a read lock.
   *
   * Downgrade a write lock to a read lock.  This is an atomic
   * operation.
   * @param num The table the caller holds the write lock to.
   */
  EXPORT void getTable_downgrade(int num) const;

  /** @brief This function unlocks the specified table.
   *
   * This function unlocks the specified table.
   *
   * @param num EMTable, EMFreeList, or VBBMTable
   * @note throws invalid_argument if num is outside the valid range
   * and runtime_error on a semaphore-related error.
   */
  EXPORT void releaseTable_read(int num) const;

  /** @brief This function unlocks the specified table.
   *
   * This function unlocks the specified table.
   *
   * @param num EMTable, EMFreeList, or VBBMTable
   * @note throws invalid_argument if num is outside the valid range
   * and runtime_error on a semaphore-related error.
   */
  EXPORT void releaseTable_write(int num) const;

  /** @brief This function gets the current VSS key out of shared memory without locking
   *
   * This function gets the current VSS key out of shared memory without any locking. It is used
   * (eventually) by DBRM::vssLookup() to try and correctly & efficiently short-circuit a vss
   * lookup when there are no locked blocks in the vss. This read should be atomic. Even if it's
   * not, we should still be okay.
   */
  inline key_t getVSSShmkey() const
  {
    return fShmDescriptors[VSSSegment].tableShmkey;
  }

 private:
  MasterSegmentTable(const MasterSegmentTable& mst);
  MasterSegmentTable& operator=(const MasterSegmentTable& mst);

  int shmid;
  mutable boost::scoped_ptr<rwlock::RWLock> rwlock[nTables];

  static const int MSTshmsize = nTables * sizeof(MSTEntry);
  int RWLockKeys[nTables];

  /// indexed by EMTable, EMFreeList, and VBBMTable
  MSTEntry* fShmDescriptors;

  void makeMSTSegment();
  void initMSTData();
  ShmKeys fShmKeys;
  MasterSegmentTableImpl* fPImpl;
};

}  // namespace BRM

#undef EXPORT
