List:Commits« Previous MessageNext Message »
From:Jim Winstead Date:August 18 2009 10:20pm
Subject:bzr commit into libmysql branch (jimw:2861) Bug#46642
View as plain text  
#At file:///Users/jimw/my/libmysql/ based on revid:jimw@stripped

 2861 Jim Winstead	2009-08-18
      Remove iocache and keycache code from mysys, as it's not used by the
      client library or clients. (Bug #46642)

    removed:
      include/keycache.h
      mysys/mf_cache.c
      mysys/mf_iocache.c
      mysys/mf_iocache2.c
      mysys/mf_keycache.c
      mysys/mf_keycaches.c
    modified:
      CMakePlatformTests.txt
      include/my_sys.h
      mysys/CMakeLists.txt
      mysys/my_static.c
      mysys/mysys_priv.h
=== modified file 'CMakePlatformTests.txt'
--- a/CMakePlatformTests.txt	2009-06-23 19:18:48 +0000
+++ b/CMakePlatformTests.txt	2009-08-18 22:19:06 +0000
@@ -82,7 +82,6 @@ ENDIF(UNIX)
 INCLUDE (CheckFunctionExists)
 
 CHECK_FUNCTION_EXISTS (access HAVE_ACCESS)
-CHECK_FUNCTION_EXISTS (aiowait HAVE_AIOWAIT)
 CHECK_FUNCTION_EXISTS (alarm HAVE_ALARM)
 CHECK_FUNCTION_EXISTS (alloca HAVE_ALLOCA)
 CHECK_FUNCTION_EXISTS (bcmp HAVE_BCMP)

=== removed file 'include/keycache.h'
--- a/include/keycache.h	2008-07-09 07:12:43 +0000
+++ b/include/keycache.h	1970-01-01 00:00:00 +0000
@@ -1,153 +0,0 @@
-/* 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; 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
-
-/**
-  @file
-  Key cache API
-*/
-
-#ifndef _keycache_h
-#define _keycache_h
-C_MODE_START
-
-/* declare structures that is used by st_key_cache */
-
-struct st_block_link;
-typedef struct st_block_link BLOCK_LINK;
-struct st_keycache_page;
-typedef struct st_keycache_page KEYCACHE_PAGE;
-struct st_hash_link;
-typedef struct st_hash_link HASH_LINK;
-
-/* info about requests in a waiting queue */
-typedef struct st_keycache_wqueue
-{
-  struct st_my_thread_var *last_thread;  /* circular list of waiting threads */
-} KEYCACHE_WQUEUE;
-
-/** Callback called when any block is flushed */
-typedef int (*KEYCACHE_POST_WRITE_CALLBACK)(void *arg, const uchar *buffert,
-                                            uint length, my_off_t filepos);
-
-#define CHANGED_BLOCKS_HASH 128             /* must be power of 2 */
-
-/*
-  The key cache structure
-  It also contains read-only statistics parameters.
-*/   
-
-typedef struct st_key_cache
-{
-  my_bool key_cache_inited;
-  my_bool in_resize;             /* true during resize operation             */
-  my_bool resize_in_flush;       /* true during flush of resize operation    */
-  my_bool can_be_used;           /* usage of cache for read/write is allowed */
-  size_t key_cache_mem_size;      /* specified size of the cache memory       */
-  uint key_cache_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 keycache_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 */
-  HASH_LINK **hash_root;         /* arr. of entries into hash table buckets  */
-  HASH_LINK *hash_link_root;     /* memory for hash table links              */
-  HASH_LINK *free_hash_list;     /* list of free hash links                  */
-  BLOCK_LINK *free_block_list;   /* list of free blocks */
-  BLOCK_LINK *block_root;        /* memory for block links                   */
-  uchar HUGE_PTR *block_mem;     /* memory for block buffers                 */
-  BLOCK_LINK *used_last;         /* ptr to the last block of the LRU chain   */
-  BLOCK_LINK *used_ins;          /* ptr to the insertion block in LRU chain  */
-  pthread_mutex_t cache_lock;    /* to lock access to the cache structure    */
-  KEYCACHE_WQUEUE resize_queue;  /* threads waiting during resize operation  */
-  /*
-    Waiting for a zero resize count. Using a queue for symmetry though
-    only one thread can wait here.
-  */
-  KEYCACHE_WQUEUE waiting_for_resize_cnt;
-  KEYCACHE_WQUEUE waiting_for_hash_link; /* waiting for a free hash link     */
-  KEYCACHE_WQUEUE waiting_for_block;    /* requests waiting for a free block */
-  BLOCK_LINK *changed_blocks[CHANGED_BLOCKS_HASH]; /* hash for dirty file bl.*/
-  BLOCK_LINK *file_blocks[CHANGED_BLOCKS_HASH];    /* hash for other file bl.*/
-  KEYCACHE_POST_WRITE_CALLBACK post_write;/**< Called when flushing any block*/
-
-  /*
-    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     */
-} KEY_CACHE;
-
-/* The default key cache */
-extern KEY_CACHE dflt_key_cache_var, *dflt_key_cache;
-
-extern int init_key_cache(KEY_CACHE *keycache, uint key_cache_block_size,
-			  size_t use_mem, uint division_limit,
-			  uint age_threshold);
-extern int resize_key_cache(KEY_CACHE *keycache, uint key_cache_block_size,
-			    size_t use_mem, uint division_limit,
-			    uint age_threshold);
-extern void change_key_cache_param(KEY_CACHE *keycache, uint division_limit,
-				   uint age_threshold);
-extern uchar *key_cache_read(KEY_CACHE *keycache,
-                            File file, my_off_t filepos, int level,
-                            uchar *buff, uint length,
-			    uint block_length,int return_buffer);
-extern int key_cache_insert(KEY_CACHE *keycache,
-                            File file, my_off_t filepos, int level,
-                            uchar *buff, uint length);
-extern int key_cache_write(KEY_CACHE *keycache,
-                           File file, my_off_t filepos, int level,
-                           uchar *buff, uint length,
-                           uint block_length, int force_write,
-                           void *post_write_arg);
-extern int flush_key_blocks(KEY_CACHE *keycache,
-                            int file, enum flush_type type);
-extern void end_key_cache(KEY_CACHE *keycache, my_bool cleanup);
-
-/* Functions to handle multiple key caches */
-extern my_bool multi_keycache_init(void);
-extern void multi_keycache_free(void);
-extern KEY_CACHE *multi_key_cache_search(uchar *key, uint length,
-                                         KEY_CACHE *def);
-extern my_bool multi_key_cache_set(const uchar *key, uint length,
-				   KEY_CACHE *key_cache);
-extern void multi_key_cache_change(KEY_CACHE *old_data,
-				   KEY_CACHE *new_data);
-extern int reset_key_cache_counters(const char *name,
-                                    KEY_CACHE *key_cache);
-C_MODE_END
-#endif /* _keycache_h */

=== modified file 'include/my_sys.h'
--- a/include/my_sys.h	2009-05-12 08:58:44 +0000
+++ b/include/my_sys.h	2009-08-18 22:19:06 +0000
@@ -22,14 +22,6 @@
 #define _my_sys_h
 C_MODE_START
 
-#ifdef HAVE_AIOWAIT
-#include <sys/asynch.h>			/* Used by record-cache */
-typedef struct my_aio_result {
-  aio_result_t result;
-  int	       pending;
-} my_aio_result;
-#endif
-
 #ifndef THREAD
 extern int NEAR my_errno;		/* Last error in mysys */
 #else
@@ -287,7 +279,6 @@ extern int NEAR my_umask_dir,
 extern my_bool NEAR mysys_uses_curses, my_use_symdir;
 extern size_t sf_malloc_cur_memory, sf_malloc_max_memory;
 
-extern ulong	my_default_record_cache_size;
 extern my_bool NEAR my_disable_locking,NEAR my_disable_async_io,
                NEAR my_disable_flush_key_blocks, NEAR my_disable_symlinks;
 extern char	wild_many,wild_one,wild_prefix;
@@ -312,44 +303,6 @@ enum loglevel {
    INFORMATION_LEVEL
 };
 
-enum cache_type
-{
-  TYPE_NOT_SET= 0, READ_CACHE, WRITE_CACHE,
-  SEQ_READ_APPEND		/* sequential read or append */,
-  READ_FIFO, READ_NET,WRITE_NET};
-
-enum flush_type
-{
-  FLUSH_KEEP,           /* flush block and keep it in the cache */
-  FLUSH_RELEASE,        /* flush block and remove it from the cache */
-  FLUSH_IGNORE_CHANGED, /* remove block from the cache */
-  /*
-    As my_disable_flush_pagecache_blocks is always 0, the following option
-    is strictly equivalent to FLUSH_KEEP
-  */
-  FLUSH_FORCE_WRITE,
-  /**
-     @brief like FLUSH_KEEP but return immediately if file is already being
-     flushed (even partially) by another thread; only for page cache,
-     forbidden for key cache.
-  */
-  FLUSH_KEEP_LAZY
-};
-
-typedef struct st_record_cache	/* Used when cacheing records */
-{
-  File file;
-  int	rc_seek,error,inited;
-  uint	rc_length,read_length,reclength;
-  my_off_t rc_record_pos,end_of_file;
-  uchar *rc_buff,*rc_buff2,*rc_pos,*rc_end,*rc_request_pos;
-#ifdef HAVE_AIOWAIT
-  int	use_async_io;
-  my_aio_result aio_result;
-#endif
-  enum cache_type type;
-} RECORD_CACHE;
-
 enum file_type
 {
   UNOPEN = 0, FILE_BY_OPEN, FILE_BY_CREATE, STREAM_BY_FOPEN, STREAM_BY_FDOPEN,
@@ -395,217 +348,9 @@ typedef struct st_dynamic_string
   size_t length,max_length,alloc_increment;
 } DYNAMIC_STRING;
 
-struct st_io_cache;
-/** Function called when certain events happen to an IO_CACHE */
-typedef int (*IO_CACHE_CALLBACK)(struct st_io_cache *cache,
-                                 const uchar *buffert, uint length,
-                                 my_off_t filepos);
-
-#ifdef THREAD
-typedef struct st_io_cache_share
-{
-  pthread_mutex_t       mutex;           /* To sync on reads into buffer. */
-  pthread_cond_t        cond;            /* To wait for signals. */
-  pthread_cond_t        cond_writer;     /* For a synchronized writer. */
-  /* Offset in file corresponding to the first byte of buffer. */
-  my_off_t              pos_in_file;
-  /* If a synchronized write cache is the source of the data. */
-  struct st_io_cache    *source_cache;
-  uchar                 *buffer;         /* The read buffer. */
-  uchar                 *read_end;       /* Behind last valid byte of buffer. */
-  int                   running_threads; /* threads not in lock. */
-  int                   total_threads;   /* threads sharing the cache. */
-  int                   error;           /* Last error. */
-#ifdef NOT_YET_IMPLEMENTED
-  /* whether the structure should be free'd */
-  my_bool alloced;
-#endif
-} IO_CACHE_SHARE;
-#endif
-
-typedef struct st_io_cache		/* Used when cacheing files */
-{
-  /* Offset in file corresponding to the first byte of uchar* buffer. */
-  my_off_t pos_in_file;
-  /*
-    The offset of end of file for READ_CACHE and WRITE_CACHE.
-    For SEQ_READ_APPEND it the maximum of the actual end of file and
-    the position represented by read_end.
-  */
-  my_off_t end_of_file;
-  /* Points to current read position in the buffer */
-  uchar	*read_pos;
-  /* the non-inclusive boundary in the buffer for the currently valid read */
-  uchar  *read_end;
-  uchar  *buffer;				/* The read buffer */
-  /* Used in ASYNC_IO */
-  uchar  *request_pos;
-
-  /* Only used in WRITE caches and in SEQ_READ_APPEND to buffer writes */
-  uchar  *write_buffer;
-  /*
-    Only used in SEQ_READ_APPEND, and points to the current read position
-    in the write buffer. Note that reads in SEQ_READ_APPEND caches can
-    happen from both read buffer (uchar* buffer) and write buffer
-    (uchar* write_buffer).
-  */
-  uchar *append_read_pos;
-  /* Points to current write position in the write buffer */
-  uchar *write_pos;
-  /* The non-inclusive boundary of the valid write area */
-  uchar *write_end;
-
-  /*
-    Current_pos and current_end are convenience variables used by
-    my_b_tell() and other routines that need to know the current offset
-    current_pos points to &write_pos, and current_end to &write_end in a
-    WRITE_CACHE, and &read_pos and &read_end respectively otherwise
-  */
-  uchar  **current_pos, **current_end;
-#ifdef THREAD
-  /*
-    The lock is for append buffer used in SEQ_READ_APPEND cache
-    need mutex copying from append buffer to read buffer.
-  */
-  pthread_mutex_t append_buffer_lock;
-  /*
-    The following is used when several threads are reading the
-    same file in parallel. They are synchronized on disk
-    accesses reading the cached part of the file asynchronously.
-    It should be set to NULL to disable the feature.  Only
-    READ_CACHE mode is supported.
-  */
-  IO_CACHE_SHARE *share;
-#endif
-  /*
-    A caller will use my_b_read() macro to read from the cache
-    if the data is already in cache, it will be simply copied with
-    memcpy() and internal variables will be accordinging updated with
-    no functions invoked. However, if the data is not fully in the cache,
-    my_b_read() will call read_function to fetch the data. read_function
-    must never be invoked directly.
-  */
-  int (*read_function)(struct st_io_cache *,uchar *,size_t);
-  /*
-    Same idea as in the case of read_function, except my_b_write() needs to
-    be replaced with my_b_append() for a SEQ_READ_APPEND cache
-  */
-  int (*write_function)(struct st_io_cache *,const uchar *,size_t);
-  /*
-    Specifies the type of the cache. Depending on the type of the cache
-    certain operations might not be available and yield unpredicatable
-    results. Details to be documented later
-  */
-  enum cache_type type;
-  /*
-    Callbacks were added and are currently used for binary logging of LOAD
-    DATA INFILE - when a block is read from the file, we create a block
-    create/append event, and when IO_CACHE is closed, we create an end event;
-    also used to write the MyISAM WRITE_CACHE blocks to the MyISAM physical
-    log. These functions could, of course be used for other things. Note: some
-    callbacks share the same argument ("arg").
-  */
-  IO_CACHE_CALLBACK pre_read;  /**< called before reading from disk */
-  IO_CACHE_CALLBACK post_read; /**< called after reading from disk */
-  IO_CACHE_CALLBACK pre_close; /**< called before ending the cache */
-  /** Called _after_ writing to disk; not honoured by SEQ_READ_APPEND */
-  IO_CACHE_CALLBACK post_write;
-  /*
-    Counts the number of times, when we were forced to use disk. We use it to
-    increase the binlog_cache_disk_use status variable.
-  */
-  ulong disk_writes;
-  void *arg;			     /**< used by pre/post_read,post_write */
-  char *file_name;			/* if used with 'open_cached_file' */
-  char *dir,*prefix;
-  File file; /* file descriptor */
-  /*
-    seek_not_done is set by my_b_seek() to inform the upcoming read/write
-    operation that a seek needs to be preformed prior to the actual I/O
-    error is 0 if the cache operation was successful, -1 if there was a
-    "hard" error, and the actual number of I/O-ed bytes if the read/write was
-    partial.
-  */
-  int	seek_not_done,error;
-  /**
-     Cumulative 'error' since last [re]init_io_cache(). Useful if cache's user
-     polls for errors only once in a while.
-  */
-  int hard_write_error_in_the_past;
-  /* buffer_length is memory size allocated for buffer or write_buffer */
-  size_t	buffer_length;
-  /* read_length is the same as buffer_length except when we use async io */
-  size_t  read_length;
-  myf	myflags;			/* Flags used to my_read/my_write */
-  /*
-    alloced_buffer is 1 if the buffer was allocated by init_io_cache() and
-    0 if it was supplied by the user.
-    Currently READ_NET is the only one that will use a buffer allocated
-    somewhere else
-  */
-  my_bool alloced_buffer;
-#ifdef HAVE_AIOWAIT
-  /*
-    As inidicated by ifdef, this is for async I/O, which is not currently
-    used (because it's not reliable on all systems)
-  */
-  uint inited;
-  my_off_t aio_read_pos;
-  my_aio_result aio_result;
-#endif
-} IO_CACHE;
 
 typedef int (*qsort2_cmp)(const void *, const void *, const void *);
 
-	/* defines for mf_iocache */
-
-	/* Test if buffer is inited */
-#define my_b_clear(info) (info)->buffer=0
-#define my_b_inited(info) (info)->buffer
-#define my_b_EOF INT_MIN
-
-#define my_b_read(info,Buffer,Count) \
-  ((info)->read_pos + (Count) <= (info)->read_end ?\
-   (memcpy(Buffer,(info)->read_pos,(size_t) (Count)), \
-    ((info)->read_pos+=(Count)),0) :\
-   (*(info)->read_function)((info),Buffer,Count))
-
-#define my_b_write(info,Buffer,Count) \
- ((info)->write_pos + (Count) <=(info)->write_end ?\
-  (memcpy((info)->write_pos, (Buffer), (size_t)(Count)),\
-   ((info)->write_pos+=(Count)),0) : \
-   (*(info)->write_function)((info),(Buffer),(Count)))
-
-#define my_b_get(info) \
-  ((info)->read_pos != (info)->read_end ?\
-   ((info)->read_pos++, (int) (uchar) (info)->read_pos[-1]) :\
-   _my_b_get(info))
-
-	/* my_b_write_byte dosn't have any err-check */
-#define my_b_write_byte(info,chr) \
-  (((info)->write_pos < (info)->write_end) ?\
-   ((*(info)->write_pos++)=(chr)) :\
-   (_my_b_write(info,0,0) , ((*(info)->write_pos++)=(chr))))
-
-#define my_b_fill_cache(info) \
-  (((info)->read_end=(info)->read_pos),(*(info)->read_function)(info,0,0))
-
-#define my_b_tell(info) ((info)->pos_in_file + \
-			 (size_t) (*(info)->current_pos - (info)->request_pos))
-
-#define my_b_get_buffer_start(info) (info)->request_pos 
-#define my_b_get_bytes_in_buffer(info) (char*) (info)->read_end -   \
-  (char*) my_b_get_buffer_start(info)
-#define my_b_get_pos_in_file(info) (info)->pos_in_file
-
-/* tell write offset in the SEQ_APPEND cache */
-int      my_b_copy_to_file(IO_CACHE *cache, FILE *file);
-my_off_t my_b_append_tell(IO_CACHE* info);
-my_off_t my_b_safe_tell(IO_CACHE* info); /* picks the correct tell() */
-
-#define my_b_bytes_in_cache(info) (size_t) (*(info)->current_end - \
-					  *(info)->current_pos)
-
 typedef uint32 ha_checksum;
 extern ha_checksum my_crc_dbug_check;
 
@@ -789,14 +534,6 @@ extern my_bool array_append_string_uniqu
 extern void get_date(char * to,int timeflag,time_t use_time);
 extern void soundex(CHARSET_INFO *, char * out_pntr, char * in_pntr,
                     pbool remove_garbage);
-extern int init_record_cache(RECORD_CACHE *info,size_t cachesize,File file,
-			     size_t reclength,enum cache_type type,
-			     pbool use_async_io);
-extern int read_cache_record(RECORD_CACHE *info,uchar *to);
-extern int end_record_cache(RECORD_CACHE *info);
-extern int write_cache_record(RECORD_CACHE *info,my_off_t filepos,
-			      const uchar *record,size_t length);
-extern int flush_write_cache(RECORD_CACHE *info);
 extern long my_clock(void);
 extern sig_handler sigtstp_handler(int signal_number);
 extern void handle_recived_signals(void);
@@ -812,46 +549,7 @@ extern qsort_t my_qsort2(void *base_ptr,
 extern qsort2_cmp get_ptr_compare(size_t);
 void my_store_ptr(uchar *buff, size_t pack_length, my_off_t pos);
 my_off_t my_get_ptr(uchar *ptr, size_t pack_length);
-extern int init_io_cache(IO_CACHE *info,File file,size_t cachesize,
-			 enum cache_type type,my_off_t seek_offset,
-			 pbool use_async_io, myf cache_myflags);
-extern my_bool reinit_io_cache(IO_CACHE *info,enum cache_type type,
-			       my_off_t seek_offset,pbool use_async_io,
-			       pbool clear_cache);
-extern void setup_io_cache(IO_CACHE* info);
-extern int _my_b_read(IO_CACHE *info,uchar *Buffer,size_t Count);
-#ifdef THREAD
-extern int _my_b_read_r(IO_CACHE *info,uchar *Buffer,size_t Count);
-extern void init_io_cache_share(IO_CACHE *read_cache, IO_CACHE_SHARE *cshare,
-                                IO_CACHE *write_cache, uint num_threads);
-extern void remove_io_thread(IO_CACHE *info);
-#endif
-extern int _my_b_seq_read(IO_CACHE *info,uchar *Buffer,size_t Count);
-extern int _my_b_net_read(IO_CACHE *info,uchar *Buffer,size_t Count);
-extern int _my_b_get(IO_CACHE *info);
-extern int _my_b_async_read(IO_CACHE *info,uchar *Buffer,size_t Count);
-extern int _my_b_write(IO_CACHE *info,const uchar *Buffer,size_t Count);
-extern int my_b_append(IO_CACHE *info,const uchar *Buffer,size_t Count);
-extern int my_b_safe_write(IO_CACHE *info,const uchar *Buffer,size_t Count);
-
-extern int my_block_write(IO_CACHE *info, const uchar *Buffer,
-			  size_t Count, my_off_t pos);
-extern int my_b_flush_io_cache(IO_CACHE *info, int need_append_buffer_lock);
-
-#define flush_io_cache(info) my_b_flush_io_cache((info),1)
-
-extern int end_io_cache(IO_CACHE *info);
-extern size_t my_b_fill(IO_CACHE *info);
-extern void my_b_seek(IO_CACHE *info,my_off_t pos);
-extern size_t my_b_gets(IO_CACHE *info, char *to, size_t max_length);
-extern my_off_t my_b_filelength(IO_CACHE *info);
-extern size_t my_b_printf(IO_CACHE *info, const char* fmt, ...);
-extern size_t my_b_vprintf(IO_CACHE *info, const char* fmt, va_list ap);
-extern my_bool open_cached_file(IO_CACHE *cache,const char *dir,
-				 const char *prefix, size_t cache_size,
-				 myf cache_myflags);
-extern my_bool real_open_cached_file(IO_CACHE *cache);
-extern void close_cached_file(IO_CACHE *cache);
+
 File create_temp_file(char *to, const char *dir, const char *pfx,
 		      int mode, myf MyFlags);
 #define my_init_dynamic_array(A,B,C,D) init_dynamic_array2(A,B,NULL,C,D CALLER_INFO)

=== modified file 'mysys/CMakeLists.txt'
--- a/mysys/CMakeLists.txt	2009-04-17 23:40:04 +0000
+++ b/mysys/CMakeLists.txt	2009-08-18 22:19:06 +0000
@@ -14,9 +14,9 @@
 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
 SET(MYSYS_SOURCES  array.c charset-def.c charset.c checksum.c default.c default_modify.c
-				errors.c hash.c list.c md5.c mf_brkhant.c mf_cache.c mf_dirname.c mf_fn_ext.c
-				mf_format.c mf_getdate.c mf_iocache.c mf_iocache2.c mf_keycache.c my_safehash.c
-				mf_keycaches.c mf_loadpath.c mf_pack.c mf_path.c mf_qsort.c mf_qsort2.c
+				errors.c hash.c list.c md5.c mf_brkhant.c mf_dirname.c mf_fn_ext.c
+				mf_format.c mf_getdate.c my_safehash.c
+				mf_loadpath.c mf_pack.c mf_path.c mf_qsort.c mf_qsort2.c
 				mf_radix.c mf_same.c mf_sort.c mf_soundex.c mf_strip.c mf_arr_appstr.c mf_tempdir.c
 				mf_tempfile.c mf_unixpath.c mf_wcomp.c mf_wfile.c mulalloc.c my_access.c
 				my_aes.c my_alarm.c my_alloc.c my_append.c my_bit.c my_bitmap.c my_chmod.c my_chsize.c

=== removed file 'mysys/mf_cache.c'
--- a/mysys/mf_cache.c	2007-05-10 09:59:39 +0000
+++ b/mysys/mf_cache.c	1970-01-01 00:00:00 +0000
@@ -1,121 +0,0 @@
-/* Copyright (C) 2000 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; 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
-
-/* Open a temporary file and cache it with io_cache. Delete it on close */
-
-#include "mysys_priv.h"
-#include <m_string.h>
-#include "my_static.h"
-#include "mysys_err.h"
-
-	/*
-	  Remove an open tempfile so that it doesn't survive
-	  if we crash;	If the operating system doesn't support
-	  this, just remember the file name for later removal
-	*/
-
-static my_bool cache_remove_open_tmp(IO_CACHE *cache __attribute__((unused)),
-				     const char *name)
-{
-#if O_TEMPORARY == 0
-#if !defined(CANT_DELETE_OPEN_FILES)
-  /* The following should always succeed */
-  (void) my_delete(name,MYF(MY_WME | ME_NOINPUT));
-#else
-  int length;
-  if (!(cache->file_name=
-	(char*) my_malloc((length=strlen(name)+1),MYF(MY_WME))))
-  {
-    my_close(cache->file,MYF(0));
-    cache->file = -1;
-    errno=my_errno=ENOMEM;
-    return 1;
-  }
-  memcpy(cache->file_name,name,length);
-#endif
-#endif /* O_TEMPORARY == 0 */
-  return 0;
-}
-
-	/*
-	** Open tempfile cached by IO_CACHE
-	** Should be used when no seeks are done (only reinit_io_buff)
-	** Return 0 if cache is inited ok
-	** The actual file is created when the IO_CACHE buffer gets filled
-	** If dir is not given, use TMPDIR.
-	*/
-
-my_bool open_cached_file(IO_CACHE *cache, const char* dir, const char *prefix,
-                         size_t cache_size, myf cache_myflags)
-{
-  DBUG_ENTER("open_cached_file");
-  cache->dir=	 dir ? my_strdup(dir,MYF(cache_myflags & MY_WME)) : (char*) 0;
-  cache->prefix= (prefix ? my_strdup(prefix,MYF(cache_myflags & MY_WME)) :
-		 (char*) 0);
-  cache->file_name=0;
-  cache->buffer=0;				/* Mark that not open */
-  if (!init_io_cache(cache,-1,cache_size,WRITE_CACHE,0L,0,
-		     MYF(cache_myflags | MY_NABP)))
-  {
-    DBUG_RETURN(0);
-  }
-  my_free(cache->dir,	MYF(MY_ALLOW_ZERO_PTR));
-  my_free(cache->prefix,MYF(MY_ALLOW_ZERO_PTR));
-  DBUG_RETURN(1);
-}
-
-	/* Create the temporary file */
-
-my_bool real_open_cached_file(IO_CACHE *cache)
-{
-  char name_buff[FN_REFLEN];
-  int error=1;
-  DBUG_ENTER("real_open_cached_file");
-  if ((cache->file=create_temp_file(name_buff, cache->dir, cache->prefix,
-				    (O_RDWR | O_BINARY | O_TRUNC |
-				     O_TEMPORARY | O_SHORT_LIVED),
-				    MYF(MY_WME))) >= 0)
-  {
-    error=0;
-    cache_remove_open_tmp(cache, name_buff);
-  }
-  DBUG_RETURN(error);
-}
-
-
-void close_cached_file(IO_CACHE *cache)
-{
-  DBUG_ENTER("close_cached_file");
-  if (my_b_inited(cache))
-  {
-    File file=cache->file;
-    cache->file= -1;				/* Don't flush data */
-    (void) end_io_cache(cache);
-    if (file >= 0)
-    {
-      (void) my_close(file,MYF(0));
-#ifdef CANT_DELETE_OPEN_FILES
-      if (cache->file_name)
-      {
-	(void) my_delete(cache->file_name,MYF(MY_WME | ME_NOINPUT));
-	my_free(cache->file_name,MYF(0));
-      }
-#endif
-    }
-    my_free(cache->dir,MYF(MY_ALLOW_ZERO_PTR));
-    my_free(cache->prefix,MYF(MY_ALLOW_ZERO_PTR));
-  }
-  DBUG_VOID_RETURN;
-}

=== removed file 'mysys/mf_iocache.c'
--- a/mysys/mf_iocache.c	2008-07-09 07:12:43 +0000
+++ b/mysys/mf_iocache.c	1970-01-01 00:00:00 +0000
@@ -1,1967 +0,0 @@
-/* Copyright (C) 2000 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; 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
-
-/*
-  Cashing of files with only does (sequential) read or writes of fixed-
-  length records. A read isn't allowed to go over file-length. A read is ok
-  if it ends at file-length and next read can try to read after file-length
-  (and get a EOF-error).
-  Possibly use of asyncronic io.
-  macros for read and writes for faster io.
-  Used instead of FILE when reading or writing whole files.
-  This code makes mf_rec_cache obsolete (currently only used by ISAM)
-  One can change info->pos_in_file to a higher value to skip bytes in file if
-  also info->read_pos is set to info->read_end.
-  If called through open_cached_file(), then the temporary file will
-  only be created if a write exeeds the file buffer or if one calls
-  my_b_flush_io_cache().
-
-  If one uses SEQ_READ_APPEND, then two buffers are allocated, one for
-  reading and another for writing.  Reads are first done from disk and
-  then done from the write buffer.  This is an efficient way to read
-  from a log file when one is writing to it at the same time.
-  For this to work, the file has to be opened in append mode!
-  Note that when one uses SEQ_READ_APPEND, one MUST write using
-  my_b_append !  This is needed because we need to lock the mutex
-  every time we access the write buffer.
-
-TODO:
-  When one SEQ_READ_APPEND and we are reading and writing at the same time,
-  each time the write buffer gets full and it's written to disk, we will
-  always do a disk read to read a part of the buffer from disk to the
-  read buffer.
-  This should be fixed so that when we do a my_b_flush_io_cache() and
-  we have been reading the write buffer, we should transfer the rest of the
-  write buffer to the read buffer before we start to reuse it.
-*/
-
-#define MAP_TO_USE_RAID
-#include "mysys_priv.h"
-#include <m_string.h>
-#ifdef HAVE_AIOWAIT
-#include "mysys_err.h"
-static void my_aiowait(my_aio_result *result);
-#endif
-#include <errno.h>
-
-#ifdef THREAD
-#define lock_append_buffer(info) \
- pthread_mutex_lock(&(info)->append_buffer_lock)
-#define unlock_append_buffer(info) \
- pthread_mutex_unlock(&(info)->append_buffer_lock)
-#else
-#define lock_append_buffer(info)
-#define unlock_append_buffer(info)
-#endif
-
-#define IO_ROUND_UP(X) (((X)+IO_SIZE-1) & ~(IO_SIZE-1))
-#define IO_ROUND_DN(X) ( (X)            & ~(IO_SIZE-1))
-
-/*
-  Setup internal pointers inside IO_CACHE
-
-  SYNOPSIS
-    setup_io_cache()
-    info		IO_CACHE handler
-
-  NOTES
-    This is called on automaticly on init or reinit of IO_CACHE
-    It must be called externally if one moves or copies an IO_CACHE
-    object.
-*/
-
-void setup_io_cache(IO_CACHE* info)
-{
-  /* Ensure that my_b_tell() and my_b_bytes_in_cache works */
-  if (info->type == WRITE_CACHE)
-  {
-    info->current_pos= &info->write_pos;
-    info->current_end= &info->write_end;
-  }
-  else
-  {
-    info->current_pos= &info->read_pos;
-    info->current_end= &info->read_end;
-  }
-}
-
-
-static void
-init_functions(IO_CACHE* info)
-{
-  enum cache_type type= info->type;
-  switch (type) {
-  case READ_NET:
-    /*
-      Must be initialized by the caller. The problem is that
-      _my_b_net_read has to be defined in sql directory because of
-      the dependency on THD, and therefore cannot be visible to
-      programs that link against mysys but know nothing about THD, such
-      as myisamchk
-    */
-    break;
-  case SEQ_READ_APPEND:
-    info->read_function = _my_b_seq_read;
-    info->write_function = 0;			/* Force a core if used */
-    break;
-  default:
-    info->read_function =
-#ifdef THREAD
-                          info->share ? _my_b_read_r :
-#endif
-                                        _my_b_read;
-    info->write_function = _my_b_write;
-  }
-
-  setup_io_cache(info);
-}
-
-
-/* FUNCTIONS TO SET UP OR RESET A CACHE */
-
-
-/*
-  Initialize an IO_CACHE object
-
-  SYNOPSOS
-    init_io_cache()
-    info		cache handler to initialize
-    file		File that should be associated to to the handler
-			If == -1 then real_open_cached_file()
-			will be called when it's time to open file.
-    cachesize		Size of buffer to allocate for read/write
-			If == 0 then use my_default_record_cache_size
-    type		Type of cache
-    seek_offset		Where cache should start reading/writing
-    use_async_io	Set to 1 of we should use async_io (if avaiable)
-    cache_myflags	Bitmap of differnt flags
-			MY_WME | MY_FAE | MY_NABP | MY_FNABP |
-			MY_DONT_CHECK_FILESIZE
-
-  RETURN
-    0  ok
-    #  error
-*/
-
-int init_io_cache(IO_CACHE *info, File file, size_t cachesize,
-		  enum cache_type type, my_off_t seek_offset,
-		  pbool use_async_io, myf cache_myflags)
-{
-  size_t min_cache;
-  my_off_t pos;
-  my_off_t end_of_file= ~(my_off_t) 0;
-  DBUG_ENTER("init_io_cache");
-  DBUG_PRINT("enter",("cache: %p  type: %d  pos: %ld",
-		      info, (int) type, (ulong) seek_offset));
-
-  info->file= file;
-  info->type= TYPE_NOT_SET;	    /* Don't set it until mutex are created */
-  info->pos_in_file= seek_offset;
-  info->pre_close= info->pre_read= info->post_read= info->post_write= NULL;
-  info->arg = 0;
-  info->alloced_buffer = 0;
-  info->buffer=0;
-  info->seek_not_done= 0;
-
-  if (file >= 0)
-  {
-    pos= my_tell(file, MYF(0));
-    if ((pos == (my_off_t) -1) && (my_errno == ESPIPE))
-    {
-      /*
-         This kind of object doesn't support seek() or tell(). Don't set a
-         flag that will make us again try to seek() later and fail.
-      */
-      info->seek_not_done= 0;
-      /*
-        Additionally, if we're supposed to start somewhere other than the
-        the beginning of whatever this file is, then somebody made a bad
-        assumption.
-      */
-      DBUG_ASSERT(seek_offset == 0);
-    }
-    else
-      info->seek_not_done= test(seek_offset != pos);
-  }
-
-  info->disk_writes= 0;
-#ifdef THREAD
-  info->share=0;
-#endif
-
-  if (!cachesize && !(cachesize= my_default_record_cache_size))
-    DBUG_RETURN(1);				/* No cache requested */
-  min_cache=use_async_io ? IO_SIZE*4 : IO_SIZE*2;
-  if (type == READ_CACHE || type == SEQ_READ_APPEND)
-  {						/* Assume file isn't growing */
-    if (!(cache_myflags & MY_DONT_CHECK_FILESIZE))
-    {
-      /* Calculate end of file to avoid allocating oversized buffers */
-      end_of_file=my_seek(file,0L,MY_SEEK_END,MYF(0));
-      /* Need to reset seek_not_done now that we just did a seek. */
-      info->seek_not_done= end_of_file == seek_offset ? 0 : 1;
-      if (end_of_file < seek_offset)
-	end_of_file=seek_offset;
-      /* Trim cache size if the file is very small */
-      if ((my_off_t) cachesize > end_of_file-seek_offset+IO_SIZE*2-1)
-      {
-	cachesize= (size_t) (end_of_file-seek_offset)+IO_SIZE*2-1;
-	use_async_io=0;				/* No need to use async */
-      }
-    }
-  }
-  cache_myflags &= ~MY_DONT_CHECK_FILESIZE;
-  if (type != READ_NET && type != WRITE_NET)
-  {
-    /* Retry allocating memory in smaller blocks until we get one */
-    cachesize= ((cachesize + min_cache-1) & ~(min_cache-1));
-    for (;;)
-    {
-      size_t buffer_block;
-      if (cachesize < min_cache)
-	cachesize = min_cache;
-      buffer_block= cachesize;
-      if (type == SEQ_READ_APPEND)
-	buffer_block *= 2;
-      if ((info->buffer=
-	   (uchar*) my_malloc(buffer_block,
-			     MYF((cache_myflags & ~ MY_WME) |
-				 (cachesize == min_cache ? MY_WME : 0)))) != 0)
-      {
-	info->write_buffer=info->buffer;
-	if (type == SEQ_READ_APPEND)
-	  info->write_buffer = info->buffer + cachesize;
-	info->alloced_buffer=1;
-	break;					/* Enough memory found */
-      }
-      if (cachesize == min_cache)
-	DBUG_RETURN(2);				/* Can't alloc cache */
-      /* Try with less memory */
-      cachesize= (cachesize*3/4 & ~(min_cache-1));
-    }
-  }
-
-  DBUG_PRINT("info",("init_io_cache: cachesize = %lu", (ulong) cachesize));
-  info->read_length=info->buffer_length=cachesize;
-  info->myflags=cache_myflags & ~(MY_NABP | MY_FNABP);
-  info->request_pos= info->read_pos= info->write_pos = info->buffer;
-  if (type == SEQ_READ_APPEND)
-  {
-    info->append_read_pos = info->write_pos = info->write_buffer;
-    info->write_end = info->write_buffer + info->buffer_length;
-#ifdef THREAD
-    pthread_mutex_init(&info->append_buffer_lock,MY_MUTEX_INIT_FAST);
-#endif
-  }
-#if defined(SAFE_MUTEX) && defined(THREAD)
-  else
-  {
-    /* Clear mutex so that safe_mutex will notice that it's not initialized */
-    bzero((char*) &info->append_buffer_lock, sizeof(info));
-  }
-#endif
-
-  if (type == WRITE_CACHE)
-    info->write_end=
-      info->buffer+info->buffer_length- (seek_offset & (IO_SIZE-1));
-  else
-    info->read_end=info->buffer;		/* Nothing in cache */
-
-  /* End_of_file may be changed by user later */
-  info->end_of_file= end_of_file;
-  info->error= info->hard_write_error_in_the_past= 0;
-  info->type= type;
-  init_functions(info);
-#ifdef HAVE_AIOWAIT
-  if (use_async_io && ! my_disable_async_io)
-  {
-    DBUG_PRINT("info",("Using async io"));
-    info->read_length/=2;
-    info->read_function=_my_b_async_read;
-  }
-  info->inited=info->aio_result.pending=0;
-#endif
-  DBUG_RETURN(0);
-}						/* init_io_cache */
-
-	/* Wait until current request is ready */
-
-#ifdef HAVE_AIOWAIT
-static void my_aiowait(my_aio_result *result)
-{
-  if (result->pending)
-  {
-    struct aio_result_t *tmp;
-    for (;;)
-    {
-      if ((int) (tmp=aiowait((struct timeval *) 0)) == -1)
-      {
-	if (errno == EINTR)
-	  continue;
-	DBUG_PRINT("error",("No aio request, error: %d",errno));
-	result->pending=0;			/* Assume everythings is ok */
-	break;
-      }
-      ((my_aio_result*) tmp)->pending=0;
-      if ((my_aio_result*) tmp == result)
-	break;
-    }
-  }
-  return;
-}
-#endif
-
-
-/*
-  Use this to reset cache to re-start reading or to change the type
-  between READ_CACHE <-> WRITE_CACHE
-  If we are doing a reinit of a cache where we have the start of the file
-  in the cache, we are reusing this memory without flushing it to disk.
-*/
-
-my_bool reinit_io_cache(IO_CACHE *info, enum cache_type type,
-			my_off_t seek_offset,
-			pbool use_async_io __attribute__((unused)),
-			pbool clear_cache)
-{
-  DBUG_ENTER("reinit_io_cache");
-  DBUG_PRINT("enter",("cache: %p type: %d  seek_offset: %lu  clear_cache: %d",
-		      info, type, (ulong) seek_offset,
-		      (int) clear_cache));
-
-  /* One can't do reinit with the following types */
-  DBUG_ASSERT(type != READ_NET && info->type != READ_NET &&
-	      type != WRITE_NET && info->type != WRITE_NET &&
-	      type != SEQ_READ_APPEND && info->type != SEQ_READ_APPEND);
-
-  /* If the whole file is in memory, avoid flushing to disk */
-  if (! clear_cache &&
-      seek_offset >= info->pos_in_file &&
-      seek_offset <= my_b_tell(info))
-  {
-    /* Reuse current buffer without flushing it to disk */
-    uchar *pos;
-    if (info->type == WRITE_CACHE && type == READ_CACHE)
-    {
-      info->read_end=info->write_pos;
-      info->end_of_file=my_b_tell(info);
-      /*
-        Trigger a new seek only if we have a valid
-        file handle.
-      */
-      info->seek_not_done= (info->file != -1);
-    }
-    else if (type == WRITE_CACHE)
-    {
-      if (info->type == READ_CACHE)
-      {
-	info->write_end=info->write_buffer+info->buffer_length;
-	info->seek_not_done=1;
-      }
-      info->end_of_file = ~(my_off_t) 0;
-    }
-    pos=info->request_pos+(seek_offset-info->pos_in_file);
-    if (type == WRITE_CACHE)
-      info->write_pos=pos;
-    else
-      info->read_pos= pos;
-#ifdef HAVE_AIOWAIT
-    my_aiowait(&info->aio_result);		/* Wait for outstanding req */
-#endif
-  }
-  else
-  {
-    /*
-      If we change from WRITE_CACHE to READ_CACHE, assume that everything
-      after the current positions should be ignored
-    */
-    if (info->type == WRITE_CACHE && type == READ_CACHE)
-      info->end_of_file=my_b_tell(info);
-    /* flush cache if we want to reuse it */
-    if (!clear_cache && my_b_flush_io_cache(info,1))
-      DBUG_RETURN(1);
-    info->pos_in_file=seek_offset;
-    /* Better to do always do a seek */
-    info->seek_not_done=1;
-    info->request_pos=info->read_pos=info->write_pos=info->buffer;
-    if (type == READ_CACHE)
-    {
-      info->read_end=info->buffer;		/* Nothing in cache */
-    }
-    else
-    {
-      info->write_end=(info->buffer + info->buffer_length -
-		       (seek_offset & (IO_SIZE-1)));
-      info->end_of_file= ~(my_off_t) 0;
-    }
-  }
-  info->type=type;
-  info->error= info->hard_write_error_in_the_past= 0;
-  init_functions(info);
-
-#ifdef HAVE_AIOWAIT
-  if (use_async_io && ! my_disable_async_io &&
-      ((ulong) info->buffer_length <
-       (ulong) (info->end_of_file - seek_offset)))
-  {
-    info->read_length=info->buffer_length/2;
-    info->read_function=_my_b_async_read;
-  }
-  info->inited=0;
-#endif
-  DBUG_RETURN(0);
-} /* reinit_io_cache */
-
-
-/* FUNCTIONS TO DO READS FROM THE CACHE */
-
-
-/*
-  Read buffered.
-
-  SYNOPSIS
-    _my_b_read()
-      info                      IO_CACHE pointer
-      Buffer                    Buffer to retrieve count bytes from file
-      Count                     Number of bytes to read into Buffer
-
-  NOTE
-    This function is only called from the my_b_read() macro when there
-    isn't enough characters in the buffer to satisfy the request.
-
-  WARNING
-
-    When changing this function, be careful with handling file offsets
-    (end-of_file, pos_in_file). Do not cast them to possibly smaller
-    types than my_off_t unless you can be sure that their value fits.
-    Same applies to differences of file offsets.
-
-    When changing this function, check _my_b_read_r(). It might need the
-    same change.
-
-  RETURN
-    0      we succeeded in reading all data
-    1      Error: can't read requested characters
-*/
-
-int _my_b_read(register IO_CACHE *info, uchar *Buffer, size_t Count)
-{
-  size_t length,diff_length,left_length, max_length;
-  my_off_t pos_in_file;
-  DBUG_ENTER("_my_b_read");
-
-  if ((left_length= (size_t) (info->read_end-info->read_pos)))
-  {
-    DBUG_ASSERT(Count >= left_length);	/* User is not using my_b_read() */
-    memcpy(Buffer,info->read_pos, left_length);
-    Buffer+=left_length;
-    Count-=left_length;
-  }
-
-  /* pos_in_file always point on where info->buffer was read */
-  pos_in_file=info->pos_in_file+ (size_t) (info->read_end - info->buffer);
-
-  /* 
-    Whenever a function which operates on IO_CACHE flushes/writes
-    some part of the IO_CACHE to disk it will set the property
-    "seek_not_done" to indicate this to other functions operating
-    on the IO_CACHE.
-  */
-  if (info->seek_not_done)
-  {
-    if ((my_seek(info->file,pos_in_file,MY_SEEK_SET,MYF(0)) 
-        != MY_FILEPOS_ERROR))
-    {
-      /* No error, reset seek_not_done flag. */
-      info->seek_not_done= 0;
-    }
-    else
-    {
-      /*
-        If the seek failed and the error number is ESPIPE, it is because
-        info->file is a pipe or socket or FIFO.  We never should have tried
-        to seek on that.  See Bugs#25807 and #22828 for more info.
-      */
-      DBUG_ASSERT(my_errno != ESPIPE);
-      info->error= -1;
-      DBUG_RETURN(1);
-    }
-  }
-
-  diff_length= (size_t) (pos_in_file & (IO_SIZE-1));
-  if (Count >= (size_t) (IO_SIZE+(IO_SIZE-diff_length)))
-  {					/* Fill first intern buffer */
-    size_t read_length;
-    if (info->end_of_file <= pos_in_file)
-    {					/* End of file */
-      info->error= (int) left_length;
-      DBUG_RETURN(1);
-    }
-    length=(Count & (size_t) ~(IO_SIZE-1))-diff_length;
-    if ((read_length= my_read(info->file,Buffer, length, info->myflags))
-	!= length)
-    {
-      info->error= (read_length == (size_t) -1 ? -1 :
-		    (int) (read_length+left_length));
-      DBUG_RETURN(1);
-    }
-    Count-=length;
-    Buffer+=length;
-    pos_in_file+=length;
-    left_length+=length;
-    diff_length=0;
-  }
-
-  max_length= info->read_length-diff_length;
-  if (info->type != READ_FIFO &&
-      max_length > (info->end_of_file - pos_in_file))
-    max_length= (size_t) (info->end_of_file - pos_in_file);
-  if (!max_length)
-  {
-    if (Count)
-    {
-      info->error= (int) left_length; /* We only got this many char */
-      DBUG_RETURN(1);
-    }
-    length=0;				/* Didn't read any chars */
-  }
-  else if ((length= my_read(info->file,info->buffer, max_length,
-                            info->myflags)) < Count ||
-	   length == (size_t) -1)
-  {
-    if (length != (size_t) -1)
-      memcpy(Buffer, info->buffer, length);
-    info->pos_in_file= pos_in_file;
-    info->error= length == (size_t) -1 ? -1 : (int) (length+left_length);
-    info->read_pos=info->read_end=info->buffer;
-    DBUG_RETURN(1);
-  }
-  info->read_pos=info->buffer+Count;
-  info->read_end=info->buffer+length;
-  info->pos_in_file=pos_in_file;
-  memcpy(Buffer, info->buffer, Count);
-  DBUG_RETURN(0);
-}
-
-
-#ifdef THREAD
-/*
-  Prepare IO_CACHE for shared use.
-
-  SYNOPSIS
-    init_io_cache_share()
-      read_cache                A read cache. This will be copied for
-                                every thread after setup.
-      cshare                    The share.
-      write_cache               If non-NULL a write cache that is to be
-                                synchronized with the read caches.
-      num_threads               Number of threads sharing the cache
-                                including the write thread if any.
-
-  DESCRIPTION
-
-    The shared cache is used so: One IO_CACHE is initialized with
-    init_io_cache(). This includes the allocation of a buffer. Then a
-    share is allocated and init_io_cache_share() is called with the io
-    cache and the share. Then the io cache is copied for each thread. So
-    every thread has its own copy of IO_CACHE. But the allocated buffer
-    is shared because cache->buffer is the same for all caches.
-
-    One thread reads data from the file into the buffer. All threads
-    read from the buffer, but every thread maintains its own set of
-    pointers into the buffer. When all threads have used up the buffer
-    contents, one of the threads reads the next block of data into the
-    buffer. To accomplish this, each thread enters the cache lock before
-    accessing the buffer. They wait in lock_io_cache() until all threads
-    joined the lock. The last thread entering the lock is in charge of
-    reading from file to buffer. It wakes all threads when done.
-
-    Synchronizing a write cache to the read caches works so: Whenever
-    the write buffer needs a flush, the write thread enters the lock and
-    waits for all other threads to enter the lock too. They do this when
-    they have used up the read buffer. When all threads are in the lock,
-    the write thread copies the write buffer to the read buffer and
-    wakes all threads.
-
-    share->running_threads is the number of threads not being in the
-    cache lock. When entering lock_io_cache() the number is decreased.
-    When the thread that fills the buffer enters unlock_io_cache() the
-    number is reset to the number of threads. The condition
-    running_threads == 0 means that all threads are in the lock. Bumping
-    up the number to the full count is non-intuitive. But increasing the
-    number by one for each thread that leaves the lock could lead to a
-    solo run of one thread. The last thread to join a lock reads from
-    file to buffer, wakes the other threads, processes the data in the
-    cache and enters the lock again. If no other thread left the lock
-    meanwhile, it would think it's the last one again and read the next
-    block...
-
-    The share has copies of 'error', 'buffer', 'read_end', and
-    'pos_in_file' from the thread that filled the buffer. We may not be
-    able to access this information directly from its cache because the
-    thread may be removed from the share before the variables could be
-    copied by all other threads. Or, if a write buffer is synchronized,
-    it would change its 'pos_in_file' after waking the other threads,
-    possibly before they could copy its value.
-
-    However, the 'buffer' variable in the share is for a synchronized
-    write cache. It needs to know where to put the data. Otherwise it
-    would need access to the read cache of one of the threads that is
-    not yet removed from the share.
-
-  RETURN
-    void
-*/
-
-void init_io_cache_share(IO_CACHE *read_cache, IO_CACHE_SHARE *cshare,
-                         IO_CACHE *write_cache, uint num_threads)
-{
-  DBUG_ENTER("init_io_cache_share");
-  DBUG_PRINT("io_cache_share", ("read_cache: %p  share: %p  "
-                                "write_cache: %p  threads: %u",
-                                read_cache, cshare,
-                                write_cache, num_threads));
-
-  DBUG_ASSERT(num_threads > 1);
-  DBUG_ASSERT(read_cache->type == READ_CACHE);
-  DBUG_ASSERT(!write_cache || (write_cache->type == WRITE_CACHE));
-
-  pthread_mutex_init(&cshare->mutex, MY_MUTEX_INIT_FAST);
-  pthread_cond_init(&cshare->cond, 0);
-  pthread_cond_init(&cshare->cond_writer, 0);
-
-  cshare->running_threads= num_threads;
-  cshare->total_threads=   num_threads;
-  cshare->error=           0;    /* Initialize. */
-  cshare->buffer=          read_cache->buffer;
-  cshare->read_end=        NULL; /* See function comment of lock_io_cache(). */
-  cshare->pos_in_file=     0;    /* See function comment of lock_io_cache(). */
-  cshare->source_cache=    write_cache; /* Can be NULL. */
-
-  read_cache->share=         cshare;
-  read_cache->read_function= _my_b_read_r;
-  read_cache->current_pos=   NULL;
-  read_cache->current_end=   NULL;
-
-  if (write_cache)
-    write_cache->share= cshare;
-
-  DBUG_VOID_RETURN;
-}
-
-
-/*
-  Remove a thread from shared access to IO_CACHE.
-
-  SYNOPSIS
-    remove_io_thread()
-      cache                     The IO_CACHE to be removed from the share.
-
-  NOTE
-
-    Every thread must do that on exit for not to deadlock other threads.
-
-    The last thread destroys the pthread resources.
-
-    A writer flushes its cache first.
-
-  RETURN
-    void
-*/
-
-void remove_io_thread(IO_CACHE *cache)
-{
-  IO_CACHE_SHARE *cshare= cache->share;
-  uint total;
-  DBUG_ENTER("remove_io_thread");
-
-  /* If the writer goes, it needs to flush the write cache. */
-  if (cache == cshare->source_cache)
-    flush_io_cache(cache);
-
-  pthread_mutex_lock(&cshare->mutex);
-  DBUG_PRINT("io_cache_share", ("%s: %p",
-                                (cache == cshare->source_cache) ?
-                                "writer" : "reader", cache));
-
-  /* Remove from share. */
-  total= --cshare->total_threads;
-  DBUG_PRINT("io_cache_share", ("remaining threads: %u", total));
-
-  /* Detach from share. */
-  cache->share= NULL;
-
-  /* If the writer goes, let the readers know. */
-  if (cache == cshare->source_cache)
-  {
-    DBUG_PRINT("io_cache_share", ("writer leaves"));
-    cshare->source_cache= NULL;
-  }
-
-  /* If all threads are waiting for me to join the lock, wake them. */
-  if (!--cshare->running_threads)
-  {
-    DBUG_PRINT("io_cache_share", ("the last running thread leaves, wake all"));
-    pthread_cond_signal(&cshare->cond_writer);
-    pthread_cond_broadcast(&cshare->cond);
-  }
-
-  pthread_mutex_unlock(&cshare->mutex);
-
-  if (!total)
-  {
-    DBUG_PRINT("io_cache_share", ("last thread removed, destroy share"));
-    pthread_cond_destroy (&cshare->cond_writer);
-    pthread_cond_destroy (&cshare->cond);
-    pthread_mutex_destroy(&cshare->mutex);
-  }
-
-  DBUG_VOID_RETURN;
-}
-
-
-/*
-  Lock IO cache and wait for all other threads to join.
-
-  SYNOPSIS
-    lock_io_cache()
-      cache                     The cache of the thread entering the lock.
-      pos                       File position of the block to read.
-                                Unused for the write thread.
-
-  DESCRIPTION
-
-    Wait for all threads to finish with the current buffer. We want
-    all threads to proceed in concert. The last thread to join
-    lock_io_cache() will read the block from file and all threads start
-    to use it. Then they will join again for reading the next block.
-
-    The waiting threads detect a fresh buffer by comparing
-    cshare->pos_in_file with the position they want to process next.
-    Since the first block may start at position 0, we take
-    cshare->read_end as an additional condition. This variable is
-    initialized to NULL and will be set after a block of data is written
-    to the buffer.
-
-  RETURN
-    1           OK, lock in place, go ahead and read.
-    0           OK, unlocked, another thread did the read.
-*/
-
-static int lock_io_cache(IO_CACHE *cache, my_off_t pos)
-{
-  IO_CACHE_SHARE *cshare= cache->share;
-  DBUG_ENTER("lock_io_cache");
-
-  /* Enter the lock. */
-  pthread_mutex_lock(&cshare->mutex);
-  cshare->running_threads--;
-  DBUG_PRINT("io_cache_share", ("%s: %p  pos: %lu  running: %u",
-                                (cache == cshare->source_cache) ?
-                                "writer" : "reader", cache, (ulong) pos,
-                                cshare->running_threads));
-
-  if (cshare->source_cache)
-  {
-    /* A write cache is synchronized to the read caches. */
-
-    if (cache == cshare->source_cache)
-    {
-      /* The writer waits until all readers are here. */
-      while (cshare->running_threads)
-      {
-        DBUG_PRINT("io_cache_share", ("writer waits in lock"));
-        pthread_cond_wait(&cshare->cond_writer, &cshare->mutex);
-      }
-      DBUG_PRINT("io_cache_share", ("writer awoke, going to copy"));
-
-      /* Stay locked. Leave the lock later by unlock_io_cache(). */
-      DBUG_RETURN(1);
-    }
-
-    /* The last thread wakes the writer. */
-    if (!cshare->running_threads)
-    {
-      DBUG_PRINT("io_cache_share", ("waking writer"));
-      pthread_cond_signal(&cshare->cond_writer);
-    }
-
-    /*
-      Readers wait until the data is copied from the writer. Another
-      reason to stop waiting is the removal of the write thread. If this
-      happens, we leave the lock with old data in the buffer.
-    */
-    while ((!cshare->read_end || (cshare->pos_in_file < pos)) &&
-           cshare->source_cache)
-    {
-      DBUG_PRINT("io_cache_share", ("reader waits in lock"));
-      pthread_cond_wait(&cshare->cond, &cshare->mutex);
-    }
-
-    /*
-      If the writer was removed from the share while this thread was
-      asleep, we need to simulate an EOF condition. The writer cannot
-      reset the share variables as they might still be in use by readers
-      of the last block. When we awake here then because the last
-      joining thread signalled us. If the writer is not the last, it
-      will not signal. So it is safe to clear the buffer here.
-    */
-    if (!cshare->read_end || (cshare->pos_in_file < pos))
-    {
-      DBUG_PRINT("io_cache_share", ("reader found writer removed. EOF"));
-      cshare->read_end= cshare->buffer; /* Empty buffer. */
-      cshare->error= 0; /* EOF is not an error. */
-    }
-  }
-  else
-  {
-    /*
-      There are read caches only. The last thread arriving in
-      lock_io_cache() continues with a locked cache and reads the block.
-    */
-    if (!cshare->running_threads)
-    {
-      DBUG_PRINT("io_cache_share", ("last thread joined, going to read"));
-      /* Stay locked. Leave the lock later by unlock_io_cache(). */
-      DBUG_RETURN(1);
-    }
-
-    /*
-      All other threads wait until the requested block is read by the
-      last thread arriving. Another reason to stop waiting is the
-      removal of a thread. If this leads to all threads being in the
-      lock, we have to continue also. The first of the awaken threads
-      will then do the read.
-    */
-    while ((!cshare->read_end || (cshare->pos_in_file < pos)) &&
-           cshare->running_threads)
-    {
-      DBUG_PRINT("io_cache_share", ("reader waits in lock"));
-      pthread_cond_wait(&cshare->cond, &cshare->mutex);
-    }
-
-    /* If the block is not yet read, continue with a locked cache and read. */
-    if (!cshare->read_end || (cshare->pos_in_file < pos))
-    {
-      DBUG_PRINT("io_cache_share", ("reader awoke, going to read"));
-      /* Stay locked. Leave the lock later by unlock_io_cache(). */
-      DBUG_RETURN(1);
-    }
-
-    /* Another thread did read the block already. */
-  }
-  DBUG_PRINT("io_cache_share", ("reader awoke, going to process %u bytes",
-                                (uint) (cshare->read_end ? (size_t)
-                                        (cshare->read_end - cshare->buffer) :
-                                        0)));
-
-  /*
-    Leave the lock. Do not call unlock_io_cache() later. The thread that
-    filled the buffer did this and marked all threads as running.
-  */
-  pthread_mutex_unlock(&cshare->mutex);
-  DBUG_RETURN(0);
-}
-
-
-/*
-  Unlock IO cache.
-
-  SYNOPSIS
-    unlock_io_cache()
-      cache                     The cache of the thread leaving the lock.
-
-  NOTE
-    This is called by the thread that filled the buffer. It marks all
-    threads as running and awakes them. This must not be done by any
-    other thread.
-
-    Do not signal cond_writer. Either there is no writer or the writer
-    is the only one who can call this function.
-
-    The reason for resetting running_threads to total_threads before
-    waking all other threads is that it could be possible that this
-    thread is so fast with processing the buffer that it enters the lock
-    before even one other thread has left it. If every awoken thread
-    would increase running_threads by one, this thread could think that
-    he is again the last to join and would not wait for the other
-    threads to process the data.
-
-  RETURN
-    void
-*/
-
-static void unlock_io_cache(IO_CACHE *cache)
-{
-  IO_CACHE_SHARE *cshare= cache->share;
-  DBUG_ENTER("unlock_io_cache");
-  DBUG_PRINT("io_cache_share", ("%s: %p  pos: %lu  running: %u",
-                                (cache == cshare->source_cache) ?
-                                "writer" : "reader",
-                                cache, (ulong) cshare->pos_in_file,
-                                cshare->total_threads));
-
-  cshare->running_threads= cshare->total_threads;
-  pthread_cond_broadcast(&cshare->cond);
-  pthread_mutex_unlock(&cshare->mutex);
-  DBUG_VOID_RETURN;
-}
-
-
-/*
-  Read from IO_CACHE when it is shared between several threads.
-
-  SYNOPSIS
-    _my_b_read_r()
-      cache                     IO_CACHE pointer
-      Buffer                    Buffer to retrieve count bytes from file
-      Count                     Number of bytes to read into Buffer
-
-  NOTE
-    This function is only called from the my_b_read() macro when there
-    isn't enough characters in the buffer to satisfy the request.
-
-  IMPLEMENTATION
-
-    It works as follows: when a thread tries to read from a file (that
-    is, after using all the data from the (shared) buffer), it just
-    hangs on lock_io_cache(), waiting for other threads. When the very
-    last thread attempts a read, lock_io_cache() returns 1, the thread
-    does actual IO and unlock_io_cache(), which signals all the waiting
-    threads that data is in the buffer.
-
-  WARNING
-
-    When changing this function, be careful with handling file offsets
-    (end-of_file, pos_in_file). Do not cast them to possibly smaller
-    types than my_off_t unless you can be sure that their value fits.
-    Same applies to differences of file offsets. (Bug #11527)
-
-    When changing this function, check _my_b_read(). It might need the
-    same change.
-
-  RETURN
-    0      we succeeded in reading all data
-    1      Error: can't read requested characters
-*/
-
-int _my_b_read_r(register IO_CACHE *cache, uchar *Buffer, size_t Count)
-{
-  my_off_t pos_in_file;
-  size_t length, diff_length, left_length;
-  IO_CACHE_SHARE *cshare= cache->share;
-  DBUG_ENTER("_my_b_read_r");
-
-  if ((left_length= (size_t) (cache->read_end - cache->read_pos)))
-  {
-    DBUG_ASSERT(Count >= left_length);	/* User is not using my_b_read() */
-    memcpy(Buffer, cache->read_pos, left_length);
-    Buffer+= left_length;
-    Count-= left_length;
-  }
-  while (Count)
-  {
-    size_t cnt, len;
-
-    pos_in_file= cache->pos_in_file + (cache->read_end - cache->buffer);
-    diff_length= (size_t) (pos_in_file & (IO_SIZE-1));
-    length=IO_ROUND_UP(Count+diff_length)-diff_length;
-    length= ((length <= cache->read_length) ?
-             length + IO_ROUND_DN(cache->read_length - length) :
-             length - IO_ROUND_UP(length - cache->read_length));
-    if (cache->type != READ_FIFO &&
-	(length > (cache->end_of_file - pos_in_file)))
-      length= (size_t) (cache->end_of_file - pos_in_file);
-    if (length == 0)
-    {
-      cache->error= (int) left_length;
-      DBUG_RETURN(1);
-    }
-    if (lock_io_cache(cache, pos_in_file))
-    {
-      /* With a synchronized write/read cache we won't come here... */
-      DBUG_ASSERT(!cshare->source_cache);
-      /*
-        ... unless the writer has gone before this thread entered the
-        lock. Simulate EOF in this case. It can be distinguished by
-        cache->file.
-      */
-      if (cache->file < 0)
-        len= 0;
-      else
-      {
-        /*
-          Whenever a function which operates on IO_CACHE flushes/writes
-          some part of the IO_CACHE to disk it will set the property
-          "seek_not_done" to indicate this to other functions operating
-          on the IO_CACHE.
-        */
-        if (cache->seek_not_done)
-        {
-          if (my_seek(cache->file, pos_in_file, MY_SEEK_SET, MYF(0))
-              == MY_FILEPOS_ERROR)
-          {
-            cache->error= -1;
-            unlock_io_cache(cache);
-            DBUG_RETURN(1);
-          }
-        }
-        len= my_read(cache->file, cache->buffer, length, cache->myflags);
-      }
-      DBUG_PRINT("io_cache_share", ("read %lu bytes", (ulong) len));
-
-      cache->read_end=    cache->buffer + (len == (size_t) -1 ? 0 : len);
-      cache->error=       (len == length ? 0 : (int) len);
-      cache->pos_in_file= pos_in_file;
-
-      /* Copy important values to the share. */
-      cshare->error=       cache->error;
-      cshare->read_end=    cache->read_end;
-      cshare->pos_in_file= pos_in_file;
-
-      /* Mark all threads as running and wake them. */
-      unlock_io_cache(cache);
-    }
-    else
-    {
-      /*
-        With a synchronized write/read cache readers always come here.
-        Copy important values from the share.
-      */
-      cache->error=       cshare->error;
-      cache->read_end=    cshare->read_end;
-      cache->pos_in_file= cshare->pos_in_file;
-
-      len= ((cache->error == -1) ? (size_t) -1 :
-            (size_t) (cache->read_end - cache->buffer));
-    }
-    cache->read_pos=      cache->buffer;
-    cache->seek_not_done= 0;
-    if (len == 0 || len == (size_t) -1)
-    {
-      DBUG_PRINT("io_cache_share", ("reader error. len %lu  left %lu",
-                                    (ulong) len, (ulong) left_length));
-      cache->error= (int) left_length;
-      DBUG_RETURN(1);
-    }
-    cnt= (len > Count) ? Count : len;
-    memcpy(Buffer, cache->read_pos, cnt);
-    Count -= cnt;
-    Buffer+= cnt;
-    left_length+= cnt;
-    cache->read_pos+= cnt;
-  }
-  DBUG_RETURN(0);
-}
-
-
-/*
-  Copy data from write cache to read cache.
-
-  SYNOPSIS
-    copy_to_read_buffer()
-      write_cache               The write cache.
-      write_buffer              The source of data, mostly the cache buffer.
-      write_length              The number of bytes to copy.
-
-  NOTE
-    The write thread will wait for all read threads to join the cache
-    lock. Then it copies the data over and wakes the read threads.
-
-  RETURN
-    void
-*/
-
-static void copy_to_read_buffer(IO_CACHE *write_cache,
-                                const uchar *write_buffer, size_t write_length)
-{
-  IO_CACHE_SHARE *cshare= write_cache->share;
-
-  DBUG_ASSERT(cshare->source_cache == write_cache);
-  /*
-    write_length is usually less or equal to buffer_length.
-    It can be bigger if _my_b_write() is called with a big length.
-  */
-  while (write_length)
-  {
-    size_t copy_length= min(write_length, write_cache->buffer_length);
-    int  __attribute__((unused)) rc;
-
-    rc= lock_io_cache(write_cache, write_cache->pos_in_file);
-    /* The writing thread does always have the lock when it awakes. */
-    DBUG_ASSERT(rc);
-
-    memcpy(cshare->buffer, write_buffer, copy_length);
-
-    cshare->error=       0;
-    cshare->read_end=    cshare->buffer + copy_length;
-    cshare->pos_in_file= write_cache->pos_in_file;
-
-    /* Mark all threads as running and wake them. */
-    unlock_io_cache(write_cache);
-
-    write_buffer+= copy_length;
-    write_length-= copy_length;
-  }
-}
-#endif /*THREAD*/
-
-
-/*
-  Do sequential read from the SEQ_READ_APPEND cache.
-  
-  We do this in three stages:
-   - first read from info->buffer
-   - then if there are still data to read, try the file descriptor
-   - afterwards, if there are still data to read, try append buffer
-
-  RETURNS
-    0  Success
-    1  Failed to read
-*/
-
-int _my_b_seq_read(register IO_CACHE *info, uchar *Buffer, size_t Count)
-{
-  size_t length, diff_length, left_length, save_count, max_length;
-  my_off_t pos_in_file;
-  save_count=Count;
-
-  /* first, read the regular buffer */
-  if ((left_length=(size_t) (info->read_end-info->read_pos)))
-  {
-    DBUG_ASSERT(Count > left_length);	/* User is not using my_b_read() */
-    memcpy(Buffer,info->read_pos, left_length);
-    Buffer+=left_length;
-    Count-=left_length;
-  }
-  lock_append_buffer(info);
-
-  /* pos_in_file always point on where info->buffer was read */
-  if ((pos_in_file=info->pos_in_file +
-       (size_t) (info->read_end - info->buffer)) >= info->end_of_file)
-    goto read_append_buffer;
-
-  /*
-    With read-append cache we must always do a seek before we read,
-    because the write could have moved the file pointer astray
-  */
-  if (my_seek(info->file,pos_in_file,MY_SEEK_SET,MYF(0)) == MY_FILEPOS_ERROR)
-  {
-   info->error= -1;
-   unlock_append_buffer(info);
-   return (1);
-  }
-  info->seek_not_done=0;
-
-  diff_length= (size_t) (pos_in_file & (IO_SIZE-1));
-
-  /* now the second stage begins - read from file descriptor */
-  if (Count >= (size_t) (IO_SIZE+(IO_SIZE-diff_length)))
-  {
-    /* Fill first intern buffer */
-    size_t read_length;
-
-    length=(Count & (size_t) ~(IO_SIZE-1))-diff_length;
-    if ((read_length= my_read(info->file,Buffer, length,
-                              info->myflags)) == (size_t) -1)
-    {
-      info->error= -1;
-      unlock_append_buffer(info);
-      return 1;
-    }
-    Count-=read_length;
-    Buffer+=read_length;
-    pos_in_file+=read_length;
-
-    if (read_length != length)
-    {
-      /*
-	We only got part of data;  Read the rest of the data from the
-	write buffer
-      */
-      goto read_append_buffer;
-    }
-    left_length+=length;
-    diff_length=0;
-  }
-
-  max_length= info->read_length-diff_length;
-  if (max_length > (info->end_of_file - pos_in_file))
-    max_length= (size_t) (info->end_of_file - pos_in_file);
-  if (!max_length)
-  {
-    if (Count)
-      goto read_append_buffer;
-    length=0;				/* Didn't read any more chars */
-  }
-  else
-  {
-    length= my_read(info->file,info->buffer, max_length, info->myflags);
-    if (length == (size_t) -1)
-    {
-      info->error= -1;
-      unlock_append_buffer(info);
-      return 1;
-    }
-    if (length < Count)
-    {
-      memcpy(Buffer, info->buffer, length);
-      Count -= length;
-      Buffer += length;
-
-      /*
-	 added the line below to make
-	 DBUG_ASSERT(pos_in_file==info->end_of_file) pass.
-	 otherwise this does not appear to be needed
-      */
-      pos_in_file += length;
-      goto read_append_buffer;
-    }
-  }
-  unlock_append_buffer(info);
-  info->read_pos=info->buffer+Count;
-  info->read_end=info->buffer+length;
-  info->pos_in_file=pos_in_file;
-  memcpy(Buffer,info->buffer,(size_t) Count);
-  return 0;
-
-read_append_buffer:
-
-  /*
-     Read data from the current write buffer.
-     Count should never be == 0 here (The code will work even if count is 0)
-  */
-
-  {
-    /* First copy the data to Count */
-    size_t len_in_buff = (size_t) (info->write_pos - info->append_read_pos);
-    size_t copy_len;
-    size_t transfer_len;
-
-    DBUG_ASSERT(info->append_read_pos <= info->write_pos);
-    /*
-      TODO: figure out if the assert below is needed or correct.
-    */
-    DBUG_ASSERT(pos_in_file == info->end_of_file);
-    copy_len=min(Count, len_in_buff);
-    memcpy(Buffer, info->append_read_pos, copy_len);
-    info->append_read_pos += copy_len;
-    Count -= copy_len;
-    if (Count)
-      info->error= (int) (save_count - Count);
-
-    /* Fill read buffer with data from write buffer */
-    memcpy(info->buffer, info->append_read_pos,
-	   (size_t) (transfer_len=len_in_buff - copy_len));
-    info->read_pos= info->buffer;
-    info->read_end= info->buffer+transfer_len;
-    info->append_read_pos=info->write_pos;
-    info->pos_in_file=pos_in_file+copy_len;
-    info->end_of_file+=len_in_buff;
-  }
-  unlock_append_buffer(info);
-  return Count ? 1 : 0;
-}
-
-
-#ifdef HAVE_AIOWAIT
-
-/*
-  Read from the IO_CACHE into a buffer and feed asynchronously
-  from disk when needed.
-
-  SYNOPSIS
-    _my_b_async_read()
-      info                      IO_CACHE pointer
-      Buffer                    Buffer to retrieve count bytes from file
-      Count                     Number of bytes to read into Buffer
-
-  RETURN VALUE
-    -1          An error has occurred; my_errno is set.
-     0          Success
-     1          An error has occurred; IO_CACHE to error state.
-*/
-
-int _my_b_async_read(register IO_CACHE *info, uchar *Buffer, size_t Count)
-{
-  size_t length,read_length,diff_length,left_length,use_length,org_Count;
-  size_t max_length;
-  my_off_t next_pos_in_file;
-  uchar *read_buffer;
-
-  memcpy(Buffer,info->read_pos,
-	 (left_length= (size_t) (info->read_end-info->read_pos)));
-  Buffer+=left_length;
-  org_Count=Count;
-  Count-=left_length;
-
-  if (info->inited)
-  {						/* wait for read block */
-    info->inited=0;				/* No more block to read */
-    my_aiowait(&info->aio_result);		/* Wait for outstanding req */
-    if (info->aio_result.result.aio_errno)
-    {
-      if (info->myflags & MY_WME)
-	my_error(EE_READ, MYF(ME_BELL+ME_WAITTANG),
-		 my_filename(info->file),
-		 info->aio_result.result.aio_errno);
-      my_errno=info->aio_result.result.aio_errno;
-      info->error= -1;
-      return(1);
-    }
-    if (! (read_length= (size_t) info->aio_result.result.aio_return) ||
-	read_length == (size_t) -1)
-    {
-      my_errno=0;				/* For testing */
-      info->error= (read_length == (size_t) -1 ? -1 :
-		    (int) (read_length+left_length));
-      return(1);
-    }
-    info->pos_in_file+= (size_t) (info->read_end - info->request_pos);
-
-    if (info->request_pos != info->buffer)
-      info->request_pos=info->buffer;
-    else
-      info->request_pos=info->buffer+info->read_length;
-    info->read_pos=info->request_pos;
-    next_pos_in_file=info->aio_read_pos+read_length;
-
-	/* Check if pos_in_file is changed
-	   (_ni_read_cache may have skipped some bytes) */
-
-    if (info->aio_read_pos < info->pos_in_file)
-    {						/* Fix if skipped bytes */
-      if (info->aio_read_pos + read_length < info->pos_in_file)
-      {
-	read_length=0;				/* Skip block */
-	next_pos_in_file=info->pos_in_file;
-      }
-      else
-      {
-	my_off_t offset= (info->pos_in_file - info->aio_read_pos);
-	info->pos_in_file=info->aio_read_pos; /* Whe are here */
-	info->read_pos=info->request_pos+offset;
-	read_length-=offset;			/* Bytes left from read_pos */
-      }
-    }
-#ifndef DBUG_OFF
-    if (info->aio_read_pos > info->pos_in_file)
-    {
-      my_errno=EINVAL;
-      return(info->read_length= (size_t) -1);
-    }
-#endif
-	/* Copy found bytes to buffer */
-    length=min(Count,read_length);
-    memcpy(Buffer,info->read_pos,(size_t) length);
-    Buffer+=length;
-    Count-=length;
-    left_length+=length;
-    info->read_end=info->rc_pos+read_length;
-    info->read_pos+=length;
-  }
-  else
-    next_pos_in_file=(info->pos_in_file+ (size_t)
-		      (info->read_end - info->request_pos));
-
-	/* If reading large blocks, or first read or read with skip */
-  if (Count)
-  {
-    if (next_pos_in_file == info->end_of_file)
-    {
-      info->error=(int) (read_length+left_length);
-      return 1;
-    }
-    
-    if (my_seek(info->file,next_pos_in_file,MY_SEEK_SET,MYF(0))
-        == MY_FILEPOS_ERROR)
-    {
-      info->error= -1;
-      return (1);
-    }
-
-    read_length=IO_SIZE*2- (size_t) (next_pos_in_file & (IO_SIZE-1));
-    if (Count < read_length)
-    {					/* Small block, read to cache */
-      if ((read_length=my_read(info->file,info->request_pos,
-			       read_length, info->myflags)) == (size_t) -1)
-        return info->error= -1;
-      use_length=min(Count,read_length);
-      memcpy(Buffer,info->request_pos,(size_t) use_length);
-      info->read_pos=info->request_pos+Count;
-      info->read_end=info->request_pos+read_length;
-      info->pos_in_file=next_pos_in_file;	/* Start of block in cache */
-      next_pos_in_file+=read_length;
-
-      if (Count != use_length)
-      {					/* Didn't find hole block */
-	if (info->myflags & (MY_WME | MY_FAE | MY_FNABP) && Count != org_Count)
-	  my_error(EE_EOFERR, MYF(ME_BELL+ME_WAITTANG),
-		   my_filename(info->file),my_errno);
-	info->error=(int) (read_length+left_length);
-	return 1;
-      }
-    }
-    else
-    {						/* Big block, don't cache it */
-      if ((read_length= my_read(info->file,Buffer, Count,info->myflags))
-	  != Count)
-      {
-	info->error= read_length == (size_t) -1 ? -1 : read_length+left_length;
-	return 1;
-      }
-      info->read_pos=info->read_end=info->request_pos;
-      info->pos_in_file=(next_pos_in_file+=Count);
-    }
-  }
-
-  /* Read next block with asyncronic io */
-  diff_length=(next_pos_in_file & (IO_SIZE-1));
-  max_length= info->read_length - diff_length;
-  if (max_length > info->end_of_file - next_pos_in_file)
-    max_length= (size_t) (info->end_of_file - next_pos_in_file);
-
-  if (info->request_pos != info->buffer)
-    read_buffer=info->buffer;
-  else
-    read_buffer=info->buffer+info->read_length;
-  info->aio_read_pos=next_pos_in_file;
-  if (max_length)
-  {
-    info->aio_result.result.aio_errno=AIO_INPROGRESS;	/* Marker for test */
-    DBUG_PRINT("aioread",("filepos: %ld  length: %lu",
-			  (ulong) next_pos_in_file, (ulong) max_length));
-    if (aioread(info->file,read_buffer, max_length,
-		(my_off_t) next_pos_in_file,MY_SEEK_SET,
-		&info->aio_result.result))
-    {						/* Skip async io */
-      my_errno=errno;
-      DBUG_PRINT("error",("got error: %d, aio_result: %d from aioread, async skipped",
-			  errno, info->aio_result.result.aio_errno));
-      if (info->request_pos != info->buffer)
-      {
-	bmove(info->buffer,info->request_pos,
-	      (size_t) (info->read_end - info->read_pos));
-	info->request_pos=info->buffer;
-	info->read_pos-=info->read_length;
-	info->read_end-=info->read_length;
-      }
-      info->read_length=info->buffer_length;	/* Use hole buffer */
-      info->read_function=_my_b_read;		/* Use normal IO_READ next */
-    }
-    else
-      info->inited=info->aio_result.pending=1;
-  }
-  return 0;					/* Block read, async in use */
-} /* _my_b_async_read */
-#endif
-
-
-/* Read one byte when buffer is empty */
-
-int _my_b_get(IO_CACHE *info)
-{
-  uchar buff;
-  IO_CACHE_CALLBACK pre_read,post_read;
-  if ((pre_read = info->pre_read))
-    (*pre_read)(info, NULL, 0, 0);
-  if ((*(info)->read_function)(info,&buff,1))
-    return my_b_EOF;
-  if ((post_read = info->post_read))
-    (*post_read)(info, NULL, 0, 0);
-  return (int) (uchar) buff;
-}
-
-/* FUNCTIONS TO DO WRITES TO THE CACHE */
-
-#define set_hard_write_error(CACHE)                         \
-  ((CACHE)->error= (CACHE)->hard_write_error_in_the_past= -1)
-
-/* 
-   Write a byte buffer to IO_CACHE and flush to disk
-   if IO_CACHE is full.
-
-   RETURN VALUE
-    1 On error on write
-    0 On success
-   -1 On error; my_errno contains error code.
-*/
-
-int _my_b_write(register IO_CACHE *info, const uchar *Buffer, size_t Count)
-{
-  size_t rest_length,length;
-
-  if (info->pos_in_file+info->buffer_length > info->end_of_file)
-  {
-    my_errno=errno=EFBIG;
-    return set_hard_write_error(info);
-  }
-
-  rest_length= (size_t) (info->write_end - info->write_pos);
-  memcpy(info->write_pos,Buffer,(size_t) rest_length);
-  Buffer+=rest_length;
-  Count-=rest_length;
-  info->write_pos+=rest_length;
-
-  if (my_b_flush_io_cache(info,1))
-    return 1;
-  if (Count >= IO_SIZE)
-  {					/* Fill first intern buffer */
-    length=Count & (size_t) ~(IO_SIZE-1);
-    if (info->seek_not_done)
-    {
-      /*
-        Whenever a function which operates on IO_CACHE flushes/writes
-        some part of the IO_CACHE to disk it will set the property
-        "seek_not_done" to indicate this to other functions operating
-        on the IO_CACHE.
-      */
-      if (my_seek(info->file,info->pos_in_file,MY_SEEK_SET,MYF(0)))
-      {
-        set_hard_write_error(info);
-        return (1);
-      }
-      info->seek_not_done=0;
-    }
-    if (my_write(info->file, Buffer, length, info->myflags | MY_NABP))
-      return set_hard_write_error(info);
-    if (info->post_write)
-      (*(info->post_write))(info, Buffer, length, info->pos_in_file);
-
-#ifdef THREAD
-    /*
-      In case of a shared I/O cache with a writer we normally do direct
-      write cache to read cache copy. Simulate this here by direct
-      caller buffer to read cache copy. Do it after the write so that
-      the cache readers actions on the flushed part can go in parallel
-      with the write of the extra stuff. copy_to_read_buffer()
-      synchronizes writer and readers so that after this call the
-      readers can act on the extra stuff while the writer can go ahead
-      and prepare the next output. copy_to_read_buffer() relies on
-      info->pos_in_file.
-    */
-    if (info->share)
-      copy_to_read_buffer(info, Buffer, length);
-#endif
-
-    Count-=length;
-    Buffer+=length;
-    info->pos_in_file+=length;
-  }
-  memcpy(info->write_pos,Buffer,(size_t) Count);
-  info->write_pos+=Count;
-  return 0;
-}
-
-
-/*
-  Append a block to the write buffer.
-  This is done with the buffer locked to ensure that we don't read from
-  the write buffer before we are ready with it.
-*/
-
-int my_b_append(register IO_CACHE *info, const uchar *Buffer, size_t Count)
-{
-  size_t rest_length,length;
-
-#ifdef THREAD
-  /*
-    Assert that we cannot come here with a shared cache. If we do one
-    day, we might need to add a call to copy_to_read_buffer().
-  */
-  DBUG_ASSERT(!info->share);
-#endif
-  DBUG_ASSERT(info->post_write == NULL); /* unsupported */
-  lock_append_buffer(info);
-  rest_length= (size_t) (info->write_end - info->write_pos);
-  if (Count <= rest_length)
-    goto end;
-  memcpy(info->write_pos, Buffer, rest_length);
-  Buffer+=rest_length;
-  Count-=rest_length;
-  info->write_pos+=rest_length;
-  if (my_b_flush_io_cache(info,0))
-  {
-    unlock_append_buffer(info);
-    return 1;
-  }
-  if (Count >= IO_SIZE)
-  {					/* Fill first intern buffer */
-    length=Count & (size_t) ~(IO_SIZE-1);
-    if (my_write(info->file,Buffer, length, info->myflags | MY_NABP))
-    {
-      unlock_append_buffer(info);
-      return set_hard_write_error(info);
-    }
-    Count-=length;
-    Buffer+=length;
-    info->end_of_file+=length;
-  }
-
-end:
-  memcpy(info->write_pos,Buffer,(size_t) Count);
-  info->write_pos+=Count;
-  unlock_append_buffer(info);
-  return 0;
-}
-
-
-int my_b_safe_write(IO_CACHE *info, const uchar *Buffer, size_t Count)
-{
-  /*
-    Sasha: We are not writing this with the ? operator to avoid hitting
-    a possible compiler bug. At least gcc 2.95 cannot deal with 
-    several layers of ternary operators that evaluated comma(,) operator
-    expressions inside - I do have a test case if somebody wants it
-  */
-  if (info->type == SEQ_READ_APPEND)
-    return my_b_append(info, Buffer, Count);
-  return my_b_write(info, Buffer, Count);
-}
-
-
-/*
-  Write a block to disk where part of the data may be inside the record
-  buffer.  As all write calls to the data goes through the cache,
-  we will never get a seek over the end of the buffer
-*/
-
-int my_block_write(register IO_CACHE *info, const uchar *Buffer, size_t Count,
-		   my_off_t pos)
-{
-  size_t length;
-  int error=0;
-
-#ifdef THREAD
-  /*
-    Assert that we cannot come here with a shared cache. If we do one
-    day, we might need to add a call to copy_to_read_buffer().
-  */
-  DBUG_ASSERT(!info->share);
-#endif
-
-  if (pos < info->pos_in_file)
-  {
-    /* Of no overlap, write everything without buffering */
-    if (pos + Count <= info->pos_in_file)
-    {
-      int ret= my_pwrite(info->file, Buffer, Count, pos,
-                         info->myflags | MY_NABP);
-      if (unlikely(ret))
-        set_hard_write_error(info);
-      if (info->post_write)
-        (*(info->post_write))(info, Buffer, Count, pos);
-      return ret;
-    }
-    /* Write the part of the block that is before buffer */
-    length= (uint) (info->pos_in_file - pos);
-    if (my_pwrite(info->file, Buffer, length, pos, info->myflags | MY_NABP))
-      error= set_hard_write_error(info);
-    if (info->post_write)
-      (*(info->post_write))(info, Buffer, length, pos);
-    Buffer+=length;
-    pos+=  length;
-    Count-= length;
-#ifndef HAVE_PREAD
-    info->seek_not_done=1;
-#endif
-  }
-
-  /* Check if we want to write inside the used part of the buffer.*/
-  length= (size_t) (info->write_end - info->buffer);
-  if (pos < info->pos_in_file + length)
-  {
-    size_t offset= (size_t) (pos - info->pos_in_file);
-    length-=offset;
-    if (length > Count)
-      length=Count;
-    memcpy(info->buffer+offset, Buffer, length);
-    Buffer+=length;
-    Count-= length;
-    /* Fix length of buffer if the new data was larger */
-    if (info->buffer+length > info->write_pos)
-      info->write_pos=info->buffer+length;
-    if (!Count)
-      return (error);
-  }
-  /* Write at the end of the current buffer; This is the normal case */
-  if (_my_b_write(info, Buffer, Count))
-    error= -1;
-  return error;
-}
-
-
-	/* Flush write cache */
-
-#ifdef THREAD
-#define LOCK_APPEND_BUFFER if (need_append_buffer_lock) \
-  lock_append_buffer(info);
-#define UNLOCK_APPEND_BUFFER if (need_append_buffer_lock) \
-  unlock_append_buffer(info);
-#else
-#define LOCK_APPEND_BUFFER
-#define UNLOCK_APPEND_BUFFER
-#endif
-
-
-int my_b_flush_io_cache(IO_CACHE *info, int need_append_buffer_lock)
-{
-  size_t length;
-  my_bool append_cache;
-  my_off_t pos_in_file;
-  DBUG_ENTER("my_b_flush_io_cache");
-  DBUG_PRINT("enter", ("cache: 0x%lx", (long) info));
-
-  if (!(append_cache = (info->type == SEQ_READ_APPEND)))
-    need_append_buffer_lock=0;
-
-  if (info->type == WRITE_CACHE || append_cache)
-  {
-    if (info->file == -1)
-    {
-      if (real_open_cached_file(info))
-	DBUG_RETURN(set_hard_write_error(info));
-    }
-    LOCK_APPEND_BUFFER;
-
-    if ((length=(size_t) (info->write_pos - info->write_buffer)))
-    {
-#ifdef THREAD
-      /*
-        In case of a shared I/O cache with a writer we do direct write
-        cache to read cache copy. Do it before the write here so that
-        the readers can work in parallel with the write.
-        copy_to_read_buffer() relies on info->pos_in_file.
-      */
-      if (info->share)
-        copy_to_read_buffer(info, info->write_buffer, length);
-#endif
-
-      pos_in_file=info->pos_in_file;
-      /*
-	If we have append cache, we always open the file with
-	O_APPEND which moves the pos to EOF automatically on every write
-      */
-      if (!append_cache && info->seek_not_done)
-      {					/* File touched, do seek */
-	if (my_seek(info->file,pos_in_file,MY_SEEK_SET,MYF(0)) ==
-	    MY_FILEPOS_ERROR)
-	{
-	  UNLOCK_APPEND_BUFFER;
-	  DBUG_RETURN(set_hard_write_error(info));
-	}
-	if (!append_cache)
-	  info->seek_not_done=0;
-      }
-      info->write_end= (info->write_buffer+info->buffer_length-
-			((pos_in_file+length) & (IO_SIZE-1)));
-
-      if (my_write(info->file,info->write_buffer,length,
-		   info->myflags | MY_NABP))
-	set_hard_write_error(info);
-      else
-	info->error= 0;
-      if (!append_cache)
-      {
-        /*
-          This post_write is really POST-write; callers depend on this! So
-          always call it after writing to the file, not before.
-        */
-        if (info->post_write)
-          (*(info->post_write))(info, info->write_buffer,
-                                length, info->pos_in_file);
-        /*
-          The addition below will make the info->pos_in_file be the end of
-          written block; whereas the value we needed in post_write is the
-          value before the addition. That's why we called post_write before
-          this.
-        */
-	info->pos_in_file+=length;
-        set_if_bigger(info->end_of_file,(pos_in_file+length));
-      }
-      else
-      {
-	info->end_of_file+=(info->write_pos-info->append_read_pos);
-	DBUG_ASSERT(info->end_of_file == my_tell(info->file,MYF(0)));
-        DBUG_ASSERT(info->post_write == NULL); /* unsupported */
-      }
-
-      info->append_read_pos=info->write_pos=info->write_buffer;
-      ++info->disk_writes;
-      UNLOCK_APPEND_BUFFER;
-      DBUG_RETURN(info->error);
-    }
-  }
-#ifdef HAVE_AIOWAIT
-  else if (info->type != READ_NET)
-  {
-    my_aiowait(&info->aio_result);		/* Wait for outstanding req */
-    info->inited=0;
-  }
-#endif
-  UNLOCK_APPEND_BUFFER;
-  DBUG_RETURN(0);
-}
-
-/*
-  Free an IO_CACHE object
-
-  SYNOPSOS
-    end_io_cache()
-    info		IO_CACHE Handle to free
-
-  NOTES
-    It's currently safe to call this if one has called init_io_cache()
-    on the 'info' object, even if init_io_cache() failed.
-    This function is also safe to call twice with the same handle.
-
-  RETURN
-   0  ok
-   #  Error
-*/
-
-int end_io_cache(IO_CACHE *info)
-{
-  int error=0;
-  IO_CACHE_CALLBACK pre_close;
-  DBUG_ENTER("end_io_cache");
-  DBUG_PRINT("enter",("cache: %p", info));
-
-#ifdef THREAD
-  /*
-    Every thread must call remove_io_thread(). The last one destroys
-    the share elements.
-  */
-  DBUG_ASSERT(!info->share || !info->share->total_threads);
-#endif
-
-  if ((pre_close=info->pre_close))
-  {
-    (*pre_close)(info, NULL, 0, 0);
-    info->pre_close= 0;
-  }
-  if (info->alloced_buffer)
-  {
-    info->alloced_buffer=0;
-    if (info->file != -1)			/* File doesn't exist */
-      error= my_b_flush_io_cache(info,1);
-    my_free((uchar*) info->buffer,MYF(MY_WME));
-    info->buffer=info->read_pos=(uchar*) 0;
-  }
-  if (info->type == SEQ_READ_APPEND)
-  {
-    /* Destroy allocated mutex */
-    info->type= TYPE_NOT_SET;
-#ifdef THREAD
-    pthread_mutex_destroy(&info->append_buffer_lock);
-#endif
-  }
-#ifdef THREAD
-  info->share= 0;
-#endif
-  DBUG_RETURN(error);
-} /* end_io_cache */
-
-
-/**********************************************************************
- Testing of MF_IOCACHE
-**********************************************************************/
-
-#ifdef MAIN
-
-#include <my_dir.h>
-
-void die(const char* fmt, ...)
-{
-  va_list va_args;
-  va_start(va_args,fmt);
-  fprintf(stderr,"Error:");
-  vfprintf(stderr, fmt,va_args);
-  fprintf(stderr,", errno=%d\n", errno);
-  exit(1);
-}
-
-int open_file(const char* fname, IO_CACHE* info, int cache_size)
-{
-  int fd;
-  if ((fd=my_open(fname,O_CREAT | O_RDWR,MYF(MY_WME))) < 0)
-    die("Could not open %s", fname);
-  if (init_io_cache(info, fd, cache_size, SEQ_READ_APPEND, 0,0,MYF(MY_WME)))
-    die("failed in init_io_cache()");
-  return fd;
-}
-
-void close_file(IO_CACHE* info)
-{
-  end_io_cache(info);
-  my_close(info->file, MYF(MY_WME));
-}
-
-int main(int argc, char** argv)
-{
-  IO_CACHE sra_cache; /* SEQ_READ_APPEND */
-  MY_STAT status;
-  const char* fname="/tmp/iocache.test";
-  int cache_size=16384;
-  char llstr_buf[22];
-  int max_block,total_bytes=0;
-  int i,num_loops=100,error=0;
-  char *p;
-  char* block, *block_end;
-  MY_INIT(argv[0]);
-  max_block = cache_size*3;
-  if (!(block=(char*)my_malloc(max_block,MYF(MY_WME))))
-    die("Not enough memory to allocate test block");
-  block_end = block + max_block;
-  for (p = block,i=0; p < block_end;i++)
-  {
-    *p++ = (char)i;
-  }
-  if (my_stat(fname,&status, MYF(0)) &&
-      my_delete(fname,MYF(MY_WME)))
-    {
-      die("Delete of %s failed, aborting", fname);
-    }
-  open_file(fname,&sra_cache, cache_size);
-  for (i = 0; i < num_loops; i++)
-  {
-    char buf[4];
-    int block_size = abs(rand() % max_block);
-    int4store(buf, block_size);
-    if (my_b_append(&sra_cache,buf,4) ||
-	my_b_append(&sra_cache, block, block_size))
-      die("write failed");
-    total_bytes += 4+block_size;
-  }
-  close_file(&sra_cache);
-  my_free(block,MYF(MY_WME));
-  if (!my_stat(fname,&status,MYF(MY_WME)))
-    die("%s failed to stat, but I had just closed it,\
- wonder how that happened");
-  printf("Final size of %s is %s, wrote %d bytes\n",fname,
-	 llstr(status.st_size,llstr_buf),
-	 total_bytes);
-  my_delete(fname, MYF(MY_WME));
-  /* check correctness of tests */
-  if (total_bytes != status.st_size)
-  {
-    fprintf(stderr,"Not the same number of bytes acutally  in file as bytes \
-supposedly written\n");
-    error=1;
-  }
-  exit(error);
-  return 0;
-}
-#endif

=== removed file 'mysys/mf_iocache2.c'
--- a/mysys/mf_iocache2.c	2009-03-17 20:07:27 +0000
+++ b/mysys/mf_iocache2.c	1970-01-01 00:00:00 +0000
@@ -1,466 +0,0 @@
-/* Copyright (C) 2000 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; 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
-
-/*
-  More functions to be used with IO_CACHE files
-*/
-
-#define MAP_TO_USE_RAID
-#include "mysys_priv.h"
-#include <m_string.h>
-#include <stdarg.h>
-#include <m_ctype.h>
-
-/*
-  Copy contents of an IO_CACHE to a file.
-
-  SYNOPSIS
-    my_b_copy_to_file()
-    cache  IO_CACHE to copy from
-    file   File to copy to
-
-  DESCRIPTION
-    Copy the contents of the cache to the file. The cache will be
-    re-inited to a read cache and will read from the beginning of the
-    cache.
-
-    If a failure to write fully occurs, the cache is only copied
-    partially.
-
-  TODO
-    Make this function solid by handling partial reads from the cache
-    in a correct manner: it should be atomic.
-
-  RETURN VALUE
-    0  All OK
-    1  An error occured
-*/
-int
-my_b_copy_to_file(IO_CACHE *cache, FILE *file)
-{
-  size_t bytes_in_cache;
-  DBUG_ENTER("my_b_copy_to_file");
-
-  /* Reinit the cache to read from the beginning of the cache */
-  if (reinit_io_cache(cache, READ_CACHE, 0L, FALSE, FALSE))
-    DBUG_RETURN(1);
-  bytes_in_cache= my_b_bytes_in_cache(cache);
-  do
-  {
-    if (my_fwrite(file, cache->read_pos, bytes_in_cache,
-                  MYF(MY_WME | MY_NABP)) == (size_t) -1)
-      DBUG_RETURN(1);
-    cache->read_pos= cache->read_end;
-  } while ((bytes_in_cache= my_b_fill(cache)));
-  DBUG_RETURN(0);
-}
-
-
-my_off_t my_b_append_tell(IO_CACHE* info)
-{
-  /*
-    Prevent optimizer from putting res in a register when debugging
-    we need this to be able to see the value of res when the assert fails
-  */
-  dbug_volatile my_off_t res; 
-
-  /*
-    We need to lock the append buffer mutex to keep flush_io_cache()
-    from messing with the variables that we need in order to provide the
-    answer to the question.
-  */
-#ifdef THREAD
-  pthread_mutex_lock(&info->append_buffer_lock);
-#endif
-#ifndef DBUG_OFF
-  /*
-    Make sure EOF is where we think it is. Note that we cannot just use
-    my_tell() because we have a reader thread that could have left the
-    file offset in a non-EOF location
-  */
-  {
-    volatile my_off_t save_pos;
-    save_pos = my_tell(info->file,MYF(0));
-    my_seek(info->file,(my_off_t)0,MY_SEEK_END,MYF(0));
-    /*
-      Save the value of my_tell in res so we can see it when studying coredump
-    */
-    DBUG_ASSERT(info->end_of_file - (info->append_read_pos-info->write_buffer)
-		== (res=my_tell(info->file,MYF(0))));
-    my_seek(info->file,save_pos,MY_SEEK_SET,MYF(0));
-  }
-#endif  
-  res = info->end_of_file + (info->write_pos-info->append_read_pos);
-#ifdef THREAD
-  pthread_mutex_unlock(&info->append_buffer_lock);
-#endif
-  return res;
-}
-
-my_off_t my_b_safe_tell(IO_CACHE *info)
-{
-  if (unlikely(info->type == SEQ_READ_APPEND))
-    return my_b_append_tell(info);
-  return my_b_tell(info);
-}
-
-/*
-  Make next read happen at the given position
-  For write cache, make next write happen at the given position
-*/
-
-void my_b_seek(IO_CACHE *info,my_off_t pos)
-{
-  my_off_t offset;
-  DBUG_ENTER("my_b_seek");
-  DBUG_PRINT("enter",("pos: %lu", (ulong) pos));
-
-  /*
-    TODO:
-       Verify that it is OK to do seek in the non-append
-       area in SEQ_READ_APPEND cache
-     a) see if this always works
-     b) see if there is a better way to make it work
-  */
-  if (info->type == SEQ_READ_APPEND)
-    (void) flush_io_cache(info);
-
-  offset=(pos - info->pos_in_file);
-
-  if (info->type == READ_CACHE || info->type == SEQ_READ_APPEND)
-  {
-    /* TODO: explain why this works if pos < info->pos_in_file */
-    if ((ulonglong) offset < (ulonglong) (info->read_end - info->buffer))
-    {
-      /* The read is in the current buffer; Reuse it */
-      info->read_pos = info->buffer + offset;
-      DBUG_VOID_RETURN;
-    }
-    else
-    {
-      /* Force a new read on next my_b_read */
-      info->read_pos=info->read_end=info->buffer;
-    }
-  }
-  else if (info->type == WRITE_CACHE)
-  {
-    /* If write is in current buffer, reuse it */
-    if ((ulonglong) offset <
-	(ulonglong) (info->write_end - info->write_buffer))
-    {
-      info->write_pos = info->write_buffer + offset;
-      DBUG_VOID_RETURN;
-    }
-    (void) flush_io_cache(info);
-    /* Correct buffer end so that we write in increments of IO_SIZE */
-    info->write_end=(info->write_buffer+info->buffer_length-
-		     (pos & (IO_SIZE-1)));
-  }
-  info->pos_in_file=pos;
-  info->seek_not_done=1;
-  DBUG_VOID_RETURN;
-}
-
-
-/*
-  Fill buffer of the cache.
-
-  NOTES
-    This assumes that you have already used all characters in the CACHE,
-    independent of the read_pos value!
-
-  RETURN
-  0  On error or EOF (info->error = -1 on error)
-  #  Number of characters
-*/
-
-
-size_t my_b_fill(IO_CACHE *info)
-{
-  my_off_t pos_in_file=(info->pos_in_file+
-			(size_t) (info->read_end - info->buffer));
-  size_t diff_length, length, max_length;
-
-  if (info->seek_not_done)
-  {					/* File touched, do seek */
-    if (my_seek(info->file,pos_in_file,MY_SEEK_SET,MYF(0)) ==
-	MY_FILEPOS_ERROR)
-    {
-      info->error= 0;
-      return 0;
-    }
-    info->seek_not_done=0;
-  }
-  diff_length=(size_t) (pos_in_file & (IO_SIZE-1));
-  max_length=(info->read_length-diff_length);
-  if (max_length >= (info->end_of_file - pos_in_file))
-    max_length= (size_t) (info->end_of_file - pos_in_file);
-
-  if (!max_length)
-  {
-    info->error= 0;
-    return 0;					/* EOF */
-  }
-  if ((length= my_read(info->file,info->buffer,max_length,
-                       info->myflags)) == (size_t) -1)
-  {
-    info->error= -1;
-    return 0;
-  }
-  info->read_pos=info->buffer;
-  info->read_end=info->buffer+length;
-  info->pos_in_file=pos_in_file;
-  return length;
-}
-
-
-/*
-  Read a string ended by '\n' into a buffer of 'max_length' size.
-  Returns number of characters read, 0 on error.
-  last byte is set to '\0'
-  If buffer is full then to[max_length-1] will be set to \0.
-*/
-
-size_t my_b_gets(IO_CACHE *info, char *to, size_t max_length)
-{
-  char *start = to;
-  size_t length;
-  max_length--;					/* Save place for end \0 */
-
-  /* Calculate number of characters in buffer */
-  if (!(length= my_b_bytes_in_cache(info)) &&
-      !(length= my_b_fill(info)))
-    return 0;
-
-  for (;;)
-  {
-    uchar *pos, *end;
-    if (length > max_length)
-      length=max_length;
-    for (pos=info->read_pos,end=pos+length ; pos < end ;)
-    {
-      if ((*to++ = *pos++) == '\n')
-      {
-	info->read_pos=pos;
-	*to='\0';
-	return (size_t) (to-start);
-      }
-    }
-    if (!(max_length-=length))
-    {
-     /* Found enough charcters;  Return found string */
-      info->read_pos=pos;
-      *to='\0';
-      return (size_t) (to-start);
-    }
-    if (!(length=my_b_fill(info)))
-      return 0;
-  }
-}
-
-
-my_off_t my_b_filelength(IO_CACHE *info)
-{
-  if (info->type == WRITE_CACHE)
-    return my_b_tell(info);
-
-  info->seek_not_done= 1;
-  return my_seek(info->file, 0L, MY_SEEK_END, MYF(0));
-}
-
-
-/*
-  Simple printf version.  Supports '%s', '%d', '%u', "%ld" and "%lu"
-  Used for logging in MySQL
-  returns number of written character, or (size_t) -1 on error
-*/
-
-size_t my_b_printf(IO_CACHE *info, const char* fmt, ...)
-{
-  size_t result;
-  va_list args;
-  va_start(args,fmt);
-  result=my_b_vprintf(info, fmt, args);
-  va_end(args);
-  return result;
-}
-
-
-size_t my_b_vprintf(IO_CACHE *info, const char* fmt, va_list args)
-{
-  size_t out_length= 0;
-  uint minimum_width; /* as yet unimplemented */
-  uint minimum_width_sign;
-  uint precision; /* as yet unimplemented for anything but %b */
-  my_bool is_zero_padded;
-
-  /*
-    Store the location of the beginning of a format directive, for the
-    case where we learn we shouldn't have been parsing a format string
-    at all, and we don't want to lose the flag/precision/width/size
-    information.
-   */
-  const char* backtrack;
-
-  for (; *fmt != '\0'; fmt++)
-  {
-    /* Copy everything until '%' or end of string */
-    const char *start=fmt;
-    size_t length;
-    
-    for (; (*fmt != '\0') && (*fmt != '%'); fmt++) ;
-
-    length= (size_t) (fmt - start);
-    out_length+=length;
-    if (my_b_write(info, (const uchar*) start, length))
-      goto err;
-
-    if (*fmt == '\0')				/* End of format */
-      return out_length;
-
-    /* 
-      By this point, *fmt must be a percent;  Keep track of this location and
-      skip over the percent character. 
-    */
-    DBUG_ASSERT(*fmt == '%');
-    backtrack= fmt;
-    fmt++;
-
-    is_zero_padded= FALSE;
-    minimum_width_sign= 1;
-    minimum_width= 0;
-    precision= 0;
-    /* Skip if max size is used (to be compatible with printf) */
-
-process_flags:
-    switch (*fmt)
-    {
-      case '-': 
-        minimum_width_sign= -1; fmt++; goto process_flags;
-      case '0':
-        is_zero_padded= TRUE; fmt++; goto process_flags;
-      case '#':
-        /** @todo Implement "#" conversion flag. */  fmt++; goto process_flags;
-      case ' ':
-        /** @todo Implement " " conversion flag. */  fmt++; goto process_flags;
-      case '+':
-        /** @todo Implement "+" conversion flag. */  fmt++; goto process_flags;
-    }
-
-    if (*fmt == '*')
-    {
-      precision= (int) va_arg(args, int);
-      fmt++;
-    }
-    else
-    {
-      while (my_isdigit(&my_charset_latin1, *fmt)) {
-        minimum_width=(minimum_width * 10) + (*fmt - '0');
-        fmt++;
-      }
-    }
-    minimum_width*= minimum_width_sign;
-
-    if (*fmt == '.')
-    {
-      fmt++;
-      if (*fmt == '*') {
-        precision= (int) va_arg(args, int);
-        fmt++;
-      }
-      else
-      {
-        while (my_isdigit(&my_charset_latin1, *fmt)) {
-          precision=(precision * 10) + (*fmt - '0');
-          fmt++;
-        }
-      }
-    }
-
-    if (*fmt == 's')				/* String parameter */
-    {
-      reg2 char *par = va_arg(args, char *);
-      size_t length2 = strlen(par);
-      /* TODO: implement precision */
-      out_length+= length2;
-      if (my_b_write(info, (uchar*) par, length2))
-	goto err;
-    }
-    else if (*fmt == 'b')                       /* Sized buffer parameter, only precision makes sense */
-    {
-      char *par = va_arg(args, char *);
-      out_length+= precision;
-      if (my_b_write(info, (uchar*) par, precision))
-        goto err;
-    }
-    else if (*fmt == 'd' || *fmt == 'u')	/* Integer parameter */
-    {
-      register int iarg;
-      size_t length2;
-      char buff[17];
-
-      iarg = va_arg(args, int);
-      if (*fmt == 'd')
-	length2= (size_t) (int10_to_str((long) iarg,buff, -10) - buff);
-      else
-        length2= (uint) (int10_to_str((long) (uint) iarg,buff,10)- buff);
-
-      /* minimum width padding */
-      if (minimum_width > length2) 
-      {
-        uchar *buffz;
-                    
-        buffz= (uchar*) my_alloca(minimum_width - length2);
-        if (is_zero_padded)
-          memset(buffz, '0', minimum_width - length2);
-        else
-          memset(buffz, ' ', minimum_width - length2);
-        my_b_write(info, buffz, minimum_width - length2);
-        my_afree(buffz);
-      }
-
-      out_length+= length2;
-      if (my_b_write(info, (uchar*) buff, length2))
-	goto err;
-    }
-    else if ((*fmt == 'l' && fmt[1] == 'd') || fmt[1] == 'u')
-      /* long parameter */
-    {
-      register long iarg;
-      size_t length2;
-      char buff[17];
-
-      iarg = va_arg(args, long);
-      if (*++fmt == 'd')
-	length2= (size_t) (int10_to_str(iarg,buff, -10) - buff);
-      else
-	length2= (size_t) (int10_to_str(iarg,buff,10)- buff);
-      out_length+= length2;
-      if (my_b_write(info, (uchar*) buff, length2))
-	goto err;
-    }
-    else
-    {
-      /* %% or unknown code */
-      if (my_b_write(info, (uchar*) backtrack, (size_t) (fmt-backtrack)))
-        goto err;
-      out_length+= fmt-backtrack;
-    }
-  }
-  return out_length;
-
-err:
-  return (size_t) -1;
-}

=== removed file 'mysys/mf_keycache.c'
--- a/mysys/mf_keycache.c	2009-04-30 14:35:36 +0000
+++ b/mysys/mf_keycache.c	1970-01-01 00:00:00 +0000
@@ -1,4580 +0,0 @@
-/* Copyright (C) 2000 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; 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
-
-/**
-  @file
-  These functions handle keyblock cacheing for ISAM and MyISAM tables.
-
-  One cache can handle many files.
-  It must contain buffers of the same blocksize.
-  init_key_cache() should be used to init cache handler.
-
-  The free list (free_block_list) is a stack like structure.
-  When a block is freed by free_block(), it is pushed onto the stack.
-  When a new block is required it is first tried to pop one from the stack.
-  If the stack is empty, it is tried to get a never-used block from the pool.
-  If this is empty too, then a block is taken from the LRU ring, flushing it
-  to disk, if neccessary. This is handled in find_key_block().
-  With the new free list, the blocks can have three temperatures:
-  hot, warm and cold (which is free). This is remembered in the block header
-  by the enum BLOCK_TEMPERATURE temperature variable. Remembering the
-  temperature is neccessary to correctly count the number of warm blocks,
-  which is required to decide when blocks are allowed to become hot. Whenever
-  a block is inserted to another (sub-)chain, we take the old and new
-  temperature into account to decide if we got one more or less warm block.
-  blocks_unused is the sum of never used blocks in the pool and of currently
-  free blocks. blocks_used is the number of blocks fetched from the pool and
-  as such gives the maximum number of in-use blocks at any time.
-*/
-
-/*
-  Key Cache Locking
-  =================
-
-  All key cache locking is done with a single mutex per key cache:
-  keycache->cache_lock. This mutex is locked almost all the time
-  when executing code in this file (mf_keycache.c).
-  However it is released for I/O and some copy operations.
-
-  The cache_lock is also released when waiting for some event. Waiting
-  and signalling is done via condition variables. In most cases the
-  thread waits on its thread->suspend condition variable. Every thread
-  has a my_thread_var structure, which contains this variable and a
-  '*next' and '**prev' pointer. These pointers are used to insert the
-  thread into a wait queue.
-
-  A thread can wait for one block and thus be in one wait queue at a
-  time only.
-
-  Before starting to wait on its condition variable with
-  pthread_cond_wait(), the thread enters itself to a specific wait queue
-  with link_into_queue() (double linked with '*next' + '**prev') or
-  wait_on_queue() (single linked with '*next').
-
-  Another thread, when releasing a resource, looks up the waiting thread
-  in the related wait queue. It sends a signal with
-  pthread_cond_signal() to the waiting thread.
-
-  NOTE: Depending on the particular wait situation, either the sending
-  thread removes the waiting thread from the wait queue with
-  unlink_from_queue() or release_whole_queue() respectively, or the waiting
-  thread removes itself.
-
-  There is one exception from this locking scheme when one thread wants
-  to reuse a block for some other address. This works by first marking
-  the block reserved (status= BLOCK_IN_SWITCH) and then waiting for all
-  threads that are reading the block to finish. Each block has a
-  reference to a condition variable (condvar). It holds a reference to
-  the thread->suspend condition variable for the waiting thread (if such
-  a thread exists). When that thread is signaled, the reference is
-  cleared. The number of readers of a block is registered in
-  block->hash_link->requests. See wait_for_readers() / remove_reader()
-  for details. This is similar to the above, but it clearly means that
-  only one thread can wait for a particular block. There is no queue in
-  this case. Strangely enough block->convar is used for waiting for the
-  assigned hash_link only. More precisely it is used to wait for all
-  requests to be unregistered from the assigned hash_link.
-
-  The resize_queue serves two purposes:
-  1. Threads that want to do a resize wait there if in_resize is set.
-     This is not used in the server. The server refuses a second resize
-     request if one is already active. keycache->in_init is used for the
-     synchronization. See set_var.cc.
-  2. Threads that want to access blocks during resize wait here during
-     the re-initialization phase.
-  When the resize is done, all threads on the queue are signalled.
-  Hypothetical resizers can compete for resizing, and read/write
-  requests will restart to request blocks from the freshly resized
-  cache. If the cache has been resized too small, it is disabled and
-  'can_be_used' is false. In this case read/write requests bypass the
-  cache. Since they increment and decrement 'cnt_for_resize_op', the
-  next resizer can wait on the queue 'waiting_for_resize_cnt' until all
-  I/O finished.
-*/
-
-#include "mysys_priv.h"
-#include "mysys_err.h"
-#include <keycache.h>
-#include "my_static.h"
-#include <m_string.h>
-#include <my_bit.h>
-#include <errno.h>
-#include <stdarg.h>
-
-/*
-  Some compilation flags have been added specifically for this module
-  to control the following:
-  - not to let a thread to yield the control when reading directly
-    from key cache, which might improve performance in many cases;
-    to enable this add:
-    #define SERIALIZED_READ_FROM_CACHE
-  - to set an upper bound for number of threads simultaneously
-    using the key cache; this setting helps to determine an optimal
-    size for hash table and improve performance when the number of
-    blocks in the key cache much less than the number of threads
-    accessing it;
-    to set this number equal to <N> add
-      #define MAX_THREADS <N>
-  - to substitute calls of pthread_cond_wait for calls of
-    pthread_cond_timedwait (wait with timeout set up);
-    this setting should be used only when you want to trap a deadlock
-    situation, which theoretically should not happen;
-    to set timeout equal to <T> seconds add
-      #define KEYCACHE_TIMEOUT <T>
-  - to enable the module traps and to send debug information from
-    key cache module to a special debug log add:
-      #define KEYCACHE_DEBUG
-    the name of this debug log file <LOG NAME> can be set through:
-      #define KEYCACHE_DEBUG_LOG  <LOG NAME>
-    if the name is not defined, it's set by default;
-    if the KEYCACHE_DEBUG flag is not set up and we are in a debug
-    mode, i.e. when ! defined(DBUG_OFF), the debug information from the
-    module is sent to the regular debug log.
-
-  Example of the settings:
-    #define SERIALIZED_READ_FROM_CACHE
-    #define MAX_THREADS   100
-    #define KEYCACHE_TIMEOUT  1
-    #define KEYCACHE_DEBUG
-    #define KEYCACHE_DEBUG_LOG  "my_key_cache_debug.log"
-*/
-
-#define STRUCT_PTR(TYPE, MEMBER, a)                                           \
-          (TYPE *) ((char *) (a) - offsetof(TYPE, MEMBER))
-
-/* types of condition variables */
-#define  COND_FOR_REQUESTED 0
-#define  COND_FOR_SAVED     1
-#define  COND_FOR_READERS   2
-
-typedef pthread_cond_t KEYCACHE_CONDVAR;
-
-/* descriptor of the page in the key cache block buffer */
-struct st_keycache_page
-{
-  int file;               /* file to which the page belongs to  */
-  my_off_t filepos;       /* position of the page in the file   */
-};
-
-/* element in the chain of a hash table bucket */
-struct st_hash_link
-{
-  struct st_hash_link *next, **prev; /* to connect links in the same bucket  */
-  struct st_block_link *block;       /* reference to the block for the page: */
-  File file;                         /* from such a file                     */
-  my_off_t diskpos;                  /* with such an offset                  */
-  uint requests;                     /* number of requests for the page      */
-};
-
-/* simple states of a block */
-#define BLOCK_ERROR           1 /* an error occured when performing file i/o */
-#define BLOCK_READ            2 /* file block is in the block buffer         */
-#define BLOCK_IN_SWITCH       4 /* block is preparing to read new page       */
-#define BLOCK_REASSIGNED      8 /* blk does not accept requests for old page */
-#define BLOCK_IN_FLUSH       16 /* block is selected for flush               */
-#define BLOCK_CHANGED        32 /* block buffer contains a dirty page        */
-#define BLOCK_IN_USE         64 /* block is not free                         */
-#define BLOCK_IN_EVICTION   128 /* block is selected for eviction            */
-#define BLOCK_IN_FLUSHWRITE 256 /* block is in write to file                 */
-#define BLOCK_FOR_UPDATE    512 /* block is selected for buffer modification */
-
-/* page status, returned by find_key_block */
-#define PAGE_READ               0
-#define PAGE_TO_BE_READ         1
-#define PAGE_WAIT_TO_BE_READ    2
-
-/* block temperature determines in which (sub-)chain the block currently is */
-enum BLOCK_TEMPERATURE { BLOCK_COLD /*free*/ , BLOCK_WARM , BLOCK_HOT };
-
-/* key cache block */
-struct st_block_link
-{
-  struct st_block_link
-    *next_used, **prev_used;   /* to connect links in the LRU chain (ring)   */
-  struct st_block_link
-    *next_changed, **prev_changed; /* for lists of file dirty/clean blocks   */
-  struct st_hash_link *hash_link; /* backward ptr to referring hash_link     */
-  KEYCACHE_WQUEUE wqueue[2]; /* queues on waiting requests for new/old pages */
-  uint requests;          /* number of requests for the block                */
-  uchar *buffer;           /* buffer for the block page                       */
-  uint offset;            /* beginning of modified data in the buffer        */
-  uint length;            /* end of data in the buffer                       */
-  uint status;            /* state of the block                              */
-  enum BLOCK_TEMPERATURE temperature; /* block temperature: cold, warm, hot */
-  uint hits_left;         /* number of hits left until promotion             */
-  ulonglong last_hit_time; /* timestamp of the last hit                      */
-  KEYCACHE_CONDVAR *condvar; /* condition variable for 'no readers' event    */
-  void                         *post_write_arg;   /**< post_write's argument*/
-};
-
-KEY_CACHE dflt_key_cache_var;
-KEY_CACHE *dflt_key_cache= &dflt_key_cache_var;
-
-#define FLUSH_CACHE         2000            /* sort this many blocks at once */
-
-static int flush_all_key_blocks(KEY_CACHE *keycache);
-#ifdef THREAD
-static void wait_on_queue(KEYCACHE_WQUEUE *wqueue,
-                          pthread_mutex_t *mutex);
-static void release_whole_queue(KEYCACHE_WQUEUE *wqueue);
-#else
-#define wait_on_queue(wqueue, mutex)    do {} while (0)
-#define release_whole_queue(wqueue)     do {} while (0)
-#endif
-static void free_block(KEY_CACHE *keycache, BLOCK_LINK *block);
-#if !defined(DBUG_OFF)
-static void test_key_cache(KEY_CACHE *keycache,
-                           const char *where, my_bool lock);
-#endif
-
-#define KEYCACHE_HASH(f, pos)                                                 \
-(((ulong) ((pos) / keycache->key_cache_block_size) +                          \
-                                     (ulong) (f)) & (keycache->hash_entries-1))
-#define FILE_HASH(f)                 ((uint) (f) & (CHANGED_BLOCKS_HASH-1))
-
-#define DEFAULT_KEYCACHE_DEBUG_LOG  "keycache_debug.log"
-
-#if defined(KEYCACHE_DEBUG) && ! defined(KEYCACHE_DEBUG_LOG)
-#define KEYCACHE_DEBUG_LOG  DEFAULT_KEYCACHE_DEBUG_LOG
-#endif
-
-#if defined(KEYCACHE_DEBUG_LOG)
-static FILE *keycache_debug_log=NULL;
-static void keycache_debug_print _VARARGS((const char *fmt,...));
-#define KEYCACHE_DEBUG_OPEN                                                   \
-          if (!keycache_debug_log)                                            \
-          {                                                                   \
-            keycache_debug_log= fopen(KEYCACHE_DEBUG_LOG, "w");               \
-            (void) setvbuf(keycache_debug_log, NULL, _IOLBF, BUFSIZ);         \
-          }
-
-#define KEYCACHE_DEBUG_CLOSE                                                  \
-          if (keycache_debug_log)                                             \
-          {                                                                   \
-            fclose(keycache_debug_log);                                       \
-            keycache_debug_log= 0;                                            \
-          }
-#else
-#define KEYCACHE_DEBUG_OPEN
-#define KEYCACHE_DEBUG_CLOSE
-#endif /* defined(KEYCACHE_DEBUG_LOG) */
-
-#if defined(KEYCACHE_DEBUG_LOG) && defined(KEYCACHE_DEBUG)
-#define KEYCACHE_DBUG_PRINT(l, m)                                             \
-            { if (keycache_debug_log) fprintf(keycache_debug_log, "%s: ", l); \
-              keycache_debug_print m; }
-
-#define KEYCACHE_DBUG_ASSERT(a)                                               \
-            { if (! (a) && keycache_debug_log) fclose(keycache_debug_log);    \
-              assert(a); }
-#else
-#define KEYCACHE_DBUG_PRINT(l, m)  DBUG_PRINT(l, m)
-#define KEYCACHE_DBUG_ASSERT(a)    DBUG_ASSERT(a)
-#endif /* defined(KEYCACHE_DEBUG_LOG) && defined(KEYCACHE_DEBUG) */
-
-#if defined(KEYCACHE_DEBUG) || !defined(DBUG_OFF)
-#ifdef THREAD
-static long keycache_thread_id;
-#define KEYCACHE_THREAD_TRACE(l)                                              \
-             KEYCACHE_DBUG_PRINT(l,("|thread %ld",keycache_thread_id))
-
-#define KEYCACHE_THREAD_TRACE_BEGIN(l)                                        \
-            { struct st_my_thread_var *thread_var= my_thread_var;             \
-              keycache_thread_id= thread_var->id;                             \
-              KEYCACHE_DBUG_PRINT(l,("[thread %ld",keycache_thread_id)) }
-
-#define KEYCACHE_THREAD_TRACE_END(l)                                          \
-            KEYCACHE_DBUG_PRINT(l,("]thread %ld",keycache_thread_id))
-#else /* THREAD */
-#define KEYCACHE_THREAD_TRACE(l)        KEYCACHE_DBUG_PRINT(l,(""))
-#define KEYCACHE_THREAD_TRACE_BEGIN(l)  KEYCACHE_DBUG_PRINT(l,(""))
-#define KEYCACHE_THREAD_TRACE_END(l)    KEYCACHE_DBUG_PRINT(l,(""))
-#endif /* THREAD */
-#else
-#define KEYCACHE_THREAD_TRACE_BEGIN(l)
-#define KEYCACHE_THREAD_TRACE_END(l)
-#define KEYCACHE_THREAD_TRACE(l)
-#endif /* defined(KEYCACHE_DEBUG) || !defined(DBUG_OFF) */
-
-#define BLOCK_NUMBER(b)                                                       \
-  ((uint) (((char*)(b)-(char *) keycache->block_root)/sizeof(BLOCK_LINK)))
-#define HASH_LINK_NUMBER(h)                                                   \
-  ((uint) (((char*)(h)-(char *) keycache->hash_link_root)/sizeof(HASH_LINK)))
-
-#if (defined(KEYCACHE_TIMEOUT) && !defined(__WIN__)) || defined(KEYCACHE_DEBUG)
-static int keycache_pthread_cond_wait(pthread_cond_t *cond,
-                                      pthread_mutex_t *mutex);
-#else
-#define  keycache_pthread_cond_wait pthread_cond_wait
-#endif
-
-#if defined(KEYCACHE_DEBUG)
-static int keycache_pthread_mutex_lock(pthread_mutex_t *mutex);
-static void keycache_pthread_mutex_unlock(pthread_mutex_t *mutex);
-static int keycache_pthread_cond_signal(pthread_cond_t *cond);
-#else
-#define keycache_pthread_mutex_lock pthread_mutex_lock
-#define keycache_pthread_mutex_unlock pthread_mutex_unlock
-#define keycache_pthread_cond_signal pthread_cond_signal
-#endif /* defined(KEYCACHE_DEBUG) */
-
-#if !defined(DBUG_OFF)
-#if defined(inline)
-#undef inline
-#endif
-#define inline  /* disabled inline for easier debugging */
-static int fail_block(BLOCK_LINK *block);
-static int fail_hlink(HASH_LINK *hlink);
-static int cache_empty(KEY_CACHE *keycache);
-#endif
-
-static inline uint next_power(uint value)
-{
-  return (uint) my_round_up_to_next_power((uint32) value) << 1;
-}
-
-
-/*
-  Initialize a key cache
-
-  SYNOPSIS
-    init_key_cache()
-    keycache			pointer to a key cache data structure
-    key_cache_block_size	size of blocks to keep cached data
-    use_mem                 	total memory to use for the key cache
-    division_limit		division limit (may be zero)
-    age_threshold		age threshold (may be zero)
-
-  RETURN VALUE
-    number of blocks in the key cache, if successful,
-    0 - otherwise.
-
-  NOTES.
-    if keycache->key_cache_inited != 0 we assume that the key cache
-    is already initialized.  This is for now used by myisamchk, but shouldn't
-    be something that a program should rely on!
-
-    It's assumed that no two threads call this function simultaneously
-    referring to the same key cache handle.
-
-*/
-
-int init_key_cache(KEY_CACHE *keycache, uint key_cache_block_size,
-		   size_t use_mem, uint division_limit,
-		   uint age_threshold)
-{
-  ulong blocks, hash_links;
-  size_t length;
-  int error;
-  DBUG_ENTER("init_key_cache");
-  DBUG_ASSERT(key_cache_block_size >= 512);
-
-  KEYCACHE_DEBUG_OPEN;
-  if (keycache->key_cache_inited && keycache->disk_blocks > 0)
-  {
-    DBUG_PRINT("warning",("key cache already in use"));
-    DBUG_RETURN(0);
-  }
-
-  keycache->global_cache_w_requests= keycache->global_cache_r_requests= 0;
-  keycache->global_cache_read= keycache->global_cache_write= 0;
-  keycache->disk_blocks= -1;
-  if (! keycache->key_cache_inited)
-  {
-    keycache->key_cache_inited= 1;
-    /*
-      Initialize these variables once only.
-      Their value must survive re-initialization during resizing.
-    */
-    keycache->in_resize= 0;
-    keycache->resize_in_flush= 0;
-    keycache->cnt_for_resize_op= 0;
-    keycache->waiting_for_resize_cnt.last_thread= NULL;
-    keycache->in_init= 0;
-    pthread_mutex_init(&keycache->cache_lock, MY_MUTEX_INIT_FAST);
-    keycache->resize_queue.last_thread= NULL;
-    keycache->post_write= NULL;
-  }
-
-  keycache->key_cache_mem_size= use_mem;
-  keycache->key_cache_block_size= key_cache_block_size;
-  DBUG_PRINT("info", ("key_cache_block_size: %u",
-		      key_cache_block_size));
-
-  blocks= (ulong) (use_mem / (sizeof(BLOCK_LINK) + 2 * sizeof(HASH_LINK) +
-                              sizeof(HASH_LINK*) * 5/4 + key_cache_block_size));
-  /* It doesn't make sense to have too few blocks (less than 8) */
-  if (blocks >= 8)
-  {
-    for ( ; ; )
-    {
-      /* Set my_hash_entries to the next bigger 2 power */
-      if ((keycache->hash_entries= next_power(blocks)) < blocks * 5/4)
-        keycache->hash_entries<<= 1;
-      hash_links= 2 * blocks;
-#if defined(MAX_THREADS)
-      if (hash_links < MAX_THREADS + blocks - 1)
-        hash_links= MAX_THREADS + blocks - 1;
-#endif
-      while ((length= (ALIGN_SIZE(blocks * sizeof(BLOCK_LINK)) +
-		       ALIGN_SIZE(hash_links * sizeof(HASH_LINK)) +
-		       ALIGN_SIZE(sizeof(HASH_LINK*) *
-                                  keycache->hash_entries))) +
-	     ((size_t) blocks * keycache->key_cache_block_size) > use_mem)
-        blocks--;
-      /* Allocate memory for cache page buffers */
-      if ((keycache->block_mem=
-	   my_large_malloc((size_t) blocks * keycache->key_cache_block_size,
-			  MYF(0))))
-      {
-        /*
-	  Allocate memory for blocks, hash_links and hash entries;
-	  For each block 2 hash links are allocated
-        */
-        if ((keycache->block_root= (BLOCK_LINK*) my_malloc(length,
-                                                           MYF(0))))
-          break;
-        my_large_free(keycache->block_mem, MYF(0));
-        keycache->block_mem= 0;
-      }
-      if (blocks < 8)
-      {
-        my_errno= ENOMEM;
-        my_error(EE_OUTOFMEMORY, MYF(0), blocks * keycache->key_cache_block_size);
-        goto err;
-      }
-      blocks= blocks / 4*3;
-    }
-    keycache->blocks_unused= blocks;
-    keycache->disk_blocks= (int) blocks;
-    keycache->hash_links= hash_links;
-    keycache->hash_root= (HASH_LINK**) ((char*) keycache->block_root +
-				        ALIGN_SIZE(blocks*sizeof(BLOCK_LINK)));
-    keycache->hash_link_root= (HASH_LINK*) ((char*) keycache->hash_root +
-				            ALIGN_SIZE((sizeof(HASH_LINK*) *
-							keycache->hash_entries)));
-    bzero((uchar*) keycache->block_root,
-	  keycache->disk_blocks * sizeof(BLOCK_LINK));
-    bzero((uchar*) keycache->hash_root,
-          keycache->hash_entries * sizeof(HASH_LINK*));
-    bzero((uchar*) keycache->hash_link_root,
-	  keycache->hash_links * sizeof(HASH_LINK));
-    keycache->hash_links_used= 0;
-    keycache->free_hash_list= NULL;
-    keycache->blocks_used= keycache->blocks_changed= 0;
-
-    keycache->global_blocks_changed= 0;
-    keycache->blocks_available=0;		/* For debugging */
-
-    /* The LRU chain is empty after initialization */
-    keycache->used_last= NULL;
-    keycache->used_ins= NULL;
-    keycache->free_block_list= NULL;
-    keycache->keycache_time= 0;
-    keycache->warm_blocks= 0;
-    keycache->min_warm_blocks= (division_limit ?
-				blocks * division_limit / 100 + 1 :
-				blocks);
-    keycache->age_threshold= (age_threshold ?
-			      blocks * age_threshold / 100 :
-			      blocks);
-
-    keycache->can_be_used= 1;
-
-    keycache->waiting_for_hash_link.last_thread= NULL;
-    keycache->waiting_for_block.last_thread= NULL;
-    DBUG_PRINT("exit",
-	       ("disk_blocks: %d  block_root: %p  hash_entries: %d\
- hash_root: %p  hash_links: %d  hash_link_root: %p",
-		keycache->disk_blocks,  keycache->block_root,
-		keycache->hash_entries, keycache->hash_root,
-		keycache->hash_links,   keycache->hash_link_root));
-    bzero((uchar*) keycache->changed_blocks,
-	  sizeof(keycache->changed_blocks[0]) * CHANGED_BLOCKS_HASH);
-    bzero((uchar*) keycache->file_blocks,
-	  sizeof(keycache->file_blocks[0]) * CHANGED_BLOCKS_HASH);
-  }
-  else
-  {
-    /* key_buffer_size is specified too small. Disable the cache. */
-    keycache->can_be_used= 0;
-  }
-
-  keycache->blocks= keycache->disk_blocks > 0 ? keycache->disk_blocks : 0;
-  DBUG_RETURN((int) keycache->disk_blocks);
-
-err:
-  error= my_errno;
-  keycache->disk_blocks= 0;
-  keycache->blocks=  0;
-  if (keycache->block_mem)
-  {
-    my_large_free((uchar*) keycache->block_mem, MYF(0));
-    keycache->block_mem= NULL;
-  }
-  if (keycache->block_root)
-  {
-    my_free((uchar*) keycache->block_root, MYF(0));
-    keycache->block_root= NULL;
-  }
-  my_errno= error;
-  keycache->can_be_used= 0;
-  DBUG_RETURN(0);
-}
-
-
-/*
-  Resize a key cache
-
-  SYNOPSIS
-    resize_key_cache()
-    keycache     	        pointer to a key cache data structure
-    key_cache_block_size        size of blocks to keep cached data
-    use_mem			total memory to use for the new key cache
-    division_limit		new division limit (if not zero)
-    age_threshold		new age threshold (if not zero)
-
-  RETURN VALUE
-    number of blocks in the key cache, if successful,
-    0 - otherwise.
-
-  NOTES.
-    The function first compares the memory size and the block size parameters
-    with the key cache values.
-
-    If they differ the function free the the memory allocated for the
-    old key cache blocks by calling the end_key_cache function and
-    then rebuilds the key cache with new blocks by calling
-    init_key_cache.
-
-    The function starts the operation only when all other threads
-    performing operations with the key cache let her to proceed
-    (when cnt_for_resize=0).
-*/
-
-int resize_key_cache(KEY_CACHE *keycache, uint key_cache_block_size,
-		     size_t use_mem, uint division_limit,
-		     uint age_threshold)
-{
-  int blocks;
-  DBUG_ENTER("resize_key_cache");
-
-  if (!keycache->key_cache_inited)
-    DBUG_RETURN(keycache->disk_blocks);
-
-  if(key_cache_block_size == keycache->key_cache_block_size &&
-     use_mem == keycache->key_cache_mem_size)
-  {
-    change_key_cache_param(keycache, division_limit, age_threshold);
-    DBUG_RETURN(keycache->disk_blocks);
-  }
-
-  keycache_pthread_mutex_lock(&keycache->cache_lock);
-
-#ifdef THREAD
-  /*
-    We may need to wait for another thread which is doing a resize
-    already. This cannot happen in the MySQL server though. It allows
-    one resizer only. In set_var.cc keycache->in_init is used to block
-    multiple attempts.
-  */
-  while (keycache->in_resize)
-  {
-    /* purecov: begin inspected */
-    wait_on_queue(&keycache->resize_queue, &keycache->cache_lock);
-    /* purecov: end */
-  }
-#endif
-
-  /*
-    Mark the operation in progress. This blocks other threads from doing
-    a resize in parallel. It prohibits new blocks to enter the cache.
-    Read/write requests can bypass the cache during the flush phase.
-  */
-  keycache->in_resize= 1;
-
-  /* Need to flush only if keycache is enabled. */
-  if (keycache->can_be_used)
-  {
-    /* Start the flush phase. */
-    keycache->resize_in_flush= 1;
-
-    if (flush_all_key_blocks(keycache))
-    {
-      /* TODO: if this happens, we should write a warning in the log file ! */
-      keycache->resize_in_flush= 0;
-      blocks= 0;
-      keycache->can_be_used= 0;
-      goto finish;
-    }
-    DBUG_ASSERT(cache_empty(keycache));
-
-    /* End the flush phase. */
-    keycache->resize_in_flush= 0;
-  }
-
-#ifdef THREAD
-  /*
-    Some direct read/write operations (bypassing the cache) may still be
-    unfinished. Wait until they are done. If the key cache can be used,
-    direct I/O is done in increments of key_cache_block_size. That is,
-    every block is checked if it is in the cache. We need to wait for
-    pending I/O before re-initializing the cache, because we may change
-    the block size. Otherwise they could check for blocks at file
-    positions where the new block division has none. We do also want to
-    wait for I/O done when (if) the cache was disabled. It must not
-    run in parallel with normal cache operation.
-  */
-  while (keycache->cnt_for_resize_op)
-    wait_on_queue(&keycache->waiting_for_resize_cnt, &keycache->cache_lock);
-#else
-  KEYCACHE_DBUG_ASSERT(keycache->cnt_for_resize_op == 0);
-#endif
-
-  /*
-    Free old cache structures, allocate new structures, and initialize
-    them. Note that the cache_lock mutex and the resize_queue are left
-    untouched. We do not lose the cache_lock and will release it only at
-    the end of this function.
-  */
-  end_key_cache(keycache, 0);			/* Don't free mutex */
-  /* The following will work even if use_mem is 0 */
-  blocks= init_key_cache(keycache, key_cache_block_size, use_mem,
-			 division_limit, age_threshold);
-
-finish:
-  /*
-    Mark the resize finished. This allows other threads to start a
-    resize or to request new cache blocks.
-  */
-  keycache->in_resize= 0;
-
-  /* Signal waiting threads. */
-  release_whole_queue(&keycache->resize_queue);
-
-  keycache_pthread_mutex_unlock(&keycache->cache_lock);
-  DBUG_RETURN(blocks);
-}
-
-
-/*
-  Increment counter blocking resize key cache operation
-*/
-static inline void inc_counter_for_resize_op(KEY_CACHE *keycache)
-{
-  keycache->cnt_for_resize_op++;
-}
-
-
-/*
-  Decrement counter blocking resize key cache operation;
-  Signal the operation to proceed when counter becomes equal zero
-*/
-static inline void dec_counter_for_resize_op(KEY_CACHE *keycache)
-{
-  if (!--keycache->cnt_for_resize_op)
-    release_whole_queue(&keycache->waiting_for_resize_cnt);
-}
-
-/*
-  Change the key cache parameters
-
-  SYNOPSIS
-    change_key_cache_param()
-    keycache			pointer to a key cache data structure
-    division_limit		new division limit (if not zero)
-    age_threshold		new age threshold (if not zero)
-
-  RETURN VALUE
-    none
-
-  NOTES.
-    Presently the function resets the key cache parameters
-    concerning midpoint insertion strategy - division_limit and
-    age_threshold.
-*/
-
-void change_key_cache_param(KEY_CACHE *keycache, uint division_limit,
-			    uint age_threshold)
-{
-  DBUG_ENTER("change_key_cache_param");
-
-  keycache_pthread_mutex_lock(&keycache->cache_lock);
-  if (division_limit)
-    keycache->min_warm_blocks= (keycache->disk_blocks *
-				division_limit / 100 + 1);
-  if (age_threshold)
-    keycache->age_threshold=   (keycache->disk_blocks *
-				age_threshold / 100);
-  keycache_pthread_mutex_unlock(&keycache->cache_lock);
-  DBUG_VOID_RETURN;
-}
-
-
-/*
-  Remove key_cache from memory
-
-  SYNOPSIS
-    end_key_cache()
-    keycache		key cache handle
-    cleanup		Complete free (Free also mutex for key cache)
-
-  RETURN VALUE
-    none
-*/
-
-void end_key_cache(KEY_CACHE *keycache, my_bool cleanup)
-{
-  DBUG_ENTER("end_key_cache");
-  DBUG_PRINT("enter", ("key_cache: %p", keycache));
-
-  if (!keycache->key_cache_inited)
-    DBUG_VOID_RETURN;
-
-  if (keycache->disk_blocks > 0)
-  {
-    if (keycache->block_mem)
-    {
-      my_large_free((uchar*) keycache->block_mem, MYF(0));
-      keycache->block_mem= NULL;
-      my_free((uchar*) keycache->block_root, MYF(0));
-      keycache->block_root= NULL;
-    }
-    keycache->disk_blocks= -1;
-    /* Reset blocks_changed to be safe if flush_all_key_blocks is called */
-    keycache->blocks_changed= 0;
-  }
-
-  DBUG_PRINT("status", ("used: %lu  changed: %lu  w_requests: %lu  "
-                        "writes: %lu  r_requests: %lu  reads: %lu",
-                        keycache->blocks_used, keycache->global_blocks_changed,
-                        (ulong) keycache->global_cache_w_requests,
-                        (ulong) keycache->global_cache_write,
-                        (ulong) keycache->global_cache_r_requests,
-                        (ulong) keycache->global_cache_read));
-
-  /*
-    Reset these values to be able to detect a disabled key cache.
-    See Bug#44068 (RESTORE can disable the MyISAM Key Cache).
-  */
-  keycache->blocks_used= 0;
-  keycache->blocks_unused= 0;
-
-  if (cleanup)
-  {
-    pthread_mutex_destroy(&keycache->cache_lock);
-    keycache->key_cache_inited= keycache->can_be_used= 0;
-    keycache->post_write= NULL;
-    KEYCACHE_DEBUG_CLOSE;
-  }
-  DBUG_VOID_RETURN;
-} /* end_key_cache */
-
-
-/**
-  Does a my_pwrite() to the file and then calls callback. Arguments are those
-  of my_pwrite() plus the callback and its argument.
-
-  @note The callback is really POST-write; callers depend on this! So always
-  call it after writing to the file, not before.
-
-  @return Operation status
-    @retval 0      ok
-    @retval !=0    write or callback failed
-*/
-
-static inline int key_cache_pwrite(int Filedes, const uchar *Buffer,
-                                   uint Count, my_off_t offset, myf MyFlags,
-                                   KEYCACHE_POST_WRITE_CALLBACK callback,
-                                   void *callback_arg)
-{
-  int ret= my_pwrite(Filedes, Buffer, Count, offset, MyFlags);
-  if (callback)
-    ret|= (*callback)(callback_arg, Buffer, Count, offset);
-  return ret;
-}
-
-
-#ifdef THREAD
-
-/*
-  Link a thread into double-linked queue of waiting threads.
-
-  SYNOPSIS
-    link_into_queue()
-      wqueue              pointer to the queue structure
-      thread              pointer to the thread to be added to the queue
-
-  RETURN VALUE
-    none
-
-  NOTES.
-    Queue is represented by a circular list of the thread structures
-    The list is double-linked of the type (**prev,*next), accessed by
-    a pointer to the last element.
-*/
-
-static void link_into_queue(KEYCACHE_WQUEUE *wqueue,
-                                   struct st_my_thread_var *thread)
-{
-  struct st_my_thread_var *last;
-
-  DBUG_ASSERT(!thread->next && !thread->prev);
-  if (! (last= wqueue->last_thread))
-  {
-    /* Queue is empty */
-    thread->next= thread;
-    thread->prev= &thread->next;
-  }
-  else
-  {
-    thread->prev= last->next->prev;
-    last->next->prev= &thread->next;
-    thread->next= last->next;
-    last->next= thread;
-  }
-  wqueue->last_thread= thread;
-}
-
-/*
-  Unlink a thread from double-linked queue of waiting threads
-
-  SYNOPSIS
-    unlink_from_queue()
-      wqueue              pointer to the queue structure
-      thread              pointer to the thread to be removed from the queue
-
-  RETURN VALUE
-    none
-
-  NOTES.
-    See NOTES for link_into_queue
-*/
-
-static void unlink_from_queue(KEYCACHE_WQUEUE *wqueue,
-                                     struct st_my_thread_var *thread)
-{
-  KEYCACHE_DBUG_PRINT("unlink_from_queue", ("thread %ld", thread->id));
-  DBUG_ASSERT(thread->next && thread->prev);
-  if (thread->next == thread)
-    /* The queue contains only one member */
-    wqueue->last_thread= NULL;
-  else
-  {
-    thread->next->prev= thread->prev;
-    *thread->prev=thread->next;
-    if (wqueue->last_thread == thread)
-      wqueue->last_thread= STRUCT_PTR(struct st_my_thread_var, next,
-                                      thread->prev);
-  }
-  thread->next= NULL;
-#if !defined(DBUG_OFF)
-  /*
-    This makes it easier to see it's not in a chain during debugging.
-    And some DBUG_ASSERT() rely on it.
-  */
-  thread->prev= NULL;
-#endif
-}
-
-
-/*
-  Add a thread to single-linked queue of waiting threads
-
-  SYNOPSIS
-    wait_on_queue()
-      wqueue            Pointer to the queue structure.
-      mutex             Cache_lock to acquire after awake.
-
-  RETURN VALUE
-    none
-
-  NOTES.
-    Queue is represented by a circular list of the thread structures
-    The list is single-linked of the type (*next), accessed by a pointer
-    to the last element.
-
-    The function protects against stray signals by verifying that the
-    current thread is unlinked from the queue when awaking. However,
-    since several threads can wait for the same event, it might be
-    necessary for the caller of the function to check again if the
-    condition for awake is indeed matched.
-*/
-
-static void wait_on_queue(KEYCACHE_WQUEUE *wqueue,
-                          pthread_mutex_t *mutex)
-{
-  struct st_my_thread_var *last;
-  struct st_my_thread_var *thread= my_thread_var;
-
-  /* Add to queue. */
-  DBUG_ASSERT(!thread->next);
-  DBUG_ASSERT(!thread->prev); /* Not required, but must be true anyway. */
-  if (! (last= wqueue->last_thread))
-    thread->next= thread;
-  else
-  {
-    thread->next= last->next;
-    last->next= thread;
-  }
-  wqueue->last_thread= thread;
-
-  /*
-    Wait until thread is removed from queue by the signalling thread.
-    The loop protects against stray signals.
-  */
-  do
-  {
-    KEYCACHE_DBUG_PRINT("wait", ("suspend thread %ld", thread->id));
-    keycache_pthread_cond_wait(&thread->suspend, mutex);
-  }
-  while (thread->next);
-}
-
-
-/*
-  Remove all threads from queue signaling them to proceed
-
-  SYNOPSIS
-    release_whole_queue()
-      wqueue            pointer to the queue structure
-
-  RETURN VALUE
-    none
-
-  NOTES.
-    See notes for wait_on_queue().
-    When removed from the queue each thread is signaled via condition
-    variable thread->suspend.
-*/
-
-static void release_whole_queue(KEYCACHE_WQUEUE *wqueue)
-{
-  struct st_my_thread_var *last;
-  struct st_my_thread_var *next;
-  struct st_my_thread_var *thread;
-
-  /* Queue may be empty. */
-  if (!(last= wqueue->last_thread))
-    return;
-
-  next= last->next;
-  do
-  {
-    thread=next;
-    KEYCACHE_DBUG_PRINT("release_whole_queue: signal",
-                        ("thread %ld", thread->id));
-    /* Signal the thread. */
-    keycache_pthread_cond_signal(&thread->suspend);
-    /* Take thread from queue. */
-    next=thread->next;
-    thread->next= NULL;
-  }
-  while (thread != last);
-
-  /* Now queue is definitely empty. */
-  wqueue->last_thread= NULL;
-}
-
-#endif /* THREAD */
-
-
-/*
-  Unlink a block from the chain of dirty/clean blocks
-*/
-
-static inline void unlink_changed(BLOCK_LINK *block)
-{
-  DBUG_ASSERT(block->prev_changed && *block->prev_changed == block);
-  if (block->next_changed)
-    block->next_changed->prev_changed= block->prev_changed;
-  *block->prev_changed= block->next_changed;
-
-#if !defined(DBUG_OFF)
-  /*
-    This makes it easier to see it's not in a chain during debugging.
-    And some DBUG_ASSERT() rely on it.
-  */
-  block->next_changed= NULL;
-  block->prev_changed= NULL;
-#endif
-}
-
-
-/*
-  Link a block into the chain of dirty/clean blocks
-*/
-
-static inline void link_changed(BLOCK_LINK *block, BLOCK_LINK **phead)
-{
-  DBUG_ASSERT(!block->next_changed);
-  DBUG_ASSERT(!block->prev_changed);
-  block->prev_changed= phead;
-  if ((block->next_changed= *phead))
-    (*phead)->prev_changed= &block->next_changed;
-  *phead= block;
-}
-
-
-/*
-  Link a block in a chain of clean blocks of a file.
-
-  SYNOPSIS
-    link_to_file_list()
-      keycache		Key cache handle
-      block             Block to relink
-      file              File to be linked to
-      unlink            If to unlink first
-
-  DESCRIPTION
-    Unlink a block from whichever chain it is linked in, if it's
-    asked for, and link it to the chain of clean blocks of the
-    specified file.
-
-  NOTE
-    Please do never set/clear BLOCK_CHANGED outside of
-    link_to_file_list() or link_to_changed_list().
-    You would risk to damage correct counting of changed blocks
-    and to find blocks in the wrong hash.
-
-  RETURN
-    void
-*/
-
-static void link_to_file_list(KEY_CACHE *keycache,
-                              BLOCK_LINK *block, int file,
-                              my_bool unlink_block)
-{
-  DBUG_ASSERT(block->status & BLOCK_IN_USE);
-  DBUG_ASSERT(block->hash_link && block->hash_link->block == block);
-  DBUG_ASSERT(block->hash_link->file == file);
-  if (unlink_block)
-    unlink_changed(block);
-  link_changed(block, &keycache->file_blocks[FILE_HASH(file)]);
-  if (block->status & BLOCK_CHANGED)
-  {
-    block->status&= ~BLOCK_CHANGED;
-    keycache->blocks_changed--;
-    keycache->global_blocks_changed--;
-  }
-}
-
-
-/*
-  Re-link a block from the clean chain to the dirty chain of a file.
-
-  SYNOPSIS
-    link_to_changed_list()
-      keycache		key cache handle
-      block             block to relink
-
-  DESCRIPTION
-    Unlink a block from the chain of clean blocks of a file
-    and link it to the chain of dirty blocks of the same file.
-
-  NOTE
-    Please do never set/clear BLOCK_CHANGED outside of
-    link_to_file_list() or link_to_changed_list().
-    You would risk to damage correct counting of changed blocks
-    and to find blocks in the wrong hash.
-
-  RETURN
-    void
-*/
-
-static void link_to_changed_list(KEY_CACHE *keycache,
-                                 BLOCK_LINK *block)
-{
-  DBUG_ASSERT(block->status & BLOCK_IN_USE);
-  DBUG_ASSERT(!(block->status & BLOCK_CHANGED));
-  DBUG_ASSERT(block->hash_link && block->hash_link->block == block);
-
-  unlink_changed(block);
-  link_changed(block,
-               &keycache->changed_blocks[FILE_HASH(block->hash_link->file)]);
-  block->status|=BLOCK_CHANGED;
-  keycache->blocks_changed++;
-  keycache->global_blocks_changed++;
-}
-
-
-/*
-  Link a block to the LRU chain at the beginning or at the end of
-  one of two parts.
-
-  SYNOPSIS
-    link_block()
-      keycache            pointer to a key cache data structure
-      block               pointer to the block to link to the LRU chain
-      hot                 <-> to link the block into the hot subchain
-      at_end              <-> to link the block at the end of the subchain
-
-  RETURN VALUE
-    none
-
-  NOTES.
-    The LRU ring is represented by a circular list of block structures.
-    The list is double-linked of the type (**prev,*next) type.
-    The LRU ring is divided into two parts - hot and warm.
-    There are two pointers to access the last blocks of these two
-    parts. The beginning of the warm part follows right after the
-    end of the hot part.
-    Only blocks of the warm part can be used for eviction.
-    The first block from the beginning of this subchain is always
-    taken for eviction (keycache->last_used->next)
-
-    LRU chain:       +------+   H O T    +------+
-                +----| end  |----...<----| beg  |----+
-                |    +------+last        +------+    |
-                v<-link in latest hot (new end)      |
-                |     link in latest warm (new end)->^
-                |    +------+  W A R M   +------+    |
-                +----| beg  |---->...----| end  |----+
-                     +------+            +------+ins
-                  first for eviction
-
-    It is also possible that the block is selected for eviction and thus
-    not linked in the LRU ring.
-*/
-
-static void link_block(KEY_CACHE *keycache, BLOCK_LINK *block, my_bool hot,
-                       my_bool at_end)
-{
-  BLOCK_LINK *ins;
-  BLOCK_LINK **pins;
-
-  DBUG_ASSERT((block->status & ~BLOCK_CHANGED) == (BLOCK_READ | BLOCK_IN_USE));
-  DBUG_ASSERT(block->hash_link); /*backptr to block NULL from free_block()*/
-  DBUG_ASSERT(!block->requests);
-  DBUG_ASSERT(block->prev_changed && *block->prev_changed == block);
-  DBUG_ASSERT(!block->next_used);
-  DBUG_ASSERT(!block->prev_used);
-#ifdef THREAD
-  if (!hot && keycache->waiting_for_block.last_thread)
-  {
-    /* Signal that in the LRU warm sub-chain an available block has appeared */
-    struct st_my_thread_var *last_thread=
-                               keycache->waiting_for_block.last_thread;
-    struct st_my_thread_var *first_thread= last_thread->next;
-    struct st_my_thread_var *next_thread= first_thread;
-    HASH_LINK *hash_link= (HASH_LINK *) first_thread->opt_info;
-    struct st_my_thread_var *thread;
-    do
-    {
-      thread= next_thread;
-      next_thread= thread->next;
-      /*
-         We notify about the event all threads that ask
-         for the same page as the first thread in the queue
-      */
-      if ((HASH_LINK *) thread->opt_info == hash_link)
-      {
-        KEYCACHE_DBUG_PRINT("link_block: signal", ("thread %ld", thread->id));
-        keycache_pthread_cond_signal(&thread->suspend);
-        unlink_from_queue(&keycache->waiting_for_block, thread);
-        block->requests++;
-      }
-    }
-    while (thread != last_thread);
-    hash_link->block= block;
-    /*
-      NOTE: We assigned the block to the hash_link and signalled the
-      requesting thread(s). But it is possible that other threads runs
-      first. These threads see the hash_link assigned to a block which
-      is assigned to another hash_link and not marked BLOCK_IN_SWITCH.
-      This can be a problem for functions that do not select the block
-      via its hash_link: flush and free. They do only see a block which
-      is in a "normal" state and don't know that it will be evicted soon.
-
-      We cannot set BLOCK_IN_SWITCH here because only one of the
-      requesting threads must handle the eviction. All others must wait
-      for it to complete. If we set the flag here, the threads would not
-      know who is in charge of the eviction. Without the flag, the first
-      thread takes the stick and sets the flag.
-
-      But we need to note in the block that is has been selected for
-      eviction. It must not be freed. The evicting thread will not
-      expect the block in the free list. Before freeing we could also
-      check if block->requests > 1. But I think including another flag
-      in the check of block->status is slightly more efficient and
-      probably easier to read.
-    */
-    block->status|= BLOCK_IN_EVICTION;
-    KEYCACHE_THREAD_TRACE("link_block: after signaling");
-#if defined(KEYCACHE_DEBUG)
-    KEYCACHE_DBUG_PRINT("link_block",
-        ("linked,unlinked block %u  status=%x  #requests=%u  #available=%u",
-         BLOCK_NUMBER(block), block->status,
-         block->requests, keycache->blocks_available));
-#endif
-    return;
-  }
-#else /* THREAD */
-  KEYCACHE_DBUG_ASSERT(! (!hot && keycache->waiting_for_block.last_thread));
-      /* Condition not transformed using DeMorgan, to keep the text identical */
-#endif /* THREAD */
-  pins= hot ? &keycache->used_ins : &keycache->used_last;
-  ins= *pins;
-  if (ins)
-  {
-    ins->next_used->prev_used= &block->next_used;
-    block->next_used= ins->next_used;
-    block->prev_used= &ins->next_used;
-    ins->next_used= block;
-    if (at_end)
-      *pins= block;
-  }
-  else
-  {
-    /* The LRU ring is empty. Let the block point to itself. */
-    keycache->used_last= keycache->used_ins= block->next_used= block;
-    block->prev_used= &block->next_used;
-  }
-  KEYCACHE_THREAD_TRACE("link_block");
-#if defined(KEYCACHE_DEBUG)
-  keycache->blocks_available++;
-  KEYCACHE_DBUG_PRINT("link_block",
-      ("linked block %u:%1u  status=%x  #requests=%u  #available=%u",
-       BLOCK_NUMBER(block), at_end, block->status,
-       block->requests, keycache->blocks_available));
-  KEYCACHE_DBUG_ASSERT((ulong) keycache->blocks_available <=
-                       keycache->blocks_used);
-#endif
-}
-
-
-/*
-  Unlink a block from the LRU chain
-
-  SYNOPSIS
-    unlink_block()
-      keycache            pointer to a key cache data structure
-      block               pointer to the block to unlink from the LRU chain
-
-  RETURN VALUE
-    none
-
-  NOTES.
-    See NOTES for link_block
-*/
-
-static void unlink_block(KEY_CACHE *keycache, BLOCK_LINK *block)
-{
-  DBUG_ASSERT((block->status & ~BLOCK_CHANGED) == (BLOCK_READ | BLOCK_IN_USE));
-  DBUG_ASSERT(block->hash_link); /*backptr to block NULL from free_block()*/
-  DBUG_ASSERT(!block->requests);
-  DBUG_ASSERT(block->prev_changed && *block->prev_changed == block);
-  DBUG_ASSERT(block->next_used && block->prev_used &&
-              (block->next_used->prev_used == &block->next_used) &&
-              (*block->prev_used == block));
-  if (block->next_used == block)
-    /* The list contains only one member */
-    keycache->used_last= keycache->used_ins= NULL;
-  else
-  {
-    block->next_used->prev_used= block->prev_used;
-    *block->prev_used= block->next_used;
-    if (keycache->used_last == block)
-      keycache->used_last= STRUCT_PTR(BLOCK_LINK, next_used, block->prev_used);
-    if (keycache->used_ins == block)
-      keycache->used_ins=STRUCT_PTR(BLOCK_LINK, next_used, block->prev_used);
-  }
-  block->next_used= NULL;
-#if !defined(DBUG_OFF)
-  /*
-    This makes it easier to see it's not in a chain during debugging.
-    And some DBUG_ASSERT() rely on it.
-  */
-  block->prev_used= NULL;
-#endif
-
-  KEYCACHE_THREAD_TRACE("unlink_block");
-#if defined(KEYCACHE_DEBUG)
-  KEYCACHE_DBUG_ASSERT(keycache->blocks_available != 0);
-  keycache->blocks_available--;
-  KEYCACHE_DBUG_PRINT("unlink_block",
-    ("unlinked block %u  status=%x   #requests=%u  #available=%u",
-     BLOCK_NUMBER(block), block->status,
-     block->requests, keycache->blocks_available));
-#endif
-}
-
-
-/*
-  Register requests for a block.
-
-  SYNOPSIS
-    reg_requests()
-      keycache          Pointer to a key cache data structure.
-      block             Pointer to the block to register a request on.
-      count             Number of requests. Always 1.
-
-  NOTE
-    The first request unlinks the block from the LRU ring. This means
-    that it is protected against eveiction.
-
-  RETURN
-    void
-*/
-static void reg_requests(KEY_CACHE *keycache, BLOCK_LINK *block, int count)
-{
-  DBUG_ASSERT(block->status & BLOCK_IN_USE);
-  DBUG_ASSERT(block->hash_link);
-
-  if (!block->requests)
-    unlink_block(keycache, block);
-  block->requests+=count;
-}
-
-
-/*
-  Unregister request for a block
-  linking it to the LRU chain if it's the last request
-
-  SYNOPSIS
-    unreg_request()
-    keycache            pointer to a key cache data structure
-    block               pointer to the block to link to the LRU chain
-    at_end              <-> to link the block at the end of the LRU chain
-
-  RETURN VALUE
-    none
-
-  NOTES.
-    Every linking to the LRU ring decrements by one a special block
-    counter (if it's positive). If the at_end parameter is TRUE the block is
-    added either at the end of warm sub-chain or at the end of hot sub-chain.
-    It is added to the hot subchain if its counter is zero and number of
-    blocks in warm sub-chain is not less than some low limit (determined by
-    the division_limit parameter). Otherwise the block is added to the warm
-    sub-chain. If the at_end parameter is FALSE the block is always added
-    at beginning of the warm sub-chain.
-    Thus a warm block can be promoted to the hot sub-chain when its counter
-    becomes zero for the first time.
-    At the same time  the block at the very beginning of the hot subchain
-    might be moved to the beginning of the warm subchain if it stays untouched
-    for a too long time (this time is determined by parameter age_threshold).
-
-    It is also possible that the block is selected for eviction and thus
-    not linked in the LRU ring.
-*/
-
-static void unreg_request(KEY_CACHE *keycache,
-                          BLOCK_LINK *block, int at_end)
-{
-  DBUG_ASSERT(block->status & (BLOCK_READ | BLOCK_IN_USE));
-  DBUG_ASSERT(block->hash_link); /*backptr to block NULL from free_block()*/
-  DBUG_ASSERT(block->requests);
-  DBUG_ASSERT(block->prev_changed && *block->prev_changed == block);
-  DBUG_ASSERT(!block->next_used);
-  DBUG_ASSERT(!block->prev_used);
-  /*
-    Unregister the request, but do not link erroneous blocks into the
-    LRU ring.
-  */
-  if (!--block->requests && !(block->status & BLOCK_ERROR))
-  {
-    my_bool hot;
-    if (block->hits_left)
-      block->hits_left--;
-    hot= !block->hits_left && at_end &&
-      keycache->warm_blocks > keycache->min_warm_blocks;
-    if (hot)
-    {
-      if (block->temperature == BLOCK_WARM)
-        keycache->warm_blocks--;
-      block->temperature= BLOCK_HOT;
-      KEYCACHE_DBUG_PRINT("unreg_request", ("#warm_blocks: %lu",
-                           keycache->warm_blocks));
-    }
-    link_block(keycache, block, hot, (my_bool)at_end);
-    block->last_hit_time= keycache->keycache_time;
-    keycache->keycache_time++;
-    /*
-      At this place, the block might be in the LRU ring or not. If an
-      evicter was waiting for a block, it was selected for eviction and
-      not linked in the LRU ring.
-    */
-
-    /*
-      Check if we should link a hot block to the warm block sub-chain.
-      It is possible that we select the same block as above. But it can
-      also be another block. In any case a block from the LRU ring is
-      selected. In other words it works even if the above block was
-      selected for eviction and not linked in the LRU ring. Since this
-      happens only if the LRU ring is empty, the block selected below
-      would be NULL and the rest of the function skipped.
-    */
-    block= keycache->used_ins;
-    if (block && keycache->keycache_time - block->last_hit_time >
-	keycache->age_threshold)
-    {
-      unlink_block(keycache, block);
-      link_block(keycache, block, 0, 0);
-      if (block->temperature != BLOCK_WARM)
-      {
-        keycache->warm_blocks++;
-        block->temperature= BLOCK_WARM;
-      }
-      KEYCACHE_DBUG_PRINT("unreg_request", ("#warm_blocks: %lu",
-                           keycache->warm_blocks));
-    }
-  }
-}
-
-/*
-  Remove a reader of the page in block
-*/
-
-static void remove_reader(BLOCK_LINK *block)
-{
-  DBUG_ASSERT(block->status & (BLOCK_READ | BLOCK_IN_USE));
-  DBUG_ASSERT(block->hash_link && block->hash_link->block == block);
-  DBUG_ASSERT(block->prev_changed && *block->prev_changed == block);
-  DBUG_ASSERT(!block->next_used);
-  DBUG_ASSERT(!block->prev_used);
-  DBUG_ASSERT(block->hash_link->requests);
-#ifdef THREAD
-  if (! --block->hash_link->requests && block->condvar)
-    keycache_pthread_cond_signal(block->condvar);
-#else
-  --block->hash_link->requests;
-#endif
-}
-
-
-/*
-  Wait until the last reader of the page in block
-  signals on its termination
-*/
-
-static void wait_for_readers(KEY_CACHE *keycache,
-                             BLOCK_LINK *block)
-{
-#ifdef THREAD
-  struct st_my_thread_var *thread= my_thread_var;
-  DBUG_ASSERT(block->status & (BLOCK_READ | BLOCK_IN_USE));
-  DBUG_ASSERT(!(block->status & (BLOCK_IN_FLUSH | BLOCK_CHANGED)));
-  DBUG_ASSERT(block->hash_link);
-  DBUG_ASSERT(block->hash_link->block == block);
-  /* Linked in file_blocks or changed_blocks hash. */
-  DBUG_ASSERT(block->prev_changed && *block->prev_changed == block);
-  /* Not linked in LRU ring. */
-  DBUG_ASSERT(!block->next_used);
-  DBUG_ASSERT(!block->prev_used);
-  while (block->hash_link->requests)
-  {
-    KEYCACHE_DBUG_PRINT("wait_for_readers: wait",
-                        ("suspend thread %ld  block %u",
-                         thread->id, BLOCK_NUMBER(block)));
-    /* There must be no other waiter. We have no queue here. */
-    DBUG_ASSERT(!block->condvar);
-    block->condvar= &thread->suspend;
-    keycache_pthread_cond_wait(&thread->suspend, &keycache->cache_lock);
-    block->condvar= NULL;
-  }
-#else
-  KEYCACHE_DBUG_ASSERT(block->hash_link->requests == 0);
-#endif
-}
-
-
-/*
-  Add a hash link to a bucket in the hash_table
-*/
-
-static inline void link_hash(HASH_LINK **start, HASH_LINK *hash_link)
-{
-  if (*start)
-    (*start)->prev= &hash_link->next;
-  hash_link->next= *start;
-  hash_link->prev= start;
-  *start= hash_link;
-}
-
-
-/*
-  Remove a hash link from the hash table
-*/
-
-static void unlink_hash(KEY_CACHE *keycache, HASH_LINK *hash_link)
-{
-  KEYCACHE_DBUG_PRINT("unlink_hash", ("fd: %u  pos_ %lu  #requests=%u",
-      (uint) hash_link->file,(ulong) hash_link->diskpos, hash_link->requests));
-  KEYCACHE_DBUG_ASSERT(hash_link->requests == 0);
-  if ((*hash_link->prev= hash_link->next))
-    hash_link->next->prev= hash_link->prev;
-  hash_link->block= NULL;
-#ifdef THREAD
-  if (keycache->waiting_for_hash_link.last_thread)
-  {
-    /* Signal that a free hash link has appeared */
-    struct st_my_thread_var *last_thread=
-                               keycache->waiting_for_hash_link.last_thread;
-    struct st_my_thread_var *first_thread= last_thread->next;
-    struct st_my_thread_var *next_thread= first_thread;
-    KEYCACHE_PAGE *first_page= (KEYCACHE_PAGE *) (first_thread->opt_info);
-    struct st_my_thread_var *thread;
-
-    hash_link->file= first_page->file;
-    hash_link->diskpos= first_page->filepos;
-    do
-    {
-      KEYCACHE_PAGE *page;
-      thread= next_thread;
-      page= (KEYCACHE_PAGE *) thread->opt_info;
-      next_thread= thread->next;
-      /*
-         We notify about the event all threads that ask
-         for the same page as the first thread in the queue
-      */
-      if (page->file == hash_link->file && page->filepos == hash_link->diskpos)
-      {
-        KEYCACHE_DBUG_PRINT("unlink_hash: signal", ("thread %ld", thread->id));
-        keycache_pthread_cond_signal(&thread->suspend);
-        unlink_from_queue(&keycache->waiting_for_hash_link, thread);
-      }
-    }
-    while (thread != last_thread);
-    link_hash(&keycache->hash_root[KEYCACHE_HASH(hash_link->file,
-					         hash_link->diskpos)],
-              hash_link);
-    return;
-  }
-#else /* THREAD */
-  KEYCACHE_DBUG_ASSERT(! (keycache->waiting_for_hash_link.last_thread));
-#endif /* THREAD */
-  hash_link->next= keycache->free_hash_list;
-  keycache->free_hash_list= hash_link;
-}
-
-
-/*
-  Get the hash link for a page
-*/
-
-static HASH_LINK *get_hash_link(KEY_CACHE *keycache,
-                                int file, my_off_t filepos)
-{
-  reg1 HASH_LINK *hash_link, **start;
-#if defined(KEYCACHE_DEBUG)
-  int cnt;
-#endif
-
-  KEYCACHE_DBUG_PRINT("get_hash_link", ("fd: %u  pos: %lu",
-                      (uint) file,(ulong) filepos));
-
-restart:
-  /*
-     Find the bucket in the hash table for the pair (file, filepos);
-     start contains the head of the bucket list,
-     hash_link points to the first member of the list
-  */
-  hash_link= *(start= &keycache->hash_root[KEYCACHE_HASH(file, filepos)]);
-#if defined(KEYCACHE_DEBUG)
-  cnt= 0;
-#endif
-  /* Look for an element for the pair (file, filepos) in the bucket chain */
-  while (hash_link &&
-         (hash_link->diskpos != filepos || hash_link->file != file))
-  {
-    hash_link= hash_link->next;
-#if defined(KEYCACHE_DEBUG)
-    cnt++;
-    if (! (cnt <= keycache->hash_links_used))
-    {
-      int i;
-      for (i=0, hash_link= *start ;
-           i < cnt ; i++, hash_link= hash_link->next)
-      {
-        KEYCACHE_DBUG_PRINT("get_hash_link", ("fd: %u  pos: %lu",
-            (uint) hash_link->file,(ulong) hash_link->diskpos));
-      }
-    }
-    KEYCACHE_DBUG_ASSERT(cnt <= keycache->hash_links_used);
-#endif
-  }
-  if (! hash_link)
-  {
-    /* There is no hash link in the hash table for the pair (file, filepos) */
-    if (keycache->free_hash_list)
-    {
-      hash_link= keycache->free_hash_list;
-      keycache->free_hash_list= hash_link->next;
-    }
-    else if (keycache->hash_links_used < keycache->hash_links)
-    {
-      hash_link= &keycache->hash_link_root[keycache->hash_links_used++];
-    }
-    else
-    {
-#ifdef THREAD
-      /* Wait for a free hash link */
-      struct st_my_thread_var *thread= my_thread_var;
-      KEYCACHE_PAGE page;
-      KEYCACHE_DBUG_PRINT("get_hash_link", ("waiting"));
-      page.file= file;
-      page.filepos= filepos;
-      thread->opt_info= (void *) &page;
-      link_into_queue(&keycache->waiting_for_hash_link, thread);
-      KEYCACHE_DBUG_PRINT("get_hash_link: wait",
-                        ("suspend thread %ld", thread->id));
-      keycache_pthread_cond_wait(&thread->suspend,
-                                 &keycache->cache_lock);
-      thread->opt_info= NULL;
-#else
-      KEYCACHE_DBUG_ASSERT(0);
-#endif
-      goto restart;
-    }
-    hash_link->file= file;
-    hash_link->diskpos= filepos;
-    link_hash(start, hash_link);
-  }
-  /* Register the request for the page */
-  hash_link->requests++;
-
-  return hash_link;
-}
-
-
-/*
-  Get a block for the file page requested by a keycache read/write operation;
-  If the page is not in the cache return a free block, if there is none
-  return the lru block after saving its buffer if the page is dirty.
-
-  SYNOPSIS
-
-    find_key_block()
-      keycache            pointer to a key cache data structure
-      file                handler for the file to read page from
-      filepos             position of the page in the file
-      init_hits_left      how initialize the block counter for the page
-      wrmode              <-> get for writing
-      page_st        out  {PAGE_READ,PAGE_TO_BE_READ,PAGE_WAIT_TO_BE_READ}
-
-  RETURN VALUE
-    Pointer to the found block if successful, 0 - otherwise
-
-  NOTES.
-    For the page from file positioned at filepos the function checks whether
-    the page is in the key cache specified by the first parameter.
-    If this is the case it immediately returns the block.
-    If not, the function first chooses  a block for this page. If there is
-    no not used blocks in the key cache yet, the function takes the block
-    at the very beginning of the warm sub-chain. It saves the page in that
-    block if it's dirty before returning the pointer to it.
-    The function returns in the page_st parameter the following values:
-      PAGE_READ         - if page already in the block,
-      PAGE_TO_BE_READ   - if it is to be read yet by the current thread
-      WAIT_TO_BE_READ   - if it is to be read by another thread
-    If an error occurs THE BLOCK_ERROR bit is set in the block status.
-    It might happen that there are no blocks in LRU chain (in warm part) -
-    all blocks  are unlinked for some read/write operations. Then the function
-    waits until first of this operations links any block back.
-*/
-
-static BLOCK_LINK *find_key_block(KEY_CACHE *keycache,
-                                  File file, my_off_t filepos,
-                                  int init_hits_left,
-                                  int wrmode, int *page_st)
-{
-  HASH_LINK *hash_link;
-  BLOCK_LINK *block;
-  int error= 0;
-  int page_status;
-
-  DBUG_ENTER("find_key_block");
-  KEYCACHE_THREAD_TRACE("find_key_block:begin");
-  DBUG_PRINT("enter", ("fd: %d  pos: %lu  wrmode: %d",
-                       file, (ulong) filepos, wrmode));
-  KEYCACHE_DBUG_PRINT("find_key_block", ("fd: %d  pos: %lu  wrmode: %d",
-                                         file, (ulong) filepos,
-                                         wrmode));
-#if !defined(DBUG_OFF) && defined(EXTRA_DEBUG)
-  DBUG_EXECUTE("check_keycache2",
-               test_key_cache(keycache, "start of find_key_block", 0););
-#endif
-
-restart:
-  /*
-    If the flush phase of a resize operation fails, the cache is left
-    unusable. This will be detected only after "goto restart".
-  */
-  if (!keycache->can_be_used)
-    DBUG_RETURN(0);
-
-  /*
-    Find the hash_link for the requested file block (file, filepos). We
-    do always get a hash_link here. It has registered our request so
-    that no other thread can use it for another file block until we
-    release the request (which is done by remove_reader() usually). The
-    hash_link can have a block assigned to it or not. If there is a
-    block, it may be assigned to this hash_link or not. In cases where a
-    block is evicted from the cache, it is taken from the LRU ring and
-    referenced by the new hash_link. But the block can still be assigned
-    to its old hash_link for some time if it needs to be flushed first,
-    or if there are other threads still reading it.
-
-    Summary:
-      hash_link is always returned.
-      hash_link->block can be:
-      - NULL or
-      - not assigned to this hash_link or
-      - assigned to this hash_link. If assigned, the block can have
-        - invalid data (when freshly assigned) or
-        - valid data. Valid data can be
-          - changed over the file contents (dirty) or
-          - not changed (clean).
-  */
-  hash_link= get_hash_link(keycache, file, filepos);
-  DBUG_ASSERT((hash_link->file == file) && (hash_link->diskpos == filepos));
-
-  page_status= -1;
-  if ((block= hash_link->block) &&
-      block->hash_link == hash_link && (block->status & BLOCK_READ))
-  {
-    /* Assigned block with valid (changed or unchanged) contents. */
-    page_status= PAGE_READ;
-  }
-  /*
-    else (page_status == -1)
-      - block == NULL or
-      - block not assigned to this hash_link or
-      - block assigned but not yet read from file (invalid data).
-  */
-
-  if (keycache->in_resize)
-  {
-    /* This is a request during a resize operation */
-
-    if (!block)
-    {
-      struct st_my_thread_var *thread;
-
-      /*
-        The file block is not in the cache. We don't need it in the
-        cache: we are going to read or write directly to file. Cancel
-        the request. We can simply decrement hash_link->requests because
-        we did not release cache_lock since increasing it. So no other
-        thread can wait for our request to become released.
-      */
-      if (hash_link->requests == 1)
-      {
-        /*
-          We are the only one to request this hash_link (this file/pos).
-          Free the hash_link.
-        */
-        hash_link->requests--;
-        unlink_hash(keycache, hash_link);
-        DBUG_RETURN(0);
-      }
-
-      /*
-        More requests on the hash_link. Someone tries to evict a block
-        for this hash_link (could have started before resizing started).
-        This means that the LRU ring is empty. Otherwise a block could
-        be assigned immediately. Behave like a thread that wants to
-        evict a block for this file/pos. Add to the queue of threads
-        waiting for a block. Wait until there is one assigned.
-
-        Refresh the request on the hash-link so that it cannot be reused
-        for another file/pos.
-      */
-      thread= my_thread_var;
-      thread->opt_info= (void *) hash_link;
-      link_into_queue(&keycache->waiting_for_block, thread);
-      do
-      {
-        KEYCACHE_DBUG_PRINT("find_key_block: wait",
-                            ("suspend thread %ld", thread->id));
-        keycache_pthread_cond_wait(&thread->suspend,
-                                   &keycache->cache_lock);
-      } while (thread->next);
-      thread->opt_info= NULL;
-      /*
-        A block should now be assigned to the hash_link. But it may
-        still need to be evicted. Anyway, we should re-check the
-        situation. page_status must be set correctly.
-      */
-      hash_link->requests--;
-      goto restart;
-    } /* end of if (!block) */
-
-    /*
-      There is a block for this file/pos in the cache. Register a
-      request on it. This unlinks it from the LRU ring (if it is there)
-      and hence protects it against eviction (if not already in
-      eviction). We need this for returning the block to the caller, for
-      calling remove_reader() (for debugging purposes), and for calling
-      free_block(). The only case where we don't need the request is if
-      the block is in eviction. In that case we have to unregister the
-      request later.
-    */
-    reg_requests(keycache, block, 1);
-
-    if (page_status != PAGE_READ)
-    {
-      /*
-        - block not assigned to this hash_link or
-        - block assigned but not yet read from file (invalid data).
-
-        This must be a block in eviction. It will be read soon. We need
-        to wait here until this happened. Otherwise the caller could
-        access a wrong block or a block which is in read. While waiting
-        we cannot lose hash_link nor block. We have registered a request
-        on the hash_link. Everything can happen to the block but changes
-        in the hash_link -> block relationship. In other words:
-        everything can happen to the block but free or another completed
-        eviction.
-
-        Note that we bahave like a secondary requestor here. We just
-        cannot return with PAGE_WAIT_TO_BE_READ. This would work for
-        read requests and writes on dirty blocks that are not in flush
-        only. Waiting here on COND_FOR_REQUESTED works in all
-        situations.
-      */
-      DBUG_ASSERT(((block->hash_link != hash_link) &&
-                   (block->status & (BLOCK_IN_EVICTION | BLOCK_IN_SWITCH))) ||
-                  ((block->hash_link == hash_link) &&
-                   !(block->status & BLOCK_READ)));
-      wait_on_queue(&block->wqueue[COND_FOR_REQUESTED], &keycache->cache_lock);
-      /*
-        Here we can trust that the block has been assigned to this
-        hash_link (block->hash_link == hash_link) and read into the
-        buffer (BLOCK_READ). The worst things possible here are that the
-        block is in free (BLOCK_REASSIGNED). But the block is still
-        assigned to the hash_link. The freeing thread waits until we
-        release our request on the hash_link. The block must not be
-        again in eviction because we registered an request on it before
-        starting to wait.
-      */
-      DBUG_ASSERT(block->hash_link == hash_link);
-      DBUG_ASSERT(block->status & (BLOCK_READ | BLOCK_IN_USE));
-      DBUG_ASSERT(!(block->status & (BLOCK_IN_EVICTION | BLOCK_IN_SWITCH)));
-    }
-    /*
-      The block is in the cache. Assigned to the hash_link. Valid data.
-      Note that in case of page_st == PAGE_READ, the block can be marked
-      for eviction. In any case it can be marked for freeing.
-    */
-
-    if (!wrmode)
-    {
-      /* A reader can just read the block. */
-      *page_st= PAGE_READ;
-      DBUG_ASSERT((hash_link->file == file) &&
-                  (hash_link->diskpos == filepos) &&
-                  (block->hash_link == hash_link));
-      DBUG_RETURN(block);
-    }
-
-    /*
-      This is a writer. No two writers for the same block can exist.
-      This must be assured by locks outside of the key cache.
-    */
-    DBUG_ASSERT(!(block->status & BLOCK_FOR_UPDATE) || fail_block(block));
-
-    while (block->status & BLOCK_IN_FLUSH)
-    {
-      /*
-        Wait until the block is flushed to file. Do not release the
-        request on the hash_link yet to prevent that the block is freed
-        or reassigned while we wait. While we wait, several things can
-        happen to the block, including another flush. But the block
-        cannot be reassigned to another hash_link until we release our
-        request on it. But it can be marked BLOCK_REASSIGNED from free
-        or eviction, while they wait for us to release the hash_link.
-      */
-      wait_on_queue(&block->wqueue[COND_FOR_SAVED], &keycache->cache_lock);
-      /*
-        If the flush phase failed, the resize could have finished while
-        we waited here.
-      */
-      if (!keycache->in_resize)
-      {
-        remove_reader(block);
-        unreg_request(keycache, block, 1);
-        goto restart;
-      }
-      DBUG_ASSERT(block->status & (BLOCK_READ | BLOCK_IN_USE));
-      DBUG_ASSERT(!(block->status & BLOCK_FOR_UPDATE) || fail_block(block));
-      DBUG_ASSERT(block->hash_link == hash_link);
-    }
-
-    if (block->status & BLOCK_CHANGED)
-    {
-      /*
-        We want to write a block with changed contents. If the cache
-        block size is bigger than the callers block size (e.g. MyISAM),
-        the caller may replace part of the block only. Changes of the
-        other part of the block must be preserved. Since the block has
-        not yet been selected for flush, we can still add our changes.
-      */
-      *page_st= PAGE_READ;
-      DBUG_ASSERT((hash_link->file == file) &&
-                  (hash_link->diskpos == filepos) &&
-                  (block->hash_link == hash_link));
-      DBUG_RETURN(block);
-    }
-
-    /*
-      This is a write request for a clean block. We do not want to have
-      new dirty blocks in the cache while resizing. We will free the
-      block and write directly to file. If the block is in eviction or
-      in free, we just let it go.
-
-      Unregister from the hash_link. This must be done before freeing
-      the block. And it must be done if not freeing the block. Because
-      we could have waited above, we need to call remove_reader(). Other
-      threads could wait for us to release our request on the hash_link.
-    */
-    remove_reader(block);
-
-    /* If the block is not in eviction and not in free, we can free it. */
-    if (!(block->status & (BLOCK_IN_EVICTION | BLOCK_IN_SWITCH |
-                           BLOCK_REASSIGNED)))
-    {
-      /*
-        Free block as we are going to write directly to file.
-        Although we have an exlusive lock for the updated key part,
-        the control can be yielded by the current thread as we might
-        have unfinished readers of other key parts in the block
-        buffer. Still we are guaranteed not to have any readers
-        of the key part we are writing into until the block is
-        removed from the cache as we set the BLOCK_REASSIGNED
-        flag (see the code below that handles reading requests).
-      */
-      free_block(keycache, block);
-    }
-    else
-    {
-      /*
-        The block will be evicted/freed soon. Don't touch it in any way.
-        Unregister the request that we registered above.
-      */
-      unreg_request(keycache, block, 1);
-
-      /*
-        The block is still assigned to the hash_link (the file/pos that
-        we are going to write to). Wait until the eviction/free is
-        complete. Otherwise the direct write could complete before all
-        readers are done with the block. So they could read outdated
-        data.
-
-        Since we released our request on the hash_link, it can be reused
-        for another file/pos. Hence we cannot just check for
-        block->hash_link == hash_link. As long as the resize is
-        proceeding the block cannot be reassigned to the same file/pos
-        again. So we can terminate the loop when the block is no longer
-        assigned to this file/pos.
-      */
-      do
-      {
-        wait_on_queue(&block->wqueue[COND_FOR_SAVED],
-                      &keycache->cache_lock);
-        /*
-          If the flush phase failed, the resize could have finished
-          while we waited here.
-        */
-        if (!keycache->in_resize)
-          goto restart;
-      } while (block->hash_link &&
-               (block->hash_link->file == file) &&
-               (block->hash_link->diskpos == filepos));
-    }
-    DBUG_RETURN(0);
-  }
-
-  if (page_status == PAGE_READ &&
-      (block->status & (BLOCK_IN_EVICTION | BLOCK_IN_SWITCH |
-                        BLOCK_REASSIGNED)))
-  {
-    /*
-      This is a request for a block to be removed from cache. The block
-      is assigned to this hash_link and contains valid data, but is
-      marked for eviction or to be freed. Possible reasons why it has
-      not yet been evicted/freed can be a flush before reassignment
-      (BLOCK_IN_SWITCH), readers of the block have not finished yet
-      (BLOCK_REASSIGNED), or the evicting thread did not yet awake after
-      the block has been selected for it (BLOCK_IN_EVICTION).
-    */
-
-    KEYCACHE_DBUG_PRINT("find_key_block",
-                        ("request for old page in block %u "
-                         "wrmode: %d  block->status: %d",
-                         BLOCK_NUMBER(block), wrmode, block->status));
-    /*
-       Only reading requests can proceed until the old dirty page is flushed,
-       all others are to be suspended, then resubmitted
-    */
-    if (!wrmode && !(block->status & BLOCK_REASSIGNED))
-    {
-      /*
-        This is a read request and the block not yet reassigned. We can
-        register our request and proceed. This unlinks the block from
-        the LRU ring and protects it against eviction.
-      */
-      reg_requests(keycache, block, 1);
-    }
-    else
-    {
-      /*
-        Either this is a write request for a block that is in eviction
-        or in free. We must not use it any more. Instead we must evict
-        another block. But we cannot do this before the eviction/free is
-        done. Otherwise we would find the same hash_link + block again
-        and again.
-
-        Or this is a read request for a block in eviction/free that does
-        not require a flush, but waits for readers to finish with the
-        block. We do not read this block to let the eviction/free happen
-        as soon as possible. Again we must wait so that we don't find
-        the same hash_link + block again and again.
-      */
-      DBUG_ASSERT(hash_link->requests);
-      hash_link->requests--;
-      KEYCACHE_DBUG_PRINT("find_key_block",
-                          ("request waiting for old page to be saved"));
-      wait_on_queue(&block->wqueue[COND_FOR_SAVED], &keycache->cache_lock);
-      KEYCACHE_DBUG_PRINT("find_key_block",
-                          ("request for old page resubmitted"));
-      /*
-        The block is no longer assigned to this hash_link.
-        Get another one.
-      */
-      goto restart;
-    }
-  }
-  else
-  {
-    /*
-      This is a request for a new block or for a block not to be removed.
-      Either
-      - block == NULL or
-      - block not assigned to this hash_link or
-      - block assigned but not yet read from file,
-      or
-      - block assigned with valid (changed or unchanged) data and
-      - it will not be reassigned/freed.
-    */
-    if (! block)
-    {
-      /* No block is assigned to the hash_link yet. */
-      if (keycache->blocks_unused)
-      {
-        if (keycache->free_block_list)
-        {
-          /* There is a block in the free list. */
-          block= keycache->free_block_list;
-          keycache->free_block_list= block->next_used;
-          block->next_used= NULL;
-        }
-        else
-        {
-          size_t block_mem_offset;
-          /* There are some never used blocks, take first of them */
-          DBUG_ASSERT(keycache->blocks_used <
-                      (ulong) keycache->disk_blocks);
-          block= &keycache->block_root[keycache->blocks_used];
-          block_mem_offset= 
-           ((size_t) keycache->blocks_used) * keycache->key_cache_block_size;
-          block->buffer= ADD_TO_PTR(keycache->block_mem,
-                                    block_mem_offset,
-                                    uchar*);
-          keycache->blocks_used++;
-          DBUG_ASSERT(!block->next_used);
-        }
-        DBUG_ASSERT(!block->prev_used);
-        DBUG_ASSERT(!block->next_changed);
-        DBUG_ASSERT(!block->prev_changed);
-        DBUG_ASSERT(!block->hash_link);
-        DBUG_ASSERT(!block->status);
-        DBUG_ASSERT(!block->requests);
-        keycache->blocks_unused--;
-        block->status= BLOCK_IN_USE;
-        block->length= 0;
-        block->offset= keycache->key_cache_block_size;
-        block->requests= 1;
-        block->temperature= BLOCK_COLD;
-        block->hits_left= init_hits_left;
-        block->last_hit_time= 0;
-        block->hash_link= hash_link;
-        hash_link->block= block;
-        link_to_file_list(keycache, block, file, 0);
-        page_status= PAGE_TO_BE_READ;
-        KEYCACHE_DBUG_PRINT("find_key_block",
-                            ("got free or never used block %u",
-                             BLOCK_NUMBER(block)));
-      }
-      else
-      {
-	/*
-          There are no free blocks and no never used blocks, use a block
-          from the LRU ring.
-        */
-
-#ifdef THREAD
-        if (! keycache->used_last)
-        {
-          /*
-            The LRU ring is empty. Wait until a new block is added to
-            it. Several threads might wait here for the same hash_link,
-            all of them must get the same block. While waiting for a
-            block, after a block is selected for this hash_link, other
-            threads can run first before this one awakes. During this
-            time interval other threads find this hash_link pointing to
-            the block, which is still assigned to another hash_link. In
-            this case the block is not marked BLOCK_IN_SWITCH yet, but
-            it is marked BLOCK_IN_EVICTION.
-          */
-
-          struct st_my_thread_var *thread= my_thread_var;
-          thread->opt_info= (void *) hash_link;
-          link_into_queue(&keycache->waiting_for_block, thread);
-          do
-          {
-            KEYCACHE_DBUG_PRINT("find_key_block: wait",
-                                ("suspend thread %ld", thread->id));
-            keycache_pthread_cond_wait(&thread->suspend,
-                                       &keycache->cache_lock);
-          }
-          while (thread->next);
-          thread->opt_info= NULL;
-          /* Assert that block has a request registered. */
-          DBUG_ASSERT(hash_link->block->requests);
-          /* Assert that block is not in LRU ring. */
-          DBUG_ASSERT(!hash_link->block->next_used);
-          DBUG_ASSERT(!hash_link->block->prev_used);
-        }
-#else
-        KEYCACHE_DBUG_ASSERT(keycache->used_last);
-#endif
-        /*
-          If we waited above, hash_link->block has been assigned by
-          link_block(). Otherwise it is still NULL. In the latter case
-          we need to grab a block from the LRU ring ourselves.
-        */
-        block= hash_link->block;
-        if (! block)
-        {
-          /* Select the last block from the LRU ring. */
-          block= keycache->used_last->next_used;
-          block->hits_left= init_hits_left;
-          block->last_hit_time= 0;
-          hash_link->block= block;
-          /*
-            Register a request on the block. This unlinks it from the
-            LRU ring and protects it against eviction.
-          */
-          DBUG_ASSERT(!block->requests);
-          reg_requests(keycache, block,1);
-          /*
-            We do not need to set block->status|= BLOCK_IN_EVICTION here
-            because we will set block->status|= BLOCK_IN_SWITCH
-            immediately without releasing the lock in between. This does
-            also support debugging. When looking at the block, one can
-            see if the block has been selected by link_block() after the
-            LRU ring was empty, or if it was grabbed directly from the
-            LRU ring in this branch.
-          */
-        }
-
-        /*
-          If we had to wait above, there is a small chance that another
-          thread grabbed this block for the same file block already. But
-          in most cases the first condition is true.
-        */
-        if (block->hash_link != hash_link &&
-	    ! (block->status & BLOCK_IN_SWITCH) )
-        {
-	  /* this is a primary request for a new page */
-          block->status|= BLOCK_IN_SWITCH;
-
-          KEYCACHE_DBUG_PRINT("find_key_block",
-                        ("got block %u for new page", BLOCK_NUMBER(block)));
-
-          if (block->status & BLOCK_CHANGED)
-          {
-	    /* The block contains a dirty page - push it out of the cache */
-
-            KEYCACHE_DBUG_PRINT("find_key_block", ("block is dirty"));
-            if (block->status & BLOCK_IN_FLUSH)
-            {
-              /*
-                The block is marked for flush. If we do not wait here,
-                it could happen that we write the block, reassign it to
-                another file block, then, before the new owner can read
-                the new file block, the flusher writes the cache block
-                (which still has the old contents) to the new file block!
-              */
-              wait_on_queue(&block->wqueue[COND_FOR_SAVED],
-                            &keycache->cache_lock);
-              /*
-                The block is marked BLOCK_IN_SWITCH. It should be left
-                alone except for reading. No free, no write.
-              */
-              DBUG_ASSERT(block->status & (BLOCK_READ | BLOCK_IN_USE));
-              DBUG_ASSERT(!(block->status & (BLOCK_REASSIGNED |
-                                             BLOCK_CHANGED |
-                                             BLOCK_FOR_UPDATE)));
-            }
-            else
-            {
-              block->status|= BLOCK_IN_FLUSH | BLOCK_IN_FLUSHWRITE;
-              /*
-                BLOCK_IN_EVICTION may be true or not. Other flags must
-                have a fixed value.
-              */
-              DBUG_ASSERT((block->status & ~BLOCK_IN_EVICTION) ==
-                          (BLOCK_READ | BLOCK_IN_SWITCH |
-                           BLOCK_IN_FLUSH | BLOCK_IN_FLUSHWRITE |
-                           BLOCK_CHANGED | BLOCK_IN_USE));
-              DBUG_ASSERT(block->hash_link);
-
-              keycache_pthread_mutex_unlock(&keycache->cache_lock);
-              /*
-                The call is thread safe because only the current
-                thread might change the block->hash_link value
-              */
-              error= key_cache_pwrite(block->hash_link->file,
-                                      block->buffer + block->offset,
-                                      block->length - block->offset,
-                                      block->hash_link->diskpos +
-                                      block->offset,
-                                      MYF(MY_NABP | MY_WAIT_IF_FULL),
-                                      keycache->post_write,
-                                      block->post_write_arg);
-              keycache_pthread_mutex_lock(&keycache->cache_lock);
-
-              /* Block status must not have changed. */
-              DBUG_ASSERT((block->status & ~BLOCK_IN_EVICTION) ==
-                          (BLOCK_READ | BLOCK_IN_SWITCH |
-                           BLOCK_IN_FLUSH | BLOCK_IN_FLUSHWRITE |
-                           BLOCK_CHANGED | BLOCK_IN_USE) || fail_block(block));
-              keycache->global_cache_write++;
-            }
-          }
-
-          block->status|= BLOCK_REASSIGNED;
-          /*
-            The block comes from the LRU ring. It must have a hash_link
-            assigned.
-          */
-          DBUG_ASSERT(block->hash_link);
-          if (block->hash_link)
-          {
-            /*
-              All pending requests for this page must be resubmitted.
-              This must be done before waiting for readers. They could
-              wait for the flush to complete. And we must also do it
-              after the wait. Flushers might try to free the block while
-              we wait. They would wait until the reassignment is
-              complete. Also the block status must reflect the correct
-              situation: The block is not changed nor in flush any more.
-              Note that we must not change the BLOCK_CHANGED flag
-              outside of link_to_file_list() so that it is always in the
-              correct queue and the *blocks_changed counters are
-              correct.
-            */
-            block->status&= ~(BLOCK_IN_FLUSH | BLOCK_IN_FLUSHWRITE);
-            link_to_file_list(keycache, block, block->hash_link->file, 1);
-            release_whole_queue(&block->wqueue[COND_FOR_SAVED]);
-            /*
-              The block is still assigned to its old hash_link.
-	      Wait until all pending read requests
-	      for this page are executed
-	      (we could have avoided this waiting, if we had read
-	      a page in the cache in a sweep, without yielding control)
-            */
-            wait_for_readers(keycache, block);
-            DBUG_ASSERT(block->hash_link && block->hash_link->block == block &&
-                        block->prev_changed);
-            /* The reader must not have been a writer. */
-            DBUG_ASSERT(!(block->status & BLOCK_CHANGED));
-
-            /* Wake flushers that might have found the block in between. */
-            release_whole_queue(&block->wqueue[COND_FOR_SAVED]);
-
-            /* Remove the hash link for the old file block from the hash. */
-            unlink_hash(keycache, block->hash_link);
-
-            /*
-              For sanity checks link_to_file_list() asserts that block
-              and hash_link refer to each other. Hence we need to assign
-              the hash_link first, but then we would not know if it was
-              linked before. Hence we would not know if to unlink it. So
-              unlink it here and call link_to_file_list(..., FALSE).
-            */
-            unlink_changed(block);
-          }
-          block->status= error ? BLOCK_ERROR : BLOCK_IN_USE ;
-          block->length= 0;
-          block->offset= keycache->key_cache_block_size;
-          block->hash_link= hash_link;
-          link_to_file_list(keycache, block, file, 0);
-          page_status= PAGE_TO_BE_READ;
-
-          KEYCACHE_DBUG_ASSERT(block->hash_link->block == block);
-          KEYCACHE_DBUG_ASSERT(hash_link->block->hash_link == hash_link);
-        }
-        else
-        {
-          /*
-            Either (block->hash_link == hash_link),
-	    or     (block->status & BLOCK_IN_SWITCH).
-
-            This is for secondary requests for a new file block only.
-            Either it is already assigned to the new hash_link meanwhile
-            (if we had to wait due to empty LRU), or it is already in
-            eviction by another thread. Since this block has been
-            grabbed from the LRU ring and attached to this hash_link,
-            another thread cannot grab the same block from the LRU ring
-            anymore. If the block is in eviction already, it must become
-            attached to the same hash_link and as such destined for the
-            same file block.
-          */
-          KEYCACHE_DBUG_PRINT("find_key_block",
-                              ("block->hash_link: %p  hash_link: %p  "
-                               "block->status: %u", block->hash_link,
-                               hash_link, block->status ));
-          page_status= (((block->hash_link == hash_link) &&
-                         (block->status & BLOCK_READ)) ?
-                        PAGE_READ : PAGE_WAIT_TO_BE_READ);
-        }
-      }
-    }
-    else
-    {
-      /*
-        Block is not NULL. This hash_link points to a block.
-        Either
-        - block not assigned to this hash_link (yet) or
-        - block assigned but not yet read from file,
-        or
-        - block assigned with valid (changed or unchanged) data and
-        - it will not be reassigned/freed.
-
-        The first condition means hash_link points to a block in
-        eviction. This is not necessarily marked by BLOCK_IN_SWITCH yet.
-        But then it is marked BLOCK_IN_EVICTION. See the NOTE in
-        link_block(). In both cases it is destined for this hash_link
-        and its file block address. When this hash_link got its block
-        address, the block was removed from the LRU ring and cannot be
-        selected for eviction (for another hash_link) again.
-
-        Register a request on the block. This is another protection
-        against eviction.
-      */
-      DBUG_ASSERT(((block->hash_link != hash_link) &&
-                   (block->status & (BLOCK_IN_EVICTION | BLOCK_IN_SWITCH))) ||
-                  ((block->hash_link == hash_link) &&
-                   !(block->status & BLOCK_READ)) ||
-                  ((block->status & BLOCK_READ) &&
-                   !(block->status & (BLOCK_IN_EVICTION | BLOCK_IN_SWITCH))));
-      reg_requests(keycache, block, 1);
-      KEYCACHE_DBUG_PRINT("find_key_block",
-                          ("block->hash_link: %p  hash_link: %p  "
-                           "block->status: %u", block->hash_link,
-                           hash_link, block->status ));
-      page_status= (((block->hash_link == hash_link) &&
-                     (block->status & BLOCK_READ)) ?
-                    PAGE_READ : PAGE_WAIT_TO_BE_READ);
-    }
-  }
-
-  KEYCACHE_DBUG_ASSERT(page_status != -1);
-  /* Same assert basically, but be very sure. */
-  KEYCACHE_DBUG_ASSERT(block);
-  /* Assert that block has a request and is not in LRU ring. */
-  DBUG_ASSERT(block->requests);
-  DBUG_ASSERT(!block->next_used);
-  DBUG_ASSERT(!block->prev_used);
-  /* Assert that we return the correct block. */
-  DBUG_ASSERT((page_status == PAGE_WAIT_TO_BE_READ) ||
-              ((block->hash_link->file == file) &&
-               (block->hash_link->diskpos == filepos)));
-  *page_st=page_status;
-  KEYCACHE_DBUG_PRINT("find_key_block",
-                      ("fd: %d  pos: %lu  block->status: %u  page_status: %d",
-                       file, (ulong) filepos, block->status,
-                       page_status));
-
-#if !defined(DBUG_OFF) && defined(EXTRA_DEBUG)
-  DBUG_EXECUTE("check_keycache2",
-               test_key_cache(keycache, "end of find_key_block",0););
-#endif
-  KEYCACHE_THREAD_TRACE("find_key_block:end");
-  DBUG_RETURN(block);
-}
-
-
-/*
-  Read into a key cache block buffer from disk.
-
-  SYNOPSIS
-
-    read_block()
-      keycache            pointer to a key cache data structure
-      block               block to which buffer the data is to be read
-      read_length         size of data to be read
-      min_length          at least so much data must be read
-      primary             <-> the current thread will read the data
-
-  RETURN VALUE
-    None
-
-  NOTES.
-    The function either reads a page data from file to the block buffer,
-    or waits until another thread reads it. What page to read is determined
-    by a block parameter - reference to a hash link for this page.
-    If an error occurs THE BLOCK_ERROR bit is set in the block status.
-    We do not report error when the size of successfully read
-    portion is less than read_length, but not less than min_length.
-*/
-
-static void read_block(KEY_CACHE *keycache,
-                       BLOCK_LINK *block, uint read_length,
-                       uint min_length, my_bool primary)
-{
-  uint got_length;
-
-  /* On entry cache_lock is locked */
-
-  KEYCACHE_THREAD_TRACE("read_block");
-  if (primary)
-  {
-    /*
-      This code is executed only by threads that submitted primary
-      requests. Until block->status contains BLOCK_READ, all other
-      request for the block become secondary requests. For a primary
-      request the block must be properly initialized.
-    */
-    DBUG_ASSERT(((block->status & ~BLOCK_FOR_UPDATE) == BLOCK_IN_USE) ||
-                fail_block(block));
-    DBUG_ASSERT((block->length == 0) || fail_block(block));
-    DBUG_ASSERT((block->offset == keycache->key_cache_block_size) ||
-                fail_block(block));
-    DBUG_ASSERT((block->requests > 0) || fail_block(block));
-
-    KEYCACHE_DBUG_PRINT("read_block",
-                        ("page to be read by primary request"));
-
-    keycache->global_cache_read++;
-    /* Page is not in buffer yet, is to be read from disk */
-    keycache_pthread_mutex_unlock(&keycache->cache_lock);
-    /*
-      Here other threads may step in and register as secondary readers.
-      They will register in block->wqueue[COND_FOR_REQUESTED].
-    */
-    got_length= my_pread(block->hash_link->file, block->buffer,
-                         read_length, block->hash_link->diskpos, MYF(0));
-    keycache_pthread_mutex_lock(&keycache->cache_lock);
-    /*
-      The block can now have been marked for free (in case of
-      FLUSH_RELEASE). Otherwise the state must be unchanged.
-    */
-    DBUG_ASSERT(((block->status & ~(BLOCK_REASSIGNED |
-                                    BLOCK_FOR_UPDATE)) == BLOCK_IN_USE) ||
-                fail_block(block));
-    DBUG_ASSERT((block->length == 0) || fail_block(block));
-    DBUG_ASSERT((block->offset == keycache->key_cache_block_size) ||
-                fail_block(block));
-    DBUG_ASSERT((block->requests > 0) || fail_block(block));
-
-    if (got_length < min_length)
-      block->status|= BLOCK_ERROR;
-    else
-    {
-      block->status|= BLOCK_READ;
-      block->length= got_length;
-      /*
-        Do not set block->offset here. If this block is marked
-        BLOCK_CHANGED later, we want to flush only the modified part. So
-        only a writer may set block->offset down from
-        keycache->key_cache_block_size.
-      */
-    }
-    KEYCACHE_DBUG_PRINT("read_block",
-                        ("primary request: new page in cache"));
-    /* Signal that all pending requests for this page now can be processed */
-    release_whole_queue(&block->wqueue[COND_FOR_REQUESTED]);
-  }
-  else
-  {
-    /*
-      This code is executed only by threads that submitted secondary
-      requests. At this point it could happen that the cache block is
-      not yet assigned to the hash_link for the requested file block.
-      But at awake from the wait this should be the case. Unfortunately
-      we cannot assert this here because we do not know the hash_link
-      for the requested file block nor the file and position. So we have
-      to assert this in the caller.
-    */
-    KEYCACHE_DBUG_PRINT("read_block",
-                      ("secondary request waiting for new page to be read"));
-    wait_on_queue(&block->wqueue[COND_FOR_REQUESTED], &keycache->cache_lock);
-    KEYCACHE_DBUG_PRINT("read_block",
-                        ("secondary request: new page in cache"));
-  }
-}
-
-
-/*
-  Read a block of data from a cached file into a buffer;
-
-  SYNOPSIS
-
-    key_cache_read()
-      keycache            pointer to a key cache data structure
-      file                handler for the file for the block of data to be read
-      filepos             position of the block of data in the file
-      level               determines the weight of the data
-      buff                buffer to where the data must be placed
-      length              length of the buffer
-      block_length        length of the block in the key cache buffer
-      return_buffer       return pointer to the key cache buffer with the data
-
-  RETURN VALUE
-    Returns address from where the data is placed if sucessful, 0 - otherwise.
-
-  NOTES.
-    The function ensures that a block of data of size length from file
-    positioned at filepos is in the buffers for some key cache blocks.
-    Then the function either copies the data into the buffer buff, or,
-    if return_buffer is TRUE, it just returns the pointer to the key cache
-    buffer with the data.
-    Filepos must be a multiple of 'block_length', but it doesn't
-    have to be a multiple of key_cache_block_size;
-*/
-
-uchar *key_cache_read(KEY_CACHE *keycache,
-                      File file, my_off_t filepos, int level,
-                      uchar *buff, uint length,
-                      uint block_length __attribute__((unused)),
-                      int return_buffer __attribute__((unused)))
-{
-  my_bool locked_and_incremented= FALSE;
-  int error=0;
-  uchar *start= buff;
-  DBUG_ENTER("key_cache_read");
-  DBUG_PRINT("enter", ("fd: %u  pos: %lu  length: %u",
-               (uint) file, (ulong) filepos, length));
-
-  if (keycache->key_cache_inited)
-  {
-    /* Key cache is used */
-    reg1 BLOCK_LINK *block;
-    uint read_length;
-    uint offset;
-    int page_st;
-
-    /*
-      When the key cache is once initialized, we use the cache_lock to
-      reliably distinguish the cases of normal operation, resizing, and
-      disabled cache. We always increment and decrement
-      'cnt_for_resize_op' so that a resizer can wait for pending I/O.
-    */
-    keycache_pthread_mutex_lock(&keycache->cache_lock);
-    /*
-      Cache resizing has two phases: Flushing and re-initializing. In
-      the flush phase read requests are allowed to bypass the cache for
-      blocks not in the cache. find_key_block() returns NULL in this
-      case.
-
-      After the flush phase new I/O requests must wait until the
-      re-initialization is done. The re-initialization can be done only
-      if no I/O request is in progress. The reason is that
-      key_cache_block_size can change. With enabled cache, I/O is done
-      in chunks of key_cache_block_size. Every chunk tries to use a
-      cache block first. If the block size changes in the middle, a
-      block could be missed and old data could be read.
-    */
-    while (keycache->in_resize && !keycache->resize_in_flush)
-      wait_on_queue(&keycache->resize_queue, &keycache->cache_lock);
-    /* Register the I/O for the next resize. */
-    inc_counter_for_resize_op(keycache);
-    locked_and_incremented= TRUE;
-    /* Requested data may not always be aligned to cache blocks. */
-    offset= (uint) (filepos % keycache->key_cache_block_size);
-    /* Read data in key_cache_block_size increments */
-    do
-    {
-      /* Cache could be disabled in a later iteration. */
-      
-      if (!keycache->can_be_used)
-      {
-        KEYCACHE_DBUG_PRINT("key_cache_read", ("keycache cannot be used"));
-        goto no_key_cache;
-      }
-      /* Start reading at the beginning of the cache block. */
-      filepos-= offset;
-      /* Do not read beyond the end of the cache block. */
-      read_length= length;
-      set_if_smaller(read_length, keycache->key_cache_block_size-offset);
-      KEYCACHE_DBUG_ASSERT(read_length > 0);
-
-#ifndef THREAD
-      if (block_length > keycache->key_cache_block_size || offset)
-	return_buffer=0;
-#endif
-
-      /* Request the cache block that matches file/pos. */
-      keycache->global_cache_r_requests++;
-      block=find_key_block(keycache, file, filepos, level, 0, &page_st);
-      if (!block)
-      {
-        /*
-          This happens only for requests submitted during key cache
-          resize. The block is not in the cache and shall not go in.
-          Read directly from file.
-        */
-        keycache->global_cache_read++;
-        keycache_pthread_mutex_unlock(&keycache->cache_lock);
-        error= (my_pread(file, (uchar*) buff, read_length,
-                         filepos + offset, MYF(MY_NABP)) != 0);
-        keycache_pthread_mutex_lock(&keycache->cache_lock);
-        goto next_block;
-      }
-      if (!(block->status & BLOCK_ERROR))
-      {
-        if (page_st != PAGE_READ)
-        {
-          /* The requested page is to be read into the block buffer */
-          read_block(keycache, block,
-                     keycache->key_cache_block_size, read_length+offset,
-                     (my_bool)(page_st == PAGE_TO_BE_READ));
-          /*
-            A secondary request must now have the block assigned to the
-            requested file block. It does not hurt to check it for
-            primary requests too.
-          */
-          DBUG_ASSERT(keycache->can_be_used);
-          DBUG_ASSERT(block->hash_link->file == file);
-          DBUG_ASSERT(block->hash_link->diskpos == filepos);
-          DBUG_ASSERT(block->status & (BLOCK_READ | BLOCK_IN_USE));
-        }
-        else if (block->length < read_length + offset)
-        {
-          /*
-            Impossible if nothing goes wrong:
-            this could only happen if we are using a file with
-            small key blocks and are trying to read outside the file
-          */
-          my_errno= -1;
-          block->status|= BLOCK_ERROR;
-        }
-      }
-
-      /* block status may have added BLOCK_ERROR in the above 'if'. */
-      if (!(block->status & BLOCK_ERROR))
-      {
-#ifndef THREAD
-        if (! return_buffer)
-#endif
-        {
-          DBUG_ASSERT(block->status & (BLOCK_READ | BLOCK_IN_USE));
-#if !defined(SERIALIZED_READ_FROM_CACHE)
-          keycache_pthread_mutex_unlock(&keycache->cache_lock);
-#endif
-
-          /* Copy data from the cache buffer */
-          if (!(read_length & 511))
-            bmove512(buff, block->buffer+offset, read_length);
-          else
-            memcpy(buff, block->buffer+offset, (size_t) read_length);
-
-#if !defined(SERIALIZED_READ_FROM_CACHE)
-          keycache_pthread_mutex_lock(&keycache->cache_lock);
-          DBUG_ASSERT(block->status & (BLOCK_READ | BLOCK_IN_USE));
-#endif
-        }
-      }
-
-      remove_reader(block);
-
-      /* Error injection for coverage testing. */
-      DBUG_EXECUTE_IF("key_cache_read_block_error",
-                      block->status|= BLOCK_ERROR;);
-
-      /* Do not link erroneous blocks into the LRU ring, but free them. */
-      if (!(block->status & BLOCK_ERROR))
-      {
-        /*
-          Link the block into the LRU ring if it's the last submitted
-          request for the block. This enables eviction for the block.
-        */
-        unreg_request(keycache, block, 1);
-      }
-      else
-      {
-        free_block(keycache, block);
-        error= 1;
-        break;
-      }
-
-#ifndef THREAD
-      /* This is only true if we where able to read everything in one block */
-      if (return_buffer)
-	DBUG_RETURN(block->buffer);
-#endif
-  next_block:
-      buff+= read_length;
-      filepos+= read_length+offset;
-      offset= 0;
-
-    } while ((length-= read_length));
-    goto end;
-  }
-  KEYCACHE_DBUG_PRINT("key_cache_read", ("keycache not initialized"));
-
-no_key_cache:
-  /* Key cache is not used */
-
-  keycache->global_cache_r_requests++;
-  keycache->global_cache_read++;
-
-  if (locked_and_incremented)
-    keycache_pthread_mutex_unlock(&keycache->cache_lock);
-  if (my_pread(file, (uchar*) buff, length, filepos, MYF(MY_NABP)))
-    error= 1;
-  if (locked_and_incremented)
-    keycache_pthread_mutex_lock(&keycache->cache_lock);
-
-end:
-  if (locked_and_incremented)
-  {
-    dec_counter_for_resize_op(keycache);
-    keycache_pthread_mutex_unlock(&keycache->cache_lock);
-  }
-  DBUG_PRINT("exit", ("error: %d", error ));
-  DBUG_RETURN(error ? (uchar*) 0 : start);
-}
-
-
-/*
-  Insert a block of file data from a buffer into key cache
-
-  SYNOPSIS
-    key_cache_insert()
-    keycache            pointer to a key cache data structure
-    file                handler for the file to insert data from
-    filepos             position of the block of data in the file to insert
-    level               determines the weight of the data
-    buff                buffer to read data from
-    length              length of the data in the buffer
-
-  NOTES
-    This is used by MyISAM to move all blocks from a index file to the key
-    cache
-
-  RETURN VALUE
-    0 if a success, 1 - otherwise.
-*/
-
-int key_cache_insert(KEY_CACHE *keycache,
-                     File file, my_off_t filepos, int level,
-                     uchar *buff, uint length)
-{
-  int error= 0;
-  DBUG_ENTER("key_cache_insert");
-  DBUG_PRINT("enter", ("fd: %u  pos: %lu  length: %u",
-               (uint) file,(ulong) filepos, length));
-
-  if (keycache->key_cache_inited)
-  {
-    /* Key cache is used */
-    reg1 BLOCK_LINK *block;
-    uint read_length;
-    uint offset;
-    int page_st;
-    my_bool locked_and_incremented= FALSE;
-
-    /*
-      When the keycache is once initialized, we use the cache_lock to
-      reliably distinguish the cases of normal operation, resizing, and
-      disabled cache. We always increment and decrement
-      'cnt_for_resize_op' so that a resizer can wait for pending I/O.
-    */
-    keycache_pthread_mutex_lock(&keycache->cache_lock);
-    /*
-      We do not load index data into a disabled cache nor into an
-      ongoing resize.
-    */
-    if (!keycache->can_be_used || keycache->in_resize)
-	goto no_key_cache;
-    /* Register the pseudo I/O for the next resize. */
-    inc_counter_for_resize_op(keycache);
-    locked_and_incremented= TRUE;
-    /* Loaded data may not always be aligned to cache blocks. */
-    offset= (uint) (filepos % keycache->key_cache_block_size);
-    /* Load data in key_cache_block_size increments. */
-    do
-    {
-      /* Cache could be disabled or resizing in a later iteration. */
-      if (!keycache->can_be_used || keycache->in_resize)
-	goto no_key_cache;
-      /* Start loading at the beginning of the cache block. */
-      filepos-= offset;
-      /* Do not load beyond the end of the cache block. */
-      read_length= length;
-      set_if_smaller(read_length, keycache->key_cache_block_size-offset);
-      KEYCACHE_DBUG_ASSERT(read_length > 0);
-
-      /* The block has been read by the caller already. */
-      keycache->global_cache_read++;
-      /* Request the cache block that matches file/pos. */
-      keycache->global_cache_r_requests++;
-      block= find_key_block(keycache, file, filepos, level, 0, &page_st);
-      if (!block)
-      {
-        /*
-          This happens only for requests submitted during key cache
-          resize. The block is not in the cache and shall not go in.
-          Stop loading index data.
-        */
-        goto no_key_cache;
-      }
-      if (!(block->status & BLOCK_ERROR))
-      {
-        if ((page_st == PAGE_WAIT_TO_BE_READ) ||
-            ((page_st == PAGE_TO_BE_READ) &&
-             (offset || (read_length < keycache->key_cache_block_size))))
-        {
-          /*
-            Either
-
-            this is a secondary request for a block to be read into the
-            cache. The block is in eviction. It is not yet assigned to
-            the requested file block (It does not point to the right
-            hash_link). So we cannot call remove_reader() on the block.
-            And we cannot access the hash_link directly here. We need to
-            wait until the assignment is complete. read_block() executes
-            the correct wait when called with primary == FALSE.
-
-            Or
-
-            this is a primary request for a block to be read into the
-            cache and the supplied data does not fill the whole block.
-
-            This function is called on behalf of a LOAD INDEX INTO CACHE
-            statement, which is a read-only task and allows other
-            readers. It is possible that a parallel running reader tries
-            to access this block. If it needs more data than has been
-            supplied here, it would report an error. To be sure that we
-            have all data in the block that is available in the file, we
-            read the block ourselves.
-
-            Though reading again what the caller did read already is an
-            expensive operation, we need to do this for correctness.
-          */
-          read_block(keycache, block, keycache->key_cache_block_size,
-                     read_length + offset, (page_st == PAGE_TO_BE_READ));
-          /*
-            A secondary request must now have the block assigned to the
-            requested file block. It does not hurt to check it for
-            primary requests too.
-          */
-          DBUG_ASSERT(keycache->can_be_used);
-          DBUG_ASSERT(block->hash_link->file == file);
-          DBUG_ASSERT(block->hash_link->diskpos == filepos);
-          DBUG_ASSERT(block->status & (BLOCK_READ | BLOCK_IN_USE));
-        }
-        else if (page_st == PAGE_TO_BE_READ)
-        {
-          /*
-            This is a new block in the cache. If we come here, we have
-            data for the whole block.
-          */
-          DBUG_ASSERT(block->hash_link->requests);
-          DBUG_ASSERT(block->status & BLOCK_IN_USE);
-          DBUG_ASSERT((page_st == PAGE_TO_BE_READ) ||
-                      (block->status & BLOCK_READ));
-
-#if !defined(SERIALIZED_READ_FROM_CACHE)
-          keycache_pthread_mutex_unlock(&keycache->cache_lock);
-          /*
-            Here other threads may step in and register as secondary readers.
-            They will register in block->wqueue[COND_FOR_REQUESTED].
-          */
-#endif
-
-          /* Copy data from buff */
-          if (!(read_length & 511))
-            bmove512(block->buffer+offset, buff, read_length);
-          else
-            memcpy(block->buffer+offset, buff, (size_t) read_length);
-
-#if !defined(SERIALIZED_READ_FROM_CACHE)
-          keycache_pthread_mutex_lock(&keycache->cache_lock);
-          DBUG_ASSERT(block->status & BLOCK_IN_USE);
-          DBUG_ASSERT((page_st == PAGE_TO_BE_READ) ||
-                      (block->status & BLOCK_READ));
-#endif
-          /*
-            After the data is in the buffer, we can declare the block
-            valid. Now other threads do not need to register as
-            secondary readers any more. They can immediately access the
-            block.
-          */
-          block->status|= BLOCK_READ;
-          block->length= read_length+offset;
-          /*
-            Do not set block->offset here. If this block is marked
-            BLOCK_CHANGED later, we want to flush only the modified part. So
-            only a writer may set block->offset down from
-            keycache->key_cache_block_size.
-          */
-          KEYCACHE_DBUG_PRINT("key_cache_insert",
-                              ("primary request: new page in cache"));
-          /* Signal all pending requests. */
-          release_whole_queue(&block->wqueue[COND_FOR_REQUESTED]);
-        }
-        else
-        {
-          /*
-            page_st == PAGE_READ. The block is in the buffer. All data
-            must already be present. Blocks are always read with all
-            data available on file. Assert that the block does not have
-            less contents than the preloader supplies. If the caller has
-            data beyond block->length, it means that a file write has
-            been done while this block was in cache and not extended
-            with the new data. If the condition is met, we can simply
-            ignore the block.
-          */
-          DBUG_ASSERT((page_st == PAGE_READ) &&
-                      (read_length + offset <= block->length));
-        }
-
-        /*
-          A secondary request must now have the block assigned to the
-          requested file block. It does not hurt to check it for primary
-          requests too.
-        */
-        DBUG_ASSERT(block->hash_link->file == file);
-        DBUG_ASSERT(block->hash_link->diskpos == filepos);
-        DBUG_ASSERT(block->status & (BLOCK_READ | BLOCK_IN_USE));
-      } /* end of if (!(block->status & BLOCK_ERROR)) */
-
-
-      remove_reader(block);
-
-      /* Error injection for coverage testing. */
-      DBUG_EXECUTE_IF("key_cache_insert_block_error",
-                      block->status|= BLOCK_ERROR; errno=EIO;);
-
-      /* Do not link erroneous blocks into the LRU ring, but free them. */
-      if (!(block->status & BLOCK_ERROR))
-      {
-        /*
-          Link the block into the LRU ring if it's the last submitted
-          request for the block. This enables eviction for the block.
-        */
-        unreg_request(keycache, block, 1);
-      }
-      else
-      {
-        free_block(keycache, block);
-        error= 1;
-        break;
-      }
-
-      buff+= read_length;
-      filepos+= read_length+offset;
-      offset= 0;
-
-    } while ((length-= read_length));
-
-  no_key_cache:
-    if (locked_and_incremented)
-      dec_counter_for_resize_op(keycache);
-    keycache_pthread_mutex_unlock(&keycache->cache_lock);
-  }
-  DBUG_RETURN(error);
-}
-
-
-/*
-  Write a buffer into a cached file.
-
-  SYNOPSIS
-
-    key_cache_write()
-      keycache            pointer to a key cache data structure
-      file                handler for the file to write data to
-      filepos             position in the file to write data to
-      level               determines the weight of the data
-      buff                buffer with the data
-      length              length of the buffer
-      dont_write          if is 0 then all dirty pages involved in writing
-                          should have been flushed from key cache
-      post_write_arg      argument which will be passed to key cache's
-                          post_write callback
-
-  RETURN VALUE
-    0 if a success, 1 - otherwise.
-
-  NOTES.
-    The function copies the data of size length from buff into buffers
-    for key cache blocks that are  assigned to contain the portion of
-    the file starting with position filepos.
-    It ensures that this data is flushed to the file if dont_write is FALSE.
-    Filepos must be a multiple of 'block_length', but it doesn't
-    have to be a multiple of key_cache_block_size;
-
-    dont_write is always TRUE in the server (info->lock_type is never F_UNLCK).
-*/
-
-int key_cache_write(KEY_CACHE *keycache,
-                    File file, my_off_t filepos, int level,
-                    uchar *buff, uint length,
-                    uint block_length  __attribute__((unused)),
-                    int dont_write,
-                    void *post_write_arg)
-{
-  my_bool locked_and_incremented= FALSE;
-  int error=0;
-  DBUG_ENTER("key_cache_write");
-  DBUG_PRINT("enter",
-             ("fd: %u  pos: %lu  length: %u  block_length: %u"
-              "  key_block_length: %u",
-              (uint) file, (ulong) filepos, length, block_length,
-              keycache ? keycache->key_cache_block_size : 0));
-
-  if (!dont_write)
-  {
-    /* purecov: begin inspected */
-    /* Not used in the server. */
-    /* Force writing from buff into disk. */
-    keycache->global_cache_w_requests++;
-    keycache->global_cache_write++;
-    if (key_cache_pwrite(file, buff, length, filepos,
-                         MYF(MY_NABP | MY_WAIT_IF_FULL),
-                         keycache->post_write, post_write_arg))
-      DBUG_RETURN(1);
-    /* purecov: end */
-  }
-
-#if !defined(DBUG_OFF) && defined(EXTRA_DEBUG)
-  DBUG_EXECUTE("check_keycache",
-               test_key_cache(keycache, "start of key_cache_write", 1););
-#endif
-
-  if (keycache->key_cache_inited)
-  {
-    /* Key cache is used */
-    reg1 BLOCK_LINK *block;
-    uint read_length;
-    uint offset;
-    int page_st;
-
-    /*
-      When the key cache is once initialized, we use the cache_lock to
-      reliably distinguish the cases of normal operation, resizing, and
-      disabled cache. We always increment and decrement
-      'cnt_for_resize_op' so that a resizer can wait for pending I/O.
-    */
-    keycache_pthread_mutex_lock(&keycache->cache_lock);
-    /*
-      Cache resizing has two phases: Flushing and re-initializing. In
-      the flush phase write requests can modify dirty blocks that are
-      not yet in flush. Otherwise they are allowed to bypass the cache.
-      find_key_block() returns NULL in both cases (clean blocks and
-      non-cached blocks).
-
-      After the flush phase new I/O requests must wait until the
-      re-initialization is done. The re-initialization can be done only
-      if no I/O request is in progress. The reason is that
-      key_cache_block_size can change. With enabled cache I/O is done in
-      chunks of key_cache_block_size. Every chunk tries to use a cache
-      block first. If the block size changes in the middle, a block
-      could be missed and data could be written below a cached block.
-    */
-    while (keycache->in_resize && !keycache->resize_in_flush)
-      wait_on_queue(&keycache->resize_queue, &keycache->cache_lock);
-    /* Register the I/O for the next resize. */
-    inc_counter_for_resize_op(keycache);
-    locked_and_incremented= TRUE;
-    /* Requested data may not always be aligned to cache blocks. */
-    offset= (uint) (filepos % keycache->key_cache_block_size);
-    /* Write data in key_cache_block_size increments. */
-    do
-    {
-      /* Cache could be disabled in a later iteration. */
-      if (!keycache->can_be_used)
-	goto no_key_cache;
-      /* Start writing at the beginning of the cache block. */
-      filepos-= offset;
-      /* Do not write beyond the end of the cache block. */
-      read_length= length;
-      set_if_smaller(read_length, keycache->key_cache_block_size-offset);
-      KEYCACHE_DBUG_ASSERT(read_length > 0);
-
-      /* Request the cache block that matches file/pos. */
-      keycache->global_cache_w_requests++;
-      block= find_key_block(keycache, file, filepos, level, 1, &page_st);
-      if (!block)
-      {
-        /*
-          This happens only for requests submitted during key cache
-          resize. The block is not in the cache and shall not go in.
-          Write directly to file.
-        */
-        if (dont_write)
-        {
-          /* Used in the server. */
-          keycache->global_cache_write++;
-          keycache_pthread_mutex_unlock(&keycache->cache_lock);
-          if (key_cache_pwrite(file, (uchar*) buff, read_length,
-                               filepos + offset,
-                               MYF(MY_NABP | MY_WAIT_IF_FULL),
-                               keycache->post_write, post_write_arg))
-            error=1;
-          keycache_pthread_mutex_lock(&keycache->cache_lock);
-        }
-        goto next_block;
-      }
-      /*
-        Prevent block from flushing and from being selected for to be
-        freed. This must be set when we release the cache_lock.
-        However, we must not set the status of the block before it is
-        assigned to this file/pos.
-      */
-      if (page_st != PAGE_WAIT_TO_BE_READ)
-        block->status|= BLOCK_FOR_UPDATE;
-      /*
-        We must read the file block first if it is not yet in the cache
-        and we do not replace all of its contents.
-
-        In cases where the cache block is big enough to contain (parts
-        of) index blocks of different indexes, our request can be
-        secondary (PAGE_WAIT_TO_BE_READ). In this case another thread is
-        reading the file block. If the read completes after us, it
-        overwrites our new contents with the old contents. So we have to
-        wait for the other thread to complete the read of this block.
-        read_block() takes care for the wait.
-      */
-      if (!(block->status & BLOCK_ERROR) &&
-          ((page_st == PAGE_TO_BE_READ &&
-            (offset || read_length < keycache->key_cache_block_size)) ||
-           (page_st == PAGE_WAIT_TO_BE_READ)))
-      {
-        read_block(keycache, block,
-                   offset + read_length >= keycache->key_cache_block_size?
-                   offset : keycache->key_cache_block_size,
-                   offset, (page_st == PAGE_TO_BE_READ));
-        DBUG_ASSERT(keycache->can_be_used);
-        DBUG_ASSERT(block->status & (BLOCK_READ | BLOCK_IN_USE));
-        /*
-          Prevent block from flushing and from being selected for to be
-          freed. This must be set when we release the cache_lock.
-          Here we set it in case we could not set it above.
-        */
-        block->status|= BLOCK_FOR_UPDATE;
-      }
-      /*
-        The block should always be assigned to the requested file block
-        here. It need not be BLOCK_READ when overwriting the whole block.
-      */
-      DBUG_ASSERT(block->hash_link->file == file);
-      DBUG_ASSERT(block->hash_link->diskpos == filepos);
-      DBUG_ASSERT(block->status & BLOCK_IN_USE);
-      DBUG_ASSERT((page_st == PAGE_TO_BE_READ) || (block->status & BLOCK_READ));
-      /*
-        The block to be written must not be marked BLOCK_REASSIGNED.
-        Otherwise it could be freed in dirty state or reused without
-        another flush during eviction. It must also not be in flush.
-        Otherwise the old contens may have been flushed already and
-        the flusher could clear BLOCK_CHANGED without flushing the
-        new changes again.
-      */
-      DBUG_ASSERT(!(block->status & BLOCK_REASSIGNED));
-
-      while (block->status & BLOCK_IN_FLUSHWRITE)
-      {
-        /*
-          Another thread is flushing the block. It was dirty already.
-          Wait until the block is flushed to file. Otherwise we could
-          modify the buffer contents just while it is written to file.
-          An unpredictable file block contents would be the result.
-          While we wait, several things can happen to the block,
-          including another flush. But the block cannot be reassigned to
-          another hash_link until we release our request on it.
-        */
-        wait_on_queue(&block->wqueue[COND_FOR_SAVED], &keycache->cache_lock);
-        DBUG_ASSERT(keycache->can_be_used);
-        DBUG_ASSERT(block->status & (BLOCK_READ | BLOCK_IN_USE));
-        /* Still must not be marked for free. */
-        DBUG_ASSERT(!(block->status & BLOCK_REASSIGNED));
-        DBUG_ASSERT(block->hash_link && (block->hash_link->block == block));
-      }
-
-      /*
-        We could perhaps release the cache_lock during access of the
-        data like in the other functions. Locks outside of the key cache
-        assure that readers and a writer do not access the same range of
-        data. Parallel accesses should happen only if the cache block
-        contains multiple index block(fragment)s. So different parts of
-        the buffer would be read/written. An attempt to flush during
-        memcpy() is prevented with BLOCK_FOR_UPDATE.
-      */
-      if (!(block->status & BLOCK_ERROR))
-      {
-        block->post_write_arg= post_write_arg;
-#if !defined(SERIALIZED_READ_FROM_CACHE)
-        keycache_pthread_mutex_unlock(&keycache->cache_lock);
-#endif
-        if (!(read_length & 511))
-	  bmove512(block->buffer+offset, buff, read_length);
-        else
-          memcpy(block->buffer+offset, buff, (size_t) read_length);
-
-#if !defined(SERIALIZED_READ_FROM_CACHE)
-        keycache_pthread_mutex_lock(&keycache->cache_lock);
-#endif
-      }
-
-      if (!dont_write)
-      {
-        /* Not used in the server. buff has been written to disk at start. */
-        if ((block->status & BLOCK_CHANGED) &&
-            (!offset && read_length >= keycache->key_cache_block_size))
-             link_to_file_list(keycache, block, block->hash_link->file, 1);
-      }
-      else if (! (block->status & BLOCK_CHANGED))
-        link_to_changed_list(keycache, block);
-      block->status|=BLOCK_READ;
-      /*
-        Allow block to be selected for to be freed. Since it is marked
-        BLOCK_CHANGED too, it won't be selected for to be freed without
-        a flush.
-      */
-      block->status&= ~BLOCK_FOR_UPDATE;
-      set_if_smaller(block->offset, offset);
-      set_if_bigger(block->length, read_length+offset);
-
-      /* Threads may be waiting for the changes to be complete. */
-      release_whole_queue(&block->wqueue[COND_FOR_REQUESTED]);
-
-      /*
-        If only a part of the cache block is to be replaced, and the
-        rest has been read from file, then the cache lock has been
-        released for I/O and it could be possible that another thread
-        wants to evict or free the block and waits for it to be
-        released. So we must not just decrement hash_link->requests, but
-        also wake a waiting thread.
-      */
-      remove_reader(block);
-
-      /* Error injection for coverage testing. */
-      DBUG_EXECUTE_IF("key_cache_write_block_error",
-                      block->status|= BLOCK_ERROR;);
-
-      /* Do not link erroneous blocks into the LRU ring, but free them. */
-      if (!(block->status & BLOCK_ERROR))
-      {
-        /*
-          Link the block into the LRU ring if it's the last submitted
-          request for the block. This enables eviction for the block.
-        */
-        unreg_request(keycache, block, 1);
-      }
-      else
-      {
-        /* Pretend a "clean" block to avoid complications. */
-        block->status&= ~(BLOCK_CHANGED);
-        free_block(keycache, block);
-        error= 1;
-        break;
-      }
-
-    next_block:
-      buff+= read_length;
-      filepos+= read_length+offset;
-      offset= 0;
-
-    } while ((length-= read_length));
-    goto end;
-  }
-
-no_key_cache:
-  /* Key cache is not used */
-  if (dont_write)
-  {
-    /* Used in the server. */
-    keycache->global_cache_w_requests++;
-    keycache->global_cache_write++;
-    if (locked_and_incremented)
-      keycache_pthread_mutex_unlock(&keycache->cache_lock);
-    if (key_cache_pwrite(file, (uchar*) buff, length, filepos,
-                         MYF(MY_NABP | MY_WAIT_IF_FULL),
-                         keycache->post_write, post_write_arg))
-      error=1;
-    if (locked_and_incremented)
-      keycache_pthread_mutex_lock(&keycache->cache_lock);
-  }
-
-end:
-  if (locked_and_incremented)
-  {
-    dec_counter_for_resize_op(keycache);
-    keycache_pthread_mutex_unlock(&keycache->cache_lock);
-  }
-#if !defined(DBUG_OFF) && defined(EXTRA_DEBUG)
-  DBUG_EXECUTE("exec",
-               test_key_cache(keycache, "end of key_cache_write", 1););
-#endif
-  DBUG_RETURN(error);
-}
-
-
-/*
-  Free block.
-
-  SYNOPSIS
-    free_block()
-      keycache          Pointer to a key cache data structure
-      block             Pointer to the block to free
-
-  DESCRIPTION
-    Remove reference to block from hash table.
-    Remove block from the chain of clean blocks.
-    Add block to the free list.
-
-  NOTE
-    Block must not be free (status == 0).
-    Block must not be in free_block_list.
-    Block must not be in the LRU ring.
-    Block must not be in eviction (BLOCK_IN_EVICTION | BLOCK_IN_SWITCH).
-    Block must not be in free (BLOCK_REASSIGNED).
-    Block must not be in flush (BLOCK_IN_FLUSH).
-    Block must not be dirty (BLOCK_CHANGED).
-    Block must not be in changed_blocks (dirty) hash.
-    Block must be in file_blocks (clean) hash.
-    Block must refer to a hash_link.
-    Block must have a request registered on it.
-*/
-
-static void free_block(KEY_CACHE *keycache, BLOCK_LINK *block)
-{
-  KEYCACHE_THREAD_TRACE("free block");
-  KEYCACHE_DBUG_PRINT("free_block",
-                      ("block %u to be freed, hash_link %p  status: %u",
-                       BLOCK_NUMBER(block), block->hash_link,
-                       block->status));
-  /*
-    Assert that the block is not free already. And that it is in a clean
-    state. Note that the block might just be assigned to a hash_link and
-    not yet read (BLOCK_READ may not be set here). In this case a reader
-    is registered in the hash_link and free_block() will wait for it
-    below.
-  */
-  DBUG_ASSERT((block->status & BLOCK_IN_USE) &&
-              !(block->status & (BLOCK_IN_EVICTION | BLOCK_IN_SWITCH |
-                                 BLOCK_REASSIGNED | BLOCK_IN_FLUSH |
-                                 BLOCK_CHANGED | BLOCK_FOR_UPDATE)));
-  /* Assert that the block is in a file_blocks chain. */
-  DBUG_ASSERT(block->prev_changed && *block->prev_changed == block);
-  /* Assert that the block is not in the LRU ring. */
-  DBUG_ASSERT(!block->next_used && !block->prev_used);
-  /*
-    IMHO the below condition (if()) makes no sense. I can't see how it
-    could be possible that free_block() is entered with a NULL hash_link
-    pointer. The only place where it can become NULL is in free_block()
-    (or before its first use ever, but for those blocks free_block() is
-    not called). I don't remove the conditional as it cannot harm, but
-    place an DBUG_ASSERT to confirm my hypothesis. Eventually the
-    condition (if()) can be removed.
-  */
-  DBUG_ASSERT(block->hash_link && block->hash_link->block == block);
-  if (block->hash_link)
-  {
-    /*
-      While waiting for readers to finish, new readers might request the
-      block. But since we set block->status|= BLOCK_REASSIGNED, they
-      will wait on block->wqueue[COND_FOR_SAVED]. They must be signalled
-      later.
-    */
-    block->status|= BLOCK_REASSIGNED;
-    wait_for_readers(keycache, block);
-    /*
-      The block must not have been freed by another thread. Repeat some
-      checks. An additional requirement is that it must be read now
-      (BLOCK_READ).
-    */
-    DBUG_ASSERT(block->hash_link && block->hash_link->block == block);
-    DBUG_ASSERT((block->status & (BLOCK_READ | BLOCK_IN_USE |
-                                  BLOCK_REASSIGNED)) &&
-                !(block->status & (BLOCK_IN_EVICTION | BLOCK_IN_SWITCH |
-                                   BLOCK_IN_FLUSH | BLOCK_CHANGED |
-                                   BLOCK_FOR_UPDATE)));
-    DBUG_ASSERT(block->prev_changed && *block->prev_changed == block);
-    DBUG_ASSERT(!block->prev_used);
-    /*
-      Unset BLOCK_REASSIGNED again. If we hand the block to an evicting
-      thread (through unreg_request() below), other threads must not see
-      this flag. They could become confused.
-    */
-    block->status&= ~BLOCK_REASSIGNED;
-    /*
-      Do not release the hash_link until the block is off all lists.
-      At least not if we hand it over for eviction in unreg_request().
-    */
-  }
-
-  /*
-    Unregister the block request and link the block into the LRU ring.
-    This enables eviction for the block. If the LRU ring was empty and
-    threads are waiting for a block, then the block wil be handed over
-    for eviction immediately. Otherwise we will unlink it from the LRU
-    ring again, without releasing the lock in between. So decrementing
-    the request counter and updating statistics are the only relevant
-    operation in this case. Assert that there are no other requests
-    registered.
-  */
-  DBUG_ASSERT(block->requests == 1);
-  unreg_request(keycache, block, 0);
-  /*
-    Note that even without releasing the cache lock it is possible that
-    the block is immediately selected for eviction by link_block() and
-    thus not added to the LRU ring. In this case we must not touch the
-    block any more.
-  */
-  if (block->status & BLOCK_IN_EVICTION)
-    return;
-
-  /* Error blocks are not put into the LRU ring. */
-  if (!(block->status & BLOCK_ERROR))
-  {
-    /* Here the block must be in the LRU ring. Unlink it again. */
-    DBUG_ASSERT(block->next_used && block->prev_used &&
-                *block->prev_used == block);
-    unlink_block(keycache, block);
-  }
-  if (block->temperature == BLOCK_WARM)
-    keycache->warm_blocks--;
-  block->temperature= BLOCK_COLD;
-
-  /* Remove from file_blocks hash. */
-  unlink_changed(block);
-
-  /* Remove reference to block from hash table. */
-  unlink_hash(keycache, block->hash_link);
-  block->hash_link= NULL;
-
-  block->status= 0;
-  block->length= 0;
-  block->offset= keycache->key_cache_block_size;
-  KEYCACHE_THREAD_TRACE("free block");
-  KEYCACHE_DBUG_PRINT("free_block", ("block is freed"));
-
-  /* Enforced by unlink_changed(), but just to be sure. */
-  DBUG_ASSERT(!block->next_changed && !block->prev_changed);
-  /* Enforced by unlink_block(): not in LRU ring nor in free_block_list. */
-  DBUG_ASSERT(!block->next_used && !block->prev_used);
-  /* Insert the free block in the free list. */
-  block->next_used= keycache->free_block_list;
-  keycache->free_block_list= block;
-  /* Keep track of the number of currently unused blocks. */
-  keycache->blocks_unused++;
-
-  /* All pending requests for this page must be resubmitted. */
-  release_whole_queue(&block->wqueue[COND_FOR_SAVED]);
-}
-
-
-static int cmp_sec_link(BLOCK_LINK **a, BLOCK_LINK **b)
-{
-  return (((*a)->hash_link->diskpos < (*b)->hash_link->diskpos) ? -1 :
-      ((*a)->hash_link->diskpos > (*b)->hash_link->diskpos) ? 1 : 0);
-}
-
-
-/*
-  Flush a portion of changed blocks to disk,
-  free used blocks if requested
-*/
-
-static int flush_cached_blocks(KEY_CACHE *keycache,
-                               File file, BLOCK_LINK **cache,
-                               BLOCK_LINK **end,
-                               enum flush_type type)
-{
-  int error;
-  int last_errno= 0;
-  uint count= (uint) (end-cache);
-
-  /* Don't lock the cache during the flush */
-  keycache_pthread_mutex_unlock(&keycache->cache_lock);
-  /*
-     As all blocks referred in 'cache' are marked by BLOCK_IN_FLUSH
-     we are guarunteed no thread will change them
-  */
-  my_qsort((uchar*) cache, count, sizeof(*cache), (qsort_cmp) cmp_sec_link);
-
-  keycache_pthread_mutex_lock(&keycache->cache_lock);
-  /*
-    Note: Do not break the loop. We have registered a request on every
-    block in 'cache'. These must be unregistered by free_block() or
-    unreg_request().
-  */
-  for ( ; cache != end ; cache++)
-  {
-    BLOCK_LINK *block= *cache;
-
-    KEYCACHE_DBUG_PRINT("flush_cached_blocks",
-                        ("block %u to be flushed", BLOCK_NUMBER(block)));
-    /*
-      If the block contents is going to be changed, we abandon the flush
-      for this block. flush_key_blocks_int() will restart its search and
-      handle the block properly.
-    */
-    if (!(block->status & BLOCK_FOR_UPDATE))
-    {
-      /* Blocks coming here must have a certain status. */
-      DBUG_ASSERT(block->hash_link);
-      DBUG_ASSERT(block->hash_link->block == block);
-      DBUG_ASSERT(block->hash_link->file == file);
-      DBUG_ASSERT((block->status & ~BLOCK_IN_EVICTION) ==
-                  (BLOCK_READ | BLOCK_IN_FLUSH | BLOCK_CHANGED | BLOCK_IN_USE));
-      block->status|= BLOCK_IN_FLUSHWRITE;
-      keycache_pthread_mutex_unlock(&keycache->cache_lock);
-      error= key_cache_pwrite(file,
-                              block->buffer+block->offset,
-                              block->length - block->offset,
-                              block->hash_link->diskpos+ block->offset,
-                              MYF(MY_NABP | MY_WAIT_IF_FULL),
-                              keycache->post_write, block->post_write_arg);
-      keycache_pthread_mutex_lock(&keycache->cache_lock);
-      keycache->global_cache_write++;
-      if (error)
-      {
-        block->status|= BLOCK_ERROR;
-        if (!last_errno)
-          last_errno= errno ? errno : -1;
-      }
-      block->status&= ~BLOCK_IN_FLUSHWRITE;
-      /* Block must not have changed status except BLOCK_FOR_UPDATE. */
-      DBUG_ASSERT(block->hash_link);
-      DBUG_ASSERT(block->hash_link->block == block);
-      DBUG_ASSERT(block->hash_link->file == file);
-      DBUG_ASSERT((block->status & ~(BLOCK_FOR_UPDATE | BLOCK_IN_EVICTION)) ==
-                  (BLOCK_READ | BLOCK_IN_FLUSH | BLOCK_CHANGED | BLOCK_IN_USE));
-      /*
-        Set correct status and link in right queue for free or later use.
-        free_block() must not see BLOCK_CHANGED and it may need to wait
-        for readers of the block. These should not see the block in the
-        wrong hash. If not freeing the block, we need to have it in the
-        right queue anyway.
-      */
-      link_to_file_list(keycache, block, file, 1);
-
-    }
-    block->status&= ~BLOCK_IN_FLUSH;
-    /*
-      Let to proceed for possible waiting requests to write to the block page.
-      It might happen only during an operation to resize the key cache.
-    */
-    release_whole_queue(&block->wqueue[COND_FOR_SAVED]);
-    /* type will never be FLUSH_IGNORE_CHANGED here */
-    if (!(type == FLUSH_KEEP || type == FLUSH_FORCE_WRITE) &&
-        !(block->status & (BLOCK_IN_EVICTION | BLOCK_IN_SWITCH |
-                           BLOCK_FOR_UPDATE)))
-    {
-      /*
-        Note that a request has been registered against the block in
-        flush_key_blocks_int().
-      */
-      free_block(keycache, block);
-    }
-    else
-    {
-      /*
-        Link the block into the LRU ring if it's the last submitted
-        request for the block. This enables eviction for the block.
-        Note that a request has been registered against the block in
-        flush_key_blocks_int().
-      */
-      unreg_request(keycache, block, 1);
-    }
-
-  } /* end of for ( ; cache != end ; cache++) */
-  return last_errno;
-}
-
-
-/*
-  flush all key blocks for a file to disk, but don't do any mutex locks.
-
-  SYNOPSIS
-    flush_key_blocks_int()
-      keycache            pointer to a key cache data structure
-      file                handler for the file to flush to
-      flush_type          type of the flush
-
-  NOTES
-    This function doesn't do any mutex locks because it needs to be called both
-    from flush_key_blocks and flush_all_key_blocks (the later one does the
-    mutex lock in the resize_key_cache() function).
-
-    We do only care about changed blocks that exist when the function is
-    entered. We do not guarantee that all changed blocks of the file are
-    flushed if more blocks change while this function is running.
-
-  RETURN
-    0   ok
-    1  error
-*/
-
-static int flush_key_blocks_int(KEY_CACHE *keycache,
-				File file, enum flush_type type)
-{
-  BLOCK_LINK *cache_buff[FLUSH_CACHE],**cache;
-  int last_errno= 0;
-  int last_errcnt= 0;
-  DBUG_ENTER("flush_key_blocks_int");
-  DBUG_PRINT("enter",("file: %d  blocks_used: %lu  blocks_changed: %lu",
-              file, keycache->blocks_used, keycache->blocks_changed));
-
-#if !defined(DBUG_OFF) && defined(EXTRA_DEBUG)
-  DBUG_EXECUTE("check_keycache",
-               test_key_cache(keycache, "start of flush_key_blocks", 0););
-#endif
-
-  DBUG_ASSERT(type != FLUSH_KEEP_LAZY);
-  cache= cache_buff;
-  if (keycache->disk_blocks > 0 &&
-      (!my_disable_flush_key_blocks || type != FLUSH_KEEP))
-  {
-    /* Key cache exists and flush is not disabled */
-    int error= 0;
-    uint count= FLUSH_CACHE;
-    BLOCK_LINK **pos,**end;
-    BLOCK_LINK *first_in_switch= NULL;
-    BLOCK_LINK *last_in_flush;
-    BLOCK_LINK *last_for_update;
-    BLOCK_LINK *block, *next;
-#if defined(KEYCACHE_DEBUG)
-    uint cnt=0;
-#endif
-
-    if (type != FLUSH_IGNORE_CHANGED)
-    {
-      /*
-         Count how many key blocks we have to cache to be able
-         to flush all dirty pages with minimum seek moves
-      */
-      count= 0;
-      for (block= keycache->changed_blocks[FILE_HASH(file)] ;
-           block ;
-           block= block->next_changed)
-      {
-        if ((block->hash_link->file == file) &&
-            !(block->status & BLOCK_IN_FLUSH))
-        {
-          count++;
-          KEYCACHE_DBUG_ASSERT(count<= keycache->blocks_used);
-        }
-      }
-      /*
-        Allocate a new buffer only if its bigger than the one we have.
-        Assure that we always have some entries for the case that new
-        changed blocks appear while we need to wait for something.
-      */
-      if ((count > FLUSH_CACHE) &&
-          !(cache= (BLOCK_LINK**) my_malloc(sizeof(BLOCK_LINK*)*count,
-                                            MYF(0))))
-        cache= cache_buff;
-      /*
-        After a restart there could be more changed blocks than now.
-        So we should not let count become smaller than the fixed buffer.
-      */
-      if (cache == cache_buff)
-        count= FLUSH_CACHE;
-    }
-
-    /* Retrieve the blocks and write them to a buffer to be flushed */
-restart:
-    last_in_flush= NULL;
-    last_for_update= NULL;
-    end= (pos= cache)+count;
-    for (block= keycache->changed_blocks[FILE_HASH(file)] ;
-         block ;
-         block= next)
-    {
-#if defined(KEYCACHE_DEBUG)
-      cnt++;
-      KEYCACHE_DBUG_ASSERT(cnt <= keycache->blocks_used);
-#endif
-      next= block->next_changed;
-      if (block->hash_link->file == file)
-      {
-        if (!(block->status & (BLOCK_IN_FLUSH | BLOCK_FOR_UPDATE)))
-        {
-          /*
-            Note: The special handling of BLOCK_IN_SWITCH is obsolete
-            since we set BLOCK_IN_FLUSH if the eviction includes a
-            flush. It can be removed in a later version.
-          */
-          if (!(block->status & BLOCK_IN_SWITCH))
-          {
-            /*
-              We care only for the blocks for which flushing was not
-              initiated by another thread and which are not in eviction.
-              Registering a request on the block unlinks it from the LRU
-              ring and protects against eviction.
-            */
-            reg_requests(keycache, block, 1);
-            if (type != FLUSH_IGNORE_CHANGED)
-            {
-              /* It's not a temporary file */
-              if (pos == end)
-              {
-                /*
-                  This should happen relatively seldom. Remove the
-                  request because we won't do anything with the block
-                  but restart and pick it again in the next iteration.
-                */
-                unreg_request(keycache, block, 0);
-                /*
-                  This happens only if there is not enough
-                  memory for the big block
-                */
-                if ((error= flush_cached_blocks(keycache, file, cache,
-                                                end,type)))
-                {
-                  /* Do not loop infinitely trying to flush in vain. */
-                  if ((last_errno == error) && (++last_errcnt > 5))
-                    goto err;
-                  last_errno= error;
-                }
-                /*
-                  Restart the scan as some other thread might have changed
-                  the changed blocks chain: the blocks that were in switch
-                  state before the flush started have to be excluded
-                */
-                goto restart;
-              }
-              /*
-                Mark the block with BLOCK_IN_FLUSH in order not to let
-                other threads to use it for new pages and interfere with
-                our sequence of flushing dirty file pages. We must not
-                set this flag before actually putting the block on the
-                write burst array called 'cache'.
-              */
-              block->status|= BLOCK_IN_FLUSH;
-              /* Add block to the array for a write burst. */
-              *pos++= block;
-            }
-            else
-            {
-              /* It's a temporary file */
-              DBUG_ASSERT(!(block->status & BLOCK_REASSIGNED));
-
-              /*
-                free_block() must not be called with BLOCK_CHANGED. Note
-                that we must not change the BLOCK_CHANGED flag outside of
-                link_to_file_list() so that it is always in the correct
-                queue and the *blocks_changed counters are correct.
-              */
-              link_to_file_list(keycache, block, file, 1);
-              if (!(block->status & (BLOCK_IN_EVICTION | BLOCK_IN_SWITCH)))
-              {
-                /* A request has been registered against the block above. */
-                free_block(keycache, block);
-              }
-              else
-              {
-                /*
-                  Link the block into the LRU ring if it's the last
-                  submitted request for the block. This enables eviction
-                  for the block. A request has been registered against
-                  the block above.
-                */
-                unreg_request(keycache, block, 1);
-              }
-            }
-          }
-          else
-          {
-            /*
-              Link the block into a list of blocks 'in switch'.
-
-              WARNING: Here we introduce a place where a changed block
-              is not in the changed_blocks hash! This is acceptable for
-              a BLOCK_IN_SWITCH. Never try this for another situation.
-              Other parts of the key cache code rely on changed blocks
-              being in the changed_blocks hash.
-            */
-            unlink_changed(block);
-            link_changed(block, &first_in_switch);
-          }
-        }
-        else if (type != FLUSH_KEEP)
-        {
-          /*
-            During the normal flush at end of statement (FLUSH_KEEP) we
-            do not need to ensure that blocks in flush or update by
-            other threads are flushed. They will be flushed by them
-            later. In all other cases we must assure that we do not have
-            any changed block of this file in the cache when this
-            function returns.
-          */
-          if (block->status & BLOCK_IN_FLUSH)
-          {
-            /* Remember the last block found to be in flush. */
-            last_in_flush= block;
-          }
-          else
-          {
-            /* Remember the last block found to be selected for update. */
-            last_for_update= block;
-          }
-        }
-      }
-    }
-    if (pos != cache)
-    {
-      if ((error= flush_cached_blocks(keycache, file, cache, pos, type)))
-      {
-        /* Do not loop inifnitely trying to flush in vain. */
-        if ((last_errno == error) && (++last_errcnt > 5))
-          goto err;
-        last_errno= error;
-      }
-      /*
-        Do not restart here during the normal flush at end of statement
-        (FLUSH_KEEP). We have now flushed at least all blocks that were
-        changed when entering this function. In all other cases we must
-        assure that we do not have any changed block of this file in the
-        cache when this function returns.
-      */
-      if (type != FLUSH_KEEP)
-        goto restart;
-    }
-    if (last_in_flush)
-    {
-      /*
-        There are no blocks to be flushed by this thread, but blocks in
-        flush by other threads. Wait until one of the blocks is flushed.
-        Re-check the condition for last_in_flush. We may have unlocked
-        the cache_lock in flush_cached_blocks(). The state of the block
-        could have changed.
-      */
-      if (last_in_flush->status & BLOCK_IN_FLUSH)
-        wait_on_queue(&last_in_flush->wqueue[COND_FOR_SAVED],
-                      &keycache->cache_lock);
-      /* Be sure not to lose a block. They may be flushed in random order. */
-      goto restart;
-    }
-    if (last_for_update)
-    {
-      /*
-        There are no blocks to be flushed by this thread, but blocks for
-        update by other threads. Wait until one of the blocks is updated.
-        Re-check the condition for last_for_update. We may have unlocked
-        the cache_lock in flush_cached_blocks(). The state of the block
-        could have changed.
-      */
-      if (last_for_update->status & BLOCK_FOR_UPDATE)
-        wait_on_queue(&last_for_update->wqueue[COND_FOR_REQUESTED],
-                      &keycache->cache_lock);
-      /* The block is now changed. Flush it. */
-      goto restart;
-    }
-
-    /*
-      Wait until the list of blocks in switch is empty. The threads that
-      are switching these blocks will relink them to clean file chains
-      while we wait and thus empty the 'first_in_switch' chain.
-    */
-    while (first_in_switch)
-    {
-#if defined(KEYCACHE_DEBUG)
-      cnt= 0;
-#endif
-      wait_on_queue(&first_in_switch->wqueue[COND_FOR_SAVED],
-                    &keycache->cache_lock);
-#if defined(KEYCACHE_DEBUG)
-      cnt++;
-      KEYCACHE_DBUG_ASSERT(cnt <= keycache->blocks_used);
-#endif
-      /*
-        Do not restart here. We have flushed all blocks that were
-        changed when entering this function and were not marked for
-        eviction. Other threads have now flushed all remaining blocks in
-        the course of their eviction.
-      */
-    }
-
-    if (! (type == FLUSH_KEEP || type == FLUSH_FORCE_WRITE))
-    {
-      BLOCK_LINK *last_for_update= NULL;
-      BLOCK_LINK *last_in_switch= NULL;
-      uint total_found= 0;
-      uint found;
-
-      /*
-        Finally free all clean blocks for this file.
-        During resize this may be run by two threads in parallel.
-      */
-      do
-      {
-        found= 0;
-        for (block= keycache->file_blocks[FILE_HASH(file)] ;
-             block ;
-             block= next)
-        {
-          /* Remember the next block. After freeing we cannot get at it. */
-          next= block->next_changed;
-
-          /* Changed blocks cannot appear in the file_blocks hash. */
-          DBUG_ASSERT(!(block->status & BLOCK_CHANGED));
-          if (block->hash_link->file == file)
-          {
-            /* We must skip blocks that will be changed. */
-            if (block->status & BLOCK_FOR_UPDATE)
-            {
-              last_for_update= block;
-              continue;
-            }
-
-            /*
-              We must not free blocks in eviction (BLOCK_IN_EVICTION |
-              BLOCK_IN_SWITCH) or blocks intended to be freed
-              (BLOCK_REASSIGNED).
-            */
-            if (!(block->status & (BLOCK_IN_EVICTION | BLOCK_IN_SWITCH |
-                                   BLOCK_REASSIGNED)))
-            {
-              struct st_hash_link *next_hash_link;
-              my_off_t            next_diskpos;
-              File                next_file;
-              uint                next_status;
-              uint                hash_requests;
-
-              total_found++;
-              found++;
-              KEYCACHE_DBUG_ASSERT(found <= keycache->blocks_used);
-
-              /*
-                Register a request. This unlinks the block from the LRU
-                ring and protects it against eviction. This is required
-                by free_block().
-              */
-              reg_requests(keycache, block, 1);
-
-              /*
-                free_block() may need to wait for readers of the block.
-                This is the moment where the other thread can move the
-                'next' block from the chain. free_block() needs to wait
-                if there are requests for the block pending.
-              */
-              if (next && (hash_requests= block->hash_link->requests))
-              {
-                /* Copy values from the 'next' block and its hash_link. */
-                next_status=    next->status;
-                next_hash_link= next->hash_link;
-                next_diskpos=   next_hash_link->diskpos;
-                next_file=      next_hash_link->file;
-                DBUG_ASSERT(next == next_hash_link->block);
-              }
-
-              free_block(keycache, block);
-              /*
-                If we had to wait and the state of the 'next' block
-                changed, break the inner loop. 'next' may no longer be
-                part of the current chain.
-
-                We do not want to break the loop after every free_block(),
-                not even only after waits. The chain might be quite long
-                and contain blocks for many files. Traversing it again and
-                again to find more blocks for this file could become quite
-                inefficient.
-              */
-              if (next && hash_requests &&
-                  ((next_status    != next->status) ||
-                   (next_hash_link != next->hash_link) ||
-                   (next_file      != next_hash_link->file) ||
-                   (next_diskpos   != next_hash_link->diskpos) ||
-                   (next           != next_hash_link->block)))
-                break;
-            }
-            else
-            {
-              last_in_switch= block;
-            }
-          }
-        } /* end for block in file_blocks */
-      } while (found);
-
-      /*
-        If any clean block has been found, we may have waited for it to
-        become free. In this case it could be possible that another clean
-        block became dirty. This is possible if the write request existed
-        before the flush started (BLOCK_FOR_UPDATE). Re-check the hashes.
-      */
-      if (total_found)
-        goto restart;
-
-      /*
-        To avoid an infinite loop, wait until one of the blocks marked
-        for update is updated.
-      */
-      if (last_for_update)
-      {
-        /* We did not wait. Block must not have changed status. */
-        DBUG_ASSERT(last_for_update->status & BLOCK_FOR_UPDATE);
-        wait_on_queue(&last_for_update->wqueue[COND_FOR_REQUESTED],
-                      &keycache->cache_lock);
-        goto restart;
-      }
-
-      /*
-        To avoid an infinite loop wait until one of the blocks marked
-        for eviction is switched.
-      */
-      if (last_in_switch)
-      {
-        /* We did not wait. Block must not have changed status. */
-        DBUG_ASSERT(last_in_switch->status & (BLOCK_IN_EVICTION |
-                                              BLOCK_IN_SWITCH |
-                                              BLOCK_REASSIGNED));
-        wait_on_queue(&last_in_switch->wqueue[COND_FOR_SAVED],
-                      &keycache->cache_lock);
-        goto restart;
-      }
-
-    } /* if (! (type == FLUSH_KEEP || type == FLUSH_FORCE_WRITE)) */
-
-  } /* if (keycache->disk_blocks > 0 */
-
-#ifndef DBUG_OFF
-  DBUG_EXECUTE("check_keycache",
-               test_key_cache(keycache, "end of flush_key_blocks", 0););
-#endif
-err:
-  if (cache != cache_buff)
-    my_free((uchar*) cache, MYF(0));
-  if (last_errno)
-    errno=last_errno;                /* Return first error */
-  DBUG_RETURN(last_errno != 0);
-}
-
-
-/*
-  Flush all blocks for a file to disk
-
-  SYNOPSIS
-
-    flush_key_blocks()
-      keycache            pointer to a key cache data structure
-      file                handler for the file to flush to
-      flush_type          type of the flush
-
-  RETURN
-    0   ok
-    1  error
-*/
-
-int flush_key_blocks(KEY_CACHE *keycache,
-                     File file, enum flush_type type)
-{
-  int res= 0;
-  DBUG_ENTER("flush_key_blocks");
-  DBUG_PRINT("enter", ("keycache: %p", keycache));
-
-  if (!keycache->key_cache_inited)
-    DBUG_RETURN(0);
-
-  keycache_pthread_mutex_lock(&keycache->cache_lock);
-  /* While waiting for lock, keycache could have been ended. */
-  if (keycache->disk_blocks > 0)
-  {
-    inc_counter_for_resize_op(keycache);
-    res= flush_key_blocks_int(keycache, file, type);
-    dec_counter_for_resize_op(keycache);
-  }
-  keycache_pthread_mutex_unlock(&keycache->cache_lock);
-  DBUG_RETURN(res);
-}
-
-
-/*
-  Flush all blocks in the key cache to disk.
-
-  SYNOPSIS
-    flush_all_key_blocks()
-      keycache                  pointer to key cache root structure
-
-  DESCRIPTION
-
-    Flushing of the whole key cache is done in two phases.
-
-    1. Flush all changed blocks, waiting for them if necessary. Loop
-    until there is no changed block left in the cache.
-
-    2. Free all clean blocks. Normally this means free all blocks. The
-    changed blocks were flushed in phase 1 and became clean. However we
-    may need to wait for blocks that are read by other threads. While we
-    wait, a clean block could become changed if that operation started
-    before the resize operation started. To be safe we must restart at
-    phase 1.
-
-    When we can run through the changed_blocks and file_blocks hashes
-    without finding a block any more, then we are done.
-
-    Note that we hold keycache->cache_lock all the time unless we need
-    to wait for something.
-
-  RETURN
-    0           OK
-    != 0        Error
-*/
-
-static int flush_all_key_blocks(KEY_CACHE *keycache)
-{
-  BLOCK_LINK    *block;
-  uint          total_found;
-  uint          found;
-  uint          idx;
-  DBUG_ENTER("flush_all_key_blocks");
-
-  do
-  {
-    safe_mutex_assert_owner(&keycache->cache_lock);
-    total_found= 0;
-
-    /*
-      Phase1: Flush all changed blocks, waiting for them if necessary.
-      Loop until there is no changed block left in the cache.
-    */
-    do
-    {
-      found= 0;
-      /* Step over the whole changed_blocks hash array. */
-      for (idx= 0; idx < CHANGED_BLOCKS_HASH; idx++)
-      {
-        /*
-          If an array element is non-empty, use the first block from its
-          chain to find a file for flush. All changed blocks for this
-          file are flushed. So the same block will not appear at this
-          place again with the next iteration. New writes for blocks are
-          not accepted during the flush. If multiple files share the
-          same hash bucket, one of them will be flushed per iteration
-          of the outer loop of phase 1.
-        */
-        if ((block= keycache->changed_blocks[idx]))
-        {
-          found++;
-          /*
-            Flush dirty blocks but do not free them yet. They can be used
-            for reading until all other blocks are flushed too.
-          */
-          if (flush_key_blocks_int(keycache, block->hash_link->file,
-                                   FLUSH_FORCE_WRITE))
-            DBUG_RETURN(1);
-        }
-      }
-
-    } while (found);
-
-    /*
-      Phase 2: Free all clean blocks. Normally this means free all
-      blocks. The changed blocks were flushed in phase 1 and became
-      clean. However we may need to wait for blocks that are read by
-      other threads. While we wait, a clean block could become changed
-      if that operation started before the resize operation started. To
-      be safe we must restart at phase 1.
-    */
-    do
-    {
-      found= 0;
-      /* Step over the whole file_blocks hash array. */
-      for (idx= 0; idx < CHANGED_BLOCKS_HASH; idx++)
-      {
-        /*
-          If an array element is non-empty, use the first block from its
-          chain to find a file for flush. All blocks for this file are
-          freed. So the same block will not appear at this place again
-          with the next iteration. If multiple files share the
-          same hash bucket, one of them will be flushed per iteration
-          of the outer loop of phase 2.
-        */
-        if ((block= keycache->file_blocks[idx]))
-        {
-          total_found++;
-          found++;
-          if (flush_key_blocks_int(keycache, block->hash_link->file,
-                                   FLUSH_RELEASE))
-            DBUG_RETURN(1);
-        }
-      }
-
-    } while (found);
-
-    /*
-      If any clean block has been found, we may have waited for it to
-      become free. In this case it could be possible that another clean
-      block became dirty. This is possible if the write request existed
-      before the resize started (BLOCK_FOR_UPDATE). Re-check the hashes.
-    */
-  } while (total_found);
-
-#ifndef DBUG_OFF
-  /* Now there should not exist any block any more. */
-  for (idx= 0; idx < CHANGED_BLOCKS_HASH; idx++)
-  {
-    DBUG_ASSERT(!keycache->changed_blocks[idx]);
-    DBUG_ASSERT(!keycache->file_blocks[idx]);
-  }
-#endif
-
-  DBUG_RETURN(0);
-}
-
-
-/*
-  Reset the counters of a key cache.
-
-  SYNOPSIS
-    reset_key_cache_counters()
-    name       the name of a key cache
-    key_cache  pointer to the key kache to be reset
-
-  DESCRIPTION
-   This procedure is used by process_key_caches() to reset the counters of all
-   currently used key caches, both the default one and the named ones.
-
-  RETURN
-    0 on success (always because it can't fail)
-*/
-
-int reset_key_cache_counters(const char *name __attribute__((unused)),
-                             KEY_CACHE *key_cache)
-{
-  DBUG_ENTER("reset_key_cache_counters");
-  if (!key_cache->key_cache_inited)
-  {
-    DBUG_PRINT("info", ("Key cache %s not initialized.", name));
-    DBUG_RETURN(0);
-  }
-  DBUG_PRINT("info", ("Resetting counters for key cache %s.", name));
-
-  key_cache->global_blocks_changed= 0;   /* Key_blocks_not_flushed */
-  key_cache->global_cache_r_requests= 0; /* Key_read_requests */
-  key_cache->global_cache_read= 0;       /* Key_reads */
-  key_cache->global_cache_w_requests= 0; /* Key_write_requests */
-  key_cache->global_cache_write= 0;      /* Key_writes */
-  DBUG_RETURN(0);
-}
-
-
-#ifndef DBUG_OFF
-/*
-  Test if disk-cache is ok
-*/
-static void test_key_cache(KEY_CACHE *keycache __attribute__((unused)),
-                           const char *where __attribute__((unused)),
-                           my_bool lock __attribute__((unused)))
-{
-  /* TODO */
-}
-#endif
-
-#if defined(KEYCACHE_TIMEOUT)
-
-#define KEYCACHE_DUMP_FILE  "keycache_dump.txt"
-#define MAX_QUEUE_LEN  100
-
-
-static void keycache_dump(KEY_CACHE *keycache)
-{
-  FILE *keycache_dump_file=fopen(KEYCACHE_DUMP_FILE, "w");
-  struct st_my_thread_var *last;
-  struct st_my_thread_var *thread;
-  BLOCK_LINK *block;
-  HASH_LINK *hash_link;
-  KEYCACHE_PAGE *page;
-  uint i;
-
-  fprintf(keycache_dump_file, "thread:%u\n", thread->id);
-
-  i=0;
-  thread=last=waiting_for_hash_link.last_thread;
-  fprintf(keycache_dump_file, "queue of threads waiting for hash link\n");
-  if (thread)
-    do
-    {
-      thread=thread->next;
-      page= (KEYCACHE_PAGE *) thread->opt_info;
-      fprintf(keycache_dump_file,
-              "thread:%u, (file,filepos)=(%u,%lu)\n",
-              thread->id,(uint) page->file,(ulong) page->filepos);
-      if (++i == MAX_QUEUE_LEN)
-        break;
-    }
-    while (thread != last);
-
-  i=0;
-  thread=last=waiting_for_block.last_thread;
-  fprintf(keycache_dump_file, "queue of threads waiting for block\n");
-  if (thread)
-    do
-    {
-      thread=thread->next;
-      hash_link= (HASH_LINK *) thread->opt_info;
-      fprintf(keycache_dump_file,
-        "thread:%u hash_link:%u (file,filepos)=(%u,%lu)\n",
-        thread->id, (uint) HASH_LINK_NUMBER(hash_link),
-        (uint) hash_link->file,(ulong) hash_link->diskpos);
-      if (++i == MAX_QUEUE_LEN)
-        break;
-    }
-    while (thread != last);
-
-  for (i=0 ; i< keycache->blocks_used ; i++)
-  {
-    int j;
-    block= &keycache->block_root[i];
-    hash_link= block->hash_link;
-    fprintf(keycache_dump_file,
-            "block:%u hash_link:%d status:%x #requests=%u waiting_for_readers:%d\n",
-            i, (int) (hash_link ? HASH_LINK_NUMBER(hash_link) : -1),
-            block->status, block->requests, block->condvar ? 1 : 0);
-    for (j=0 ; j < 2; j++)
-    {
-      KEYCACHE_WQUEUE *wqueue=&block->wqueue[j];
-      thread= last= wqueue->last_thread;
-      fprintf(keycache_dump_file, "queue #%d\n", j);
-      if (thread)
-      {
-        do
-        {
-          thread=thread->next;
-          fprintf(keycache_dump_file,
-                  "thread:%u\n", thread->id);
-          if (++i == MAX_QUEUE_LEN)
-            break;
-        }
-        while (thread != last);
-      }
-    }
-  }
-  fprintf(keycache_dump_file, "LRU chain:");
-  block= keycache= used_last;
-  if (block)
-  {
-    do
-    {
-      block= block->next_used;
-      fprintf(keycache_dump_file,
-              "block:%u, ", BLOCK_NUMBER(block));
-    }
-    while (block != keycache->used_last);
-  }
-  fprintf(keycache_dump_file, "\n");
-
-  fclose(keycache_dump_file);
-}
-
-#endif /* defined(KEYCACHE_TIMEOUT) */
-
-#if defined(KEYCACHE_TIMEOUT) && !defined(__WIN__)
-
-
-static int keycache_pthread_cond_wait(pthread_cond_t *cond,
-                                      pthread_mutex_t *mutex)
-{
-  int rc;
-  struct timeval  now;            /* time when we started waiting        */
-  struct timespec timeout;        /* timeout value for the wait function */
-  struct timezone tz;
-#if defined(KEYCACHE_DEBUG)
-  int cnt=0;
-#endif
-
-  /* Get current time */
-  gettimeofday(&now, &tz);
-  /* Prepare timeout value */
-  timeout.tv_sec= now.tv_sec + KEYCACHE_TIMEOUT;
- /*
-   timeval uses microseconds.
-   timespec uses nanoseconds.
-   1 nanosecond = 1000 micro seconds
- */
-  timeout.tv_nsec= now.tv_usec * 1000;
-  KEYCACHE_THREAD_TRACE_END("started waiting");
-#if defined(KEYCACHE_DEBUG)
-  cnt++;
-  if (cnt % 100 == 0)
-    fprintf(keycache_debug_log, "waiting...\n");
-    fflush(keycache_debug_log);
-#endif
-  rc= pthread_cond_timedwait(cond, mutex, &timeout);
-  KEYCACHE_THREAD_TRACE_BEGIN("finished waiting");
-  if (rc == ETIMEDOUT || rc == ETIME)
-  {
-#if defined(KEYCACHE_DEBUG)
-    fprintf(keycache_debug_log,"aborted by keycache timeout\n");
-    fclose(keycache_debug_log);
-    abort();
-#endif
-    keycache_dump();
-  }
-
-#if defined(KEYCACHE_DEBUG)
-  KEYCACHE_DBUG_ASSERT(rc != ETIMEDOUT);
-#else
-  assert(rc != ETIMEDOUT);
-#endif
-  return rc;
-}
-#else
-#if defined(KEYCACHE_DEBUG)
-static int keycache_pthread_cond_wait(pthread_cond_t *cond,
-                                      pthread_mutex_t *mutex)
-{
-  int rc;
-  KEYCACHE_THREAD_TRACE_END("started waiting");
-  rc= pthread_cond_wait(cond, mutex);
-  KEYCACHE_THREAD_TRACE_BEGIN("finished waiting");
-  return rc;
-}
-#endif
-#endif /* defined(KEYCACHE_TIMEOUT) && !defined(__WIN__) */
-
-#if defined(KEYCACHE_DEBUG)
-
-
-static int keycache_pthread_mutex_lock(pthread_mutex_t *mutex)
-{
-  int rc;
-  rc= pthread_mutex_lock(mutex);
-  KEYCACHE_THREAD_TRACE_BEGIN("");
-  return rc;
-}
-
-
-static void keycache_pthread_mutex_unlock(pthread_mutex_t *mutex)
-{
-  KEYCACHE_THREAD_TRACE_END("");
-  pthread_mutex_unlock(mutex);
-}
-
-
-static int keycache_pthread_cond_signal(pthread_cond_t *cond)
-{
-  int rc;
-  KEYCACHE_THREAD_TRACE("signal");
-  rc= pthread_cond_signal(cond);
-  return rc;
-}
-
-
-#if defined(KEYCACHE_DEBUG_LOG)
-
-
-static void keycache_debug_print(const char * fmt,...)
-{
-  va_list args;
-  va_start(args,fmt);
-  if (keycache_debug_log)
-  {
-    (void) vfprintf(keycache_debug_log, fmt, args);
-    (void) fputc('\n',keycache_debug_log);
-  }
-  va_end(args);
-}
-#endif /* defined(KEYCACHE_DEBUG_LOG) */
-
-#if defined(KEYCACHE_DEBUG_LOG)
-
-
-void keycache_debug_log_close(void)
-{
-  if (keycache_debug_log)
-    fclose(keycache_debug_log);
-}
-#endif /* defined(KEYCACHE_DEBUG_LOG) */
-
-#endif /* defined(KEYCACHE_DEBUG) */
-
-#if !defined(DBUG_OFF)
-#define F_B_PRT(_f_, _v_) DBUG_PRINT("assert_fail", (_f_, _v_))
-
-static int fail_block(BLOCK_LINK *block)
-{
-  F_B_PRT("block->next_used:    %lx\n", (ulong) block->next_used);
-  F_B_PRT("block->prev_used:    %lx\n", (ulong) block->prev_used);
-  F_B_PRT("block->next_changed: %lx\n", (ulong) block->next_changed);
-  F_B_PRT("block->prev_changed: %lx\n", (ulong) block->prev_changed);
-  F_B_PRT("block->hash_link:    %lx\n", (ulong) block->hash_link);
-  F_B_PRT("block->status:       %u\n", block->status);
-  F_B_PRT("block->length:       %u\n", block->length);
-  F_B_PRT("block->offset:       %u\n", block->offset);
-  F_B_PRT("block->requests:     %u\n", block->requests);
-  F_B_PRT("block->temperature:  %u\n", block->temperature);
-  return 0; /* Let the assert fail. */
-}
-
-static int fail_hlink(HASH_LINK *hlink)
-{
-  F_B_PRT("hlink->next:    %lx\n", (ulong) hlink->next);
-  F_B_PRT("hlink->prev:    %lx\n", (ulong) hlink->prev);
-  F_B_PRT("hlink->block:   %lx\n", (ulong) hlink->block);
-  F_B_PRT("hlink->diskpos: %lu\n", (ulong) hlink->diskpos);
-  F_B_PRT("hlink->file:    %d\n", hlink->file);
-  return 0; /* Let the assert fail. */
-}
-
-static int cache_empty(KEY_CACHE *keycache)
-{
-  int errcnt= 0;
-  int idx;
-  if (keycache->disk_blocks <= 0)
-    return 1;
-  for (idx= 0; idx < keycache->disk_blocks; idx++)
-  {
-    BLOCK_LINK *block= keycache->block_root + idx;
-    if (block->status || block->requests || block->hash_link)
-    {
-      fprintf(stderr, "block index: %u\n", idx);
-      fail_block(block);
-      errcnt++;
-    }
-  }
-  for (idx= 0; idx < keycache->hash_links; idx++)
-  {
-    HASH_LINK *hash_link= keycache->hash_link_root + idx;
-    if (hash_link->requests || hash_link->block)
-    {
-      fprintf(stderr, "hash_link index: %u\n", idx);
-      fail_hlink(hash_link);
-      errcnt++;
-    }
-  }
-  if (errcnt)
-  {
-    fprintf(stderr, "blocks: %d  used: %lu\n",
-            keycache->disk_blocks, keycache->blocks_used);
-    fprintf(stderr, "hash_links: %d  used: %d\n",
-            keycache->hash_links, keycache->hash_links_used);
-    fprintf(stderr, "\n");
-  }
-  return !errcnt;
-}
-#endif
-

=== removed file 'mysys/mf_keycaches.c'
--- a/mysys/mf_keycaches.c	2008-05-29 15:44:11 +0000
+++ b/mysys/mf_keycaches.c	1970-01-01 00:00:00 +0000
@@ -1,106 +0,0 @@
-/* Copyright (C) 2003-2007 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; 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
-
-/*
-  Handling of multiple key caches
-
-  The idea is to have a thread safe hash on the table name,
-  with a default key cache value that is returned if the table name is not in
-  the cache.
-*/
-
-#include "mysys_priv.h"
-#include <keycache.h>
-#include <hash.h>
-#include <m_string.h>
-#include "my_safehash.h"
-
-/*****************************************************************************
-  Functions to handle the key cache objects
-*****************************************************************************/
-
-/* Variable to store all key cache objects */
-static SAFE_HASH key_cache_hash;
-
-
-my_bool multi_keycache_init(void)
-{
-  return safe_hash_init(&key_cache_hash, 16, (uchar*) dflt_key_cache);
-}
-
-
-void multi_keycache_free(void)
-{
-  safe_hash_free(&key_cache_hash);
-}
-
-/*
-  Get a key cache to be used for a specific table.
-
-  SYNOPSIS
-    multi_key_cache_search()
-    key				key to find (usually table path)
-    uint length			Length of key.
-    def				Default value if no key cache
-
-  NOTES
-    This function is coded in such a way that we will return the
-    default key cache even if one never called multi_keycache_init.
-    This will ensure that it works with old MyISAM clients.
-
-  RETURN
-    key cache to use
-*/
-
-KEY_CACHE *multi_key_cache_search(uchar *key, uint length,
-                                  KEY_CACHE *def)
-{
-  if (!key_cache_hash.hash.records)
-    return def;
-  return (KEY_CACHE*) safe_hash_search(&key_cache_hash, key, length,
-                                       (void*) def);
-}
-
-
-/*
-  Assosiate a key cache with a key
-
-
-  SYONOPSIS
-    multi_key_cache_set()
-    key				key (path to table etc..)
-    length			Length of key
-    key_cache			cache to assococite with the table
-
-  NOTES
-    This can be used both to insert a new entry and change an existing
-    entry
-*/
-
-
-my_bool multi_key_cache_set(const uchar *key, uint length,
-			    KEY_CACHE *key_cache)
-{
-  return safe_hash_set(&key_cache_hash, key, length, (uchar*) key_cache);
-}
-
-
-void multi_key_cache_change(KEY_CACHE *old_data,
-			    KEY_CACHE *new_data)
-{
-  safe_hash_change(&key_cache_hash, (uchar*) old_data, (uchar*) new_data);
-}
-
-

=== modified file 'mysys/my_static.c'
--- a/mysys/my_static.c	2009-05-07 20:48:24 +0000
+++ b/mysys/my_static.c	2009-08-18 22:19:06 +0000
@@ -47,10 +47,7 @@ struct st_remember _my_sig_remember[MAX_
 sigset_t my_signals;			/* signals blocked by mf_brkhant */
 #endif
 
-	/* from mf_reccache.c */
-ulong my_default_record_cache_size=RECORD_CACHE_SIZE;
-
-	/* from soundex.c */
+        /* from soundex.c */
 				/* ABCDEFGHIJKLMNOPQRSTUVWXYZ */
 				/* :::::::::::::::::::::::::: */
 const char *soundex_map=	  "01230120022455012623010202";

=== modified file 'mysys/mysys_priv.h'
--- a/mysys/mysys_priv.h	2008-12-04 21:02:09 +0000
+++ b/mysys/mysys_priv.h	2009-08-18 22:19:06 +0000
@@ -26,7 +26,7 @@
 
 #ifdef THREAD
 #include <my_pthread.h>
-extern pthread_mutex_t THR_LOCK_malloc, THR_LOCK_open, THR_LOCK_keycache;
+extern pthread_mutex_t THR_LOCK_malloc, THR_LOCK_open;
 extern pthread_mutex_t THR_LOCK_lock, THR_LOCK_isam, THR_LOCK_net;
 extern pthread_mutex_t THR_LOCK_charset, THR_LOCK_time;
 #else


Attachment: [text/bzr-bundle] bzr/jimw@mysql.com-20090818221906-1qvotxfrpfj3a6lc.bundle
Thread
bzr commit into libmysql branch (jimw:2861) Bug#46642Jim Winstead19 Aug