/*
 * Copyright Red Hat
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA. 
 *
 * $Id: //eng/uds-releases/jasper/src/uds/indexSession.h#6 $
 */

#ifndef INDEX_SESSION_H
#define INDEX_SESSION_H

#include "atomicDefs.h"
#include "config.h"
#include "cpu.h"
#include "opaqueTypes.h"
#include "threads.h"
#include "uds.h"

/**
 * The bit position of flags used to indicate index session states.
 **/
typedef enum {
  IS_FLAG_BIT_START      = 8,
  /** Flag indicating that the session is loading */
  IS_FLAG_BIT_LOADING    = IS_FLAG_BIT_START,
  /** Flag indicating that that the session has been loaded */
  IS_FLAG_BIT_LOADED,
  /** Flag indicating that the session is disabled permanently */
  IS_FLAG_BIT_DISABLED,
  /** Flag indicating that the session is suspended */
  IS_FLAG_BIT_SUSPENDED,
  /** Flag indicating that the session is waiting for an index state change */
  IS_FLAG_BIT_WAITING,
  /** Flag indicating that that the session is closing */
  IS_FLAG_BIT_CLOSING,
  /** Flag indicating that that the session is being destroyed */
  IS_FLAG_BIT_DESTROYING,
} IndexSessionFlagBit;

/**
 * The index session state flags.
 **/
typedef enum {
  IS_FLAG_LOADED     = (1 << IS_FLAG_BIT_LOADED),
  IS_FLAG_LOADING    = (1 << IS_FLAG_BIT_LOADING),
  IS_FLAG_DISABLED   = (1 << IS_FLAG_BIT_DISABLED),
  IS_FLAG_SUSPENDED  = (1 << IS_FLAG_BIT_SUSPENDED),
  IS_FLAG_WAITING    = (1 << IS_FLAG_BIT_WAITING),
  IS_FLAG_CLOSING    = (1 << IS_FLAG_BIT_CLOSING),
  IS_FLAG_DESTROYING = (1 << IS_FLAG_BIT_DESTROYING),
} IndexSessionFlag;

typedef struct __attribute__((aligned(CACHE_LINE_BYTES))) sessionStats {
  uint64_t postsFound;            /* Post calls that found an entry */
  uint64_t postsFoundOpenChapter; /* Post calls found in the open chapter */
  uint64_t postsFoundDense;       /* Post calls found in the dense index */
  uint64_t postsFoundSparse;      /* Post calls found in the sparse index */
  uint64_t postsNotFound;         /* Post calls that did not find an entry */
  uint64_t updatesFound;          /* Update calls that found an entry */
  uint64_t updatesNotFound;       /* Update calls that did not find an entry */
  uint64_t deletionsFound;        /* Delete calls that found an entry */
  uint64_t deletionsNotFound;     /* Delete calls that did not find an entry */
  uint64_t queriesFound;          /* Query calls that found an entry */
  uint64_t queriesNotFound;       /* Query calls that did not find an entry */
  uint64_t requests;              /* Total number of requests */
} SessionStats;

/**
 * States used in the index load context, reflecting the state of the index.
 **/
typedef enum {
  /** The index has not been loaded or rebuilt completely */
  INDEX_OPENING    = 0,
  /** The index is able to handle requests */
  INDEX_READY,
  /** The index has a pending request to suspend */
  INDEX_SUSPENDING,
  /** The index is suspended in the midst of a rebuild */
  INDEX_SUSPENDED,
  /** The index is being shut down while suspended */
  INDEX_FREEING,
} IndexSuspendStatus;

/**
 * The CondVar here must be notified when the status changes to
 * INDEX_SUSPENDED, in order to wake up the waiting udsSuspendIndexSession()
 * call. It must also be notified when the status changes away from
 * INDEX_SUSPENDED, to resume rebuild the index from checkForSuspend() in the
 * index.
 **/
typedef struct indexLoadContext {
  Mutex              mutex;
  CondVar            cond;
  IndexSuspendStatus status;  // Covered by indexLoadContext.mutex.
} IndexLoadContext;

/**
 * The request CondVar here must be notified when IS_FLAG_WAITING is cleared,
 * in case udsCloseIndex() or udsDestroyIndexSession() is waiting on that flag.
 * It must also be notified when IS_FLAG_CLOSING is cleared, in case
 * udsSuspendIndexSession(), udsCloseIndex() or udsDestroyIndexSession() is
 * waiting on that flag.
 * Finally, it must also be notified when IS_FLAG_LOADING is cleared, to inform
 * udsDestroyIndexSession() that the index session can be safely freed.
 **/
struct uds_index_session {
  unsigned int             state;   // Covered by requestMutex.
  IndexRouter             *router;
  RequestQueue            *callbackQueue;
  struct udsConfiguration  userConfig;
  IndexLoadContext         loadContext;
  // Asynchronous Request synchronization
  Mutex                    requestMutex;
  CondVar                  requestCond;
  int                      requestCount;
  // Request statistics, all owned by the callback thread
  SessionStats             stats;
};

/**
 * Check that the index session is usable.
 *
 * @param indexSession  the session to query
 *
 * @return UDS_SUCCESS or an error code
 **/
int checkIndexSession(struct uds_index_session *indexSession)
  __attribute__((warn_unused_result));

/**
 * Make sure that the IndexSession is allowed to load an index, and if so, set
 * its state to indicate that the load has started.
 *
 * @param indexSession  the session to load with
 *
 * @return UDS_SUCCESS, or an error code if an index already exists.
 **/
int startLoadingIndexSession(struct uds_index_session *indexSession)
  __attribute__((warn_unused_result));

/**
 * Update the IndexSession state after attempting to load an index, to indicate
 * that the load has completed, and whether or not it succeeded.
 *
 * @param indexSession  the session that was loading
 * @param result        the result of the load operation
 **/
void finishLoadingIndexSession(struct uds_index_session *indexSession,
                               int                       result);

/**
 * Disable an index session due to an error.
 *
 * @param indexSession  the session to be disabled
 **/
void disableIndexSession(struct uds_index_session *indexSession);

/**
 * Acquire the index session for an asynchronous index request.
 *
 * The pointer must eventually be released with a corresponding call to
 * releaseIndexSession().
 *
 * @param indexSession  The index session
 *
 * @return UDS_SUCCESS or an error code
 **/
int getIndexSession(struct uds_index_session *indexSession)
  __attribute__((warn_unused_result));

/**
 * Release a pointer to an index session.
 *
 * @param indexSession  The session to release
 **/
void releaseIndexSession(struct uds_index_session *indexSession);

/**
 * Construct a new, empty index session.
 *
 * @param indexSessionPtr   The pointer to receive the new session
 *
 * @return UDS_SUCCESS or an error code
 **/
int makeEmptyIndexSession(struct uds_index_session **indexSessionPtr)
  __attribute__((warn_unused_result));

/**
 * Save an index while the session is quiescent.
 *
 * During the call to #udsSaveIndex, there should be no other call to
 * #udsSaveIndex and there should be no calls to #udsStartChunkOperation.
 *
 * @param indexSession  The session to save
 *
 * @return Either #UDS_SUCCESS or an error code
 **/
int udsSaveIndex(struct uds_index_session *indexSession)
  __attribute__((warn_unused_result));

/**
 * Close the index by saving the underlying index.
 *
 * @param indexSession  The index session to be shut down and freed
 **/
int saveAndFreeIndex(struct uds_index_session *indexSession);

/**
 * Set the checkpoint frequency of the grid.
 *
 * @param session    The index session to be modified.
 * @param frequency  New checkpoint frequency.
 *
 * @return          Either UDS_SUCCESS or an error code.
 *
 **/
int udsSetCheckpointFrequency(struct uds_index_session *session,
                              unsigned int              frequency)
  __attribute__((warn_unused_result));

#endif /* INDEX_SESSION_H */
