/* Copyright (C) 2006 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 */

/* Page cache variable structures */

#ifndef _pagecache_h
#define _pagecache_h
C_MODE_START

/* Type of the page */
enum pagecache_page_type
{
#ifndef DBUG_OFF
  /* used only for control page type chenging during debugging */
  PAGECACHE_EMPTY_PAGE,
#endif
  /* the page does not contain LSN */
  PAGECACHE_PLAIN_PAGE,
  /* the page contain LSN (maria tablespace page) */
  PAGECACHE_LSN_PAGE
};

/*
  This enum describe lock status changing. every typr of page cache will
  interpret WRITE/READ lock as it need.
*/
enum pagecache_page_lock
{
  PAGECACHE_LOCK_LEFT_UNLOCKED,       /* free  -> free  */
  PAGECACHE_LOCK_LEFT_READLOCKED,     /* read  -> read  */
  PAGECACHE_LOCK_LEFT_WRITELOCKED,    /* write -> write */
  PAGECACHE_LOCK_READ,                /* free  -> read  */
  PAGECACHE_LOCK_WRITE,               /* free  -> write */
  PAGECACHE_LOCK_READ_UNLOCK,         /* read  -> free  */
  PAGECACHE_LOCK_WRITE_UNLOCK,        /* write -> free  */
  PAGECACHE_LOCK_WRITE_TO_READ        /* write -> read  */
};
/*
  This enum describe pin status changing
*/
enum pagecache_page_pin
{
  PAGECACHE_PIN_LEFT_PINNED,   /* pinned   -> pinned   */
  PAGECACHE_PIN_LEFT_UNPINNED, /* unpinned -> unpinned */
  PAGECACHE_PIN,               /* unpinned -> pinned   */
  PAGECACHE_UNPIN              /* pinned   -> unpinned */
};
/* How to write the page */
enum pagecache_write_mode
{
  /* do not write immediately, i.e. it will be dirty page */
  PAGECACHE_WRITE_DELAY,
  /* write page to the file and put it to the cache */
  PAGECACHE_WRITE_NOW,
  /* page already is in the file. (key cache insert analogue) */
  PAGECACHE_WRITE_DONE
};

typedef void *PAGECACHE_PAGE_LINK;

/* TODO: move to loghandler emulator */
typedef void LOG_HANDLER;
typedef void *LSN;

/* file descriptor for Maria */
typedef struct st_pagecache_file
{
  int file; /* it is for debugging purposes then it will be uint32 file_no */
} PAGECACHE_FILE;

/* page number for maria */
typedef uint32 maria_page_no_t;

/* declare structures that is used by  st_pagecache */

struct st_pagecache_block_link;
typedef struct st_pagecache_block_link PAGECACHE_BLOCK_LINK;
struct st_pagecache_page;
typedef struct st_pagecache_page PAGECACHE_PAGE;
struct st_pagecache_hash_link;
typedef struct st_pagecache_hash_link PAGECACHE_HASH_LINK;

/* info about requests in a waiting queue */
typedef struct st_pagecache_wqueue
{
  struct st_my_thread_var *last_thread;  /* circular list of waiting threads */
} PAGECACHE_WQUEUE;

#define PAGECACHE_CHANGED_BLOCKS_HASH 128  /* must be power of 2 */

/*
  The page cache structure
  It also contains read-only statistics parameters.
*/

typedef struct st_pagecache
{
  my_bool inited;
  my_bool resize_in_flush;       /* true during flush of resize operation    */
  my_bool can_be_used;           /* usage of cache for read/write is allowed */
  uint shift;                    /* block size = 2 ^ shift                   */
  my_size_t mem_size;            /* specified size of the cache memory       */
  uint32 block_size;             /* size of the page buffer of a cache block */
  ulong min_warm_blocks;         /* min number of warm blocks;               */
  ulong age_threshold;           /* age threshold for hot blocks             */
  ulonglong time;                /* total number of block link operations    */
  uint hash_entries;             /* max number of entries in the hash table  */
  int hash_links;                /* max number of hash links                 */
  int hash_links_used;           /* number of hash links currently used      */
  int disk_blocks;               /* max number of blocks in the cache        */
  ulong blocks_used;           /* maximum number of concurrently used blocks */
  ulong blocks_unused;           /* number of currently unused blocks        */
  ulong blocks_changed;          /* number of currently dirty blocks         */
  ulong warm_blocks;             /* number of blocks in warm sub-chain       */
  ulong cnt_for_resize_op;       /* counter to block resize operation        */
  long blocks_available;      /* number of blocks available in the LRU chain */
  PAGECACHE_HASH_LINK **hash_root;/* arr. of entries into hash table buckets */
  PAGECACHE_HASH_LINK *hash_link_root;/* memory for hash table links         */
  PAGECACHE_HASH_LINK *free_hash_list;/* list of free hash links             */
  PAGECACHE_BLOCK_LINK *free_block_list;/* list of free blocks               */
  PAGECACHE_BLOCK_LINK *block_root;/* memory for block links                 */
  byte HUGE_PTR *block_mem;      /* memory for block buffers                 */
  PAGECACHE_BLOCK_LINK *used_last;/* ptr to the last block of the LRU chain  */
  PAGECACHE_BLOCK_LINK *used_ins;/* ptr to the insertion block in LRU chain  */
  pthread_mutex_t cache_lock;    /* to lock access to the cache structure    */
  PAGECACHE_WQUEUE resize_queue; /* threads waiting during resize operation  */
  PAGECACHE_WQUEUE waiting_for_hash_link;/* waiting for a free hash link     */
  PAGECACHE_WQUEUE waiting_for_block;   /* requests waiting for a free block */
  /* hash for dirty file bl.*/
  PAGECACHE_BLOCK_LINK *changed_blocks[PAGECACHE_CHANGED_BLOCKS_HASH];
  /* hash for other file bl.*/
  PAGECACHE_BLOCK_LINK *file_blocks[PAGECACHE_CHANGED_BLOCKS_HASH];

  LOG_HANDLER *loghandler;       /* loghandler structure */

  /*
    The following variables are and variables used to hold parameters for
    initializing the key cache.
  */

  ulonglong param_buff_size;    /* size the memory allocated for the cache  */
  ulong param_block_size;       /* size of the blocks in the key cache      */
  ulong param_division_limit;   /* min. percentage of warm blocks           */
  ulong param_age_threshold;    /* determines when hot block is downgraded  */

  /* Statistics variables. These are reset in reset_key_cache_counters(). */
  ulong global_blocks_changed;	/* number of currently dirty blocks         */
  ulonglong global_cache_w_requests;/* number of write requests (write hits) */
  ulonglong global_cache_write;     /* number of writes from cache to files  */
  ulonglong global_cache_r_requests;/* number of read requests (read hits)   */
  ulonglong global_cache_read;      /* number of reads from files to cache   */

  int blocks;                   /* max number of blocks in the cache        */
  my_bool in_init;		/* Set to 1 in MySQL during init/resize     */
} PAGECACHE;

extern int init_pagecache(PAGECACHE *pagecache, my_size_t use_mem,
                          uint division_limit, uint age_threshold,
                          uint block_size,
                          LOG_HANDLER *loghandler);
extern int resize_pagecache(PAGECACHE *pagecache,
                            my_size_t use_mem, uint division_limit,
                            uint age_threshold);
extern void change_pagecache_param(PAGECACHE *pagecache, uint division_limit,
                                   uint age_threshold);
extern byte *pagecache_read(PAGECACHE *pagecache,
                            PAGECACHE_FILE *file,
                            maria_page_no_t pageno,
                            uint level,
                            byte *buff,
                            enum pagecache_page_type type,
                            enum pagecache_page_lock lock,
                            PAGECACHE_PAGE_LINK *link);
extern my_bool pagecache_write(PAGECACHE *pagecache,
                               PAGECACHE_FILE *file,
                               maria_page_no_t pageno,
                               uint level,
                               byte *buff,
                               enum pagecache_page_type type,
                               enum pagecache_page_lock lock,
                               enum pagecache_page_pin pin,
                               enum pagecache_write_mode write_mode,
                               PAGECACHE_PAGE_LINK *link);
void pagecache_unlock_page(PAGECACHE *pagecache,
                           PAGECACHE_FILE *file,
                           maria_page_no_t pageno,
                           enum pagecache_page_lock lock,
                           enum pagecache_page_pin pin,
                           my_bool stamp_this_page,
                           LSN first_REDO_LSN_for_page);
void pagecache_unlock(PAGECACHE *pagecache,
                      PAGECACHE_PAGE_LINK *link,
                      enum pagecache_page_lock lock,
                      enum pagecache_page_pin pin,
                      my_bool stamp_this_page,
                      LSN first_REDO_LSN_for_page);
void pagecache_unpin_page(PAGECACHE *pagecache,
                          PAGECACHE_FILE *file,
                          maria_page_no_t pageno);
void pagecache_unpin(PAGECACHE *pagecache,
                     PAGECACHE_PAGE_LINK *link);
extern int flush_pagecache_blocks(PAGECACHE *keycache,
                                  PAGECACHE_FILE *file,
                                  enum flush_type type);
my_bool pagecache_delete_page(PAGECACHE *pagecache,
                              PAGECACHE_FILE *file,
                              maria_page_no_t pageno,
                              enum pagecache_page_lock lock,
                              my_bool flush);
extern void end_pagecache(PAGECACHE *keycache, my_bool cleanup);

C_MODE_END
#endif /* _keycache_h */