home *** CD-ROM | disk | FTP | other *** search
/ Chip 2001 January / Chip_2001-01_cd1.bin / tema / mysql / mysql-3.23.28g-win-source.exe / mysys / mf_keycache.c < prev    next >
C/C++ Source or Header  |  2000-09-16  |  21KB  |  765 lines

  1. /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
  2.    
  3.    This library is free software; you can redistribute it and/or
  4.    modify it under the terms of the GNU Library General Public
  5.    License as published by the Free Software Foundation; either
  6.    version 2 of the License, or (at your option) any later version.
  7.    
  8.    This library is distributed in the hope that it will be useful,
  9.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  10.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  11.    Library General Public License for more details.
  12.    
  13.    You should have received a copy of the GNU Library General Public
  14.    License along with this library; if not, write to the Free
  15.    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
  16.    MA 02111-1307, USA */
  17.  
  18. /*
  19.   This functions is handle keyblock cacheing for NISAM, MISAM and PISAM
  20.   databases.
  21.   One cache can handle many files. Every different blocksize has it owns
  22.   set of buffers that are allocated from block_mem.
  23.   init_key_cache() should be used to init cache handler.
  24.  */
  25.  
  26. #include "mysys_priv.h"
  27. #include "my_static.h"
  28. #include <m_string.h>
  29. #include <errno.h>
  30.  
  31. #if defined(MSDOS) && !defined(M_IC80386)
  32.     /* We nead much memory */
  33. #undef my_malloc_lock
  34. #undef my_free_lock
  35. #define my_malloc_lock(A,B)    halloc((long) (A/IO_SIZE),IO_SIZE)
  36. #define my_free_lock(A,B)    hfree(A)
  37. #endif
  38.  
  39. /* size of map to be used to find changed files */
  40.  
  41. #define CHANGED_BLOCKS_HASH    128    /* Must be power of 2 */
  42. #define CHANGED_BLOCKS_MASK    (CHANGED_BLOCKS_HASH-1)
  43. #define FLUSH_CACHE         2000    /* Sort this many blocks at once */
  44.  
  45. typedef struct sec_link {
  46.   struct sec_link *next_hash,**prev_hash;/* Blocks linked acc. to hash-value */
  47.   struct sec_link *next_used,*prev_used;
  48.   struct sec_link *next_changed,**prev_changed;
  49.   File file;
  50.   my_off_t diskpos;
  51.   byte *buffer;
  52.   my_bool changed;
  53. } SEC_LINK;
  54.  
  55.  
  56. static uint find_next_bigger_power(uint value);
  57. static SEC_LINK *find_key_block(int file,my_off_t filepos,int *error);
  58.  
  59.     /* static variables in this file */
  60. static SEC_LINK *_my_block_root,**_my_hash_root,
  61.         *_my_used_first,*_my_used_last;
  62. static int    _my_disk_blocks;
  63. static uint    _my_disk_blocks_used, _my_hash_blocks;
  64. ulong        _my_blocks_used,_my_blocks_changed;
  65. ulong        _my_cache_w_requests,_my_cache_write,_my_cache_r_requests,
  66.         _my_cache_read;
  67. static byte    HUGE_PTR *_my_block_mem;
  68. static SEC_LINK *changed_blocks[CHANGED_BLOCKS_HASH];
  69. static SEC_LINK *file_blocks[CHANGED_BLOCKS_HASH];
  70. #ifndef DBUG_OFF
  71. static my_bool    _my_printed;
  72. #endif
  73.  
  74.  
  75.     /* Init of disk_buffert */
  76.     /* Returns blocks in use */
  77.     /* ARGSUSED */
  78.  
  79. int init_key_cache(ulong use_mem,
  80.            ulong leave_this_much_mem __attribute__((unused)))
  81. {
  82.   uint blocks,length;
  83.   byte *extra_mem=0;
  84.   DBUG_ENTER("init_key_cache");
  85.  
  86.   if (key_cache_inited && _my_disk_blocks > 0)
  87.   {
  88.     DBUG_PRINT("warning",("key cache already in use")); /* purecov: inspected */
  89.     DBUG_RETURN(0); /* purecov: inspected */
  90.   }
  91.   if (! key_cache_inited)
  92.   {
  93.     key_cache_inited=TRUE;
  94.     _my_disk_blocks= -1;
  95. #ifndef DBUG_OFF
  96.     _my_printed=0;
  97. #endif
  98.   }
  99.  
  100.   blocks= (uint) (use_mem/(sizeof(SEC_LINK)+sizeof(SEC_LINK*)*5/4+KEYCACHE_BLOCK_SIZE));
  101.   /* No use to have very few blocks */
  102.   if (blocks >= 8 && _my_disk_blocks < 0)
  103.   {
  104. #if !defined(HAVE_ALLOCA) && !defined(THREAD)
  105.     if ((extra_mem=my_malloc((uint) leave_this_much_mem,MYF(0))) == 0)
  106.       goto err;
  107. #endif
  108.     for (;;)
  109.     {
  110.       if ((_my_hash_blocks=find_next_bigger_power((uint) blocks)) < blocks*5/4)
  111.     _my_hash_blocks<<=1;
  112.       while ((length=(uint) blocks*sizeof(SEC_LINK)+
  113.           sizeof(SEC_LINK*)*_my_hash_blocks)+(ulong) blocks*KEYCACHE_BLOCK_SIZE >
  114.          use_mem)
  115.     blocks--;
  116.       if ((_my_block_mem=my_malloc_lock((ulong) blocks * KEYCACHE_BLOCK_SIZE,MYF(0))))
  117.       {
  118.     if ((_my_block_root=(SEC_LINK*) my_malloc((uint) length,MYF(0))) != 0)
  119.       break;
  120.     my_free_lock(_my_block_mem,MYF(0));
  121.       }
  122.       if (blocks < 8)
  123.     goto err;
  124.       blocks=blocks/4*3;
  125.     }
  126.     _my_disk_blocks=(int) blocks;
  127.     _my_hash_root= (SEC_LINK**) (_my_block_root+blocks);
  128.     bzero((byte*) _my_hash_root,_my_hash_blocks*sizeof(SEC_LINK*));
  129.     _my_used_first=_my_used_last=0;
  130.     _my_blocks_used=_my_disk_blocks_used=_my_blocks_changed=0;
  131.     _my_cache_w_requests=_my_cache_r_requests=_my_cache_read=_my_cache_write=0;
  132.     DBUG_PRINT("exit",("disk_blocks: %d  block_root: %lx  _my_hash_blocks: %d  hash_root: %lx",
  133.                _my_disk_blocks,_my_block_root,_my_hash_blocks,
  134.                _my_hash_root));
  135. #if !defined(HAVE_ALLOCA) && !defined(THREAD)
  136.     my_free(extra_mem,MYF(0));
  137. #endif
  138.   }
  139.   bzero((gptr) changed_blocks,sizeof(changed_blocks[0])*CHANGED_BLOCKS_HASH);
  140.   bzero((gptr) file_blocks,sizeof(file_blocks[0])*CHANGED_BLOCKS_HASH);
  141.   DBUG_RETURN((int) blocks);
  142. err:
  143.   if (extra_mem) /* purecov: inspected */
  144.     my_free(extra_mem,MYF(0));
  145.   my_errno=ENOMEM;
  146.   DBUG_RETURN(0);
  147. } /* init_key_cache */
  148.  
  149.  
  150.     /* Remove key_cache from memory */
  151.  
  152. void end_key_cache(void)
  153. {
  154.   DBUG_ENTER("end_key_cache");
  155.   if (! _my_blocks_changed)
  156.   {
  157.     if (_my_disk_blocks > 0)
  158.     {
  159.       my_free_lock((gptr) _my_block_mem,MYF(0));
  160.       my_free((gptr) _my_block_root,MYF(0));
  161.       _my_disk_blocks= -1;
  162.     }
  163.   }
  164.   key_cache_inited=0;
  165.   DBUG_PRINT("status",
  166.          ("used: %d  changed: %d  w_requests: %ld  writes: %ld  r_requests: %ld  reads: %ld",
  167.           _my_blocks_used,_my_blocks_changed,_my_cache_w_requests,
  168.           _my_cache_write,_my_cache_r_requests,_my_cache_read));
  169.   DBUG_VOID_RETURN;
  170. } /* end_key_cache */
  171.  
  172.  
  173. static uint find_next_bigger_power(uint value)
  174. {
  175.   uint old_value=1;
  176.   while (value)
  177.   {
  178.     old_value=value;
  179.     value&= value-1;
  180.   }
  181.   return (old_value << 1);
  182. }
  183.  
  184. static inline void link_into_file_blocks(SEC_LINK *next, int file)
  185. {
  186.   reg1 SEC_LINK **ptr= &file_blocks[(uint) file & CHANGED_BLOCKS_MASK];
  187.   next->prev_changed= ptr;
  188.   if ((next->next_changed= *ptr))
  189.     (*ptr)->prev_changed= &next->next_changed;
  190.   *ptr=next;
  191. }
  192.  
  193.  
  194. static inline void relink_into_file_blocks(SEC_LINK *next, int file)
  195. {
  196.   reg1 SEC_LINK **ptr= &file_blocks[(uint) file & CHANGED_BLOCKS_MASK];
  197.   if (next->next_changed)
  198.     next->next_changed->prev_changed=next->prev_changed;
  199.   *next->prev_changed=next->next_changed;
  200.   next->prev_changed= ptr;
  201.   if ((next->next_changed= *ptr))
  202.     (*ptr)->prev_changed= &next->next_changed;
  203.   *ptr=next;
  204. }
  205.  
  206. static inline void link_changed_to_file(SEC_LINK *next,int file)
  207. {
  208.   reg1 SEC_LINK **ptr= &file_blocks[(uint) file & CHANGED_BLOCKS_MASK];
  209.   if (next->next_changed)
  210.     next->next_changed->prev_changed=next->prev_changed;
  211.   *next->prev_changed=next->next_changed;
  212.   next->prev_changed= ptr;
  213.   if ((next->next_changed= *ptr))
  214.     (*ptr)->prev_changed= &next->next_changed;
  215.   *ptr=next;
  216.   next->changed=0;
  217.   _my_blocks_changed--;
  218. }
  219.  
  220. static inline void link_file_to_changed(SEC_LINK *next)
  221. {
  222.   reg1 SEC_LINK **ptr= &changed_blocks[(uint) next->file & CHANGED_BLOCKS_MASK];
  223.   if (next->next_changed)
  224.     next->next_changed->prev_changed=next->prev_changed;
  225.   *next->prev_changed=next->next_changed;
  226.   next->prev_changed= ptr;
  227.   if ((next->next_changed= *ptr))
  228.     (*ptr)->prev_changed= &next->next_changed;
  229.   *ptr=next;
  230.   next->changed=1;
  231.   _my_blocks_changed++;
  232. }
  233.  
  234.  
  235. #ifndef DBUG_OFF
  236. #define DBUG_OFF                /* This should work */
  237. #endif
  238.  
  239. #ifndef DBUG_OFF
  240. static void test_key_cache(char *where, my_bool lock);
  241. #endif
  242.  
  243.  
  244.     /*
  245.     ** read a key_buffer
  246.     ** filepos must point at a even KEYCACHE_BLOCK_SIZE block
  247.     ** if return_buffer is set then the intern buffer is returned if
  248.     ** it can be used
  249.     ** Returns adress to where data is read
  250.     */
  251.  
  252. byte *key_cache_read(File file, my_off_t filepos, byte *buff, uint length,
  253.              uint block_length __attribute__((unused)),
  254.              int return_buffer __attribute__((unused)))
  255. {
  256.   reg1 SEC_LINK *next;
  257.   int error=0;
  258.  
  259. #ifndef THREAD
  260.   if (block_length > KEYCACHE_BLOCK_SIZE)
  261.     return_buffer=0;
  262. #endif
  263.   if (_my_disk_blocks > 0)
  264.   {                        /* We have key_cacheing */
  265.     byte *start=buff;
  266.     uint read_length;
  267.     pthread_mutex_lock(&THR_LOCK_keycache);
  268.     do
  269.     {
  270.       _my_cache_r_requests++;
  271.       read_length= length > KEYCACHE_BLOCK_SIZE ? KEYCACHE_BLOCK_SIZE : length;
  272.       if (!(next=find_key_block(file,filepos,&error)))
  273.       {
  274.     pthread_mutex_unlock(&THR_LOCK_keycache);
  275.     return (byte*) 0;            /* Got a fatal error */
  276.       }
  277.       if (error)
  278.       {                    /* Didn't find it in cache */
  279.     if (my_pread(file,next->buffer,read_length,filepos,MYF(MY_NABP)))
  280.     {
  281.       pthread_mutex_unlock(&THR_LOCK_keycache);
  282.       return((byte*) 0);
  283.     }
  284.     _my_cache_read++;
  285.       }
  286. #ifndef THREAD                /* buffer may be used a long time */
  287.       if (return_buffer)
  288.       {
  289.     pthread_mutex_unlock(&THR_LOCK_keycache);
  290.     return (next->buffer);
  291.       }
  292. #endif
  293.       if (! (read_length & 511))
  294.     bmove512(buff,next->buffer,read_length);
  295.       else
  296.     memcpy(buff,next->buffer,(size_t) read_length);
  297.       buff+=read_length;
  298.       filepos+=read_length;
  299.     } while ((length-= read_length));
  300.     pthread_mutex_unlock(&THR_LOCK_keycache);
  301.     return(start);
  302.   }
  303.   _my_cache_r_requests++;
  304.   _my_cache_read++;
  305.   if (my_pread(file,(byte*) buff,length,filepos,MYF(MY_NABP)))
  306.     error=1;
  307.   return (error ? (byte*) 0 : buff);
  308. } /* key_cache_read */
  309.  
  310.  
  311.     /* write a key_buffer */
  312.     /* We don't have to use pwrite because of write locking */
  313.     /* buff must point at a even KEYCACHE_BLOCK_SIZE block */
  314.  
  315. int key_cache_write(File file, my_off_t filepos, byte *buff, uint length,
  316.             uint block_length  __attribute__((unused)),
  317.             int dont_write)
  318. {
  319.   reg1 SEC_LINK *next;
  320.   int error=0;
  321.  
  322.   if (!dont_write)
  323.   {                        /* Forced write of buffer */
  324.     _my_cache_write++;
  325.     if (my_pwrite(file,buff,length,filepos,MYF(MY_NABP | MY_WAIT_IF_FULL)))
  326.       return(1);
  327.   }
  328.  
  329. #if !defined(DBUG_OFF) && defined(EXTRA_DEBUG)
  330.   DBUG_EXECUTE("exec",test_key_cache("start of key_cache_write",1););
  331. #endif
  332.   if (_my_disk_blocks > 0)
  333.   {                        /* We have key_cacheing */
  334.     uint read_length;
  335.     pthread_mutex_lock(&THR_LOCK_keycache);
  336.     _my_cache_w_requests++;
  337.     do
  338.     {
  339.       read_length= length > KEYCACHE_BLOCK_SIZE ? KEYCACHE_BLOCK_SIZE : length;
  340.       if (!(next=find_key_block(file,filepos,&error)))
  341.     goto end;                /* Fatal error */
  342.       if (!dont_write)                /* If we wrote buff at start */
  343.       {
  344.     if (next->changed)            /* Unlink from changed list */
  345.       link_changed_to_file(next,next->file);
  346.       }
  347.       else if (!next->changed)
  348.     link_file_to_changed(next);        /* Add to changed list */
  349.  
  350.       if (!(read_length & 511))
  351.     bmove512(next->buffer,buff,read_length);
  352.       else
  353.     memcpy(next->buffer,buff,(size_t) read_length);
  354.       buff+=read_length;
  355.       filepos+=read_length;
  356.     } while ((length-= read_length));
  357.     error=0;
  358.     pthread_mutex_unlock(&THR_LOCK_keycache);
  359.   }
  360.   else if (dont_write)
  361.   {                        /* We must write, no cache */
  362.     _my_cache_w_requests++;
  363.     _my_cache_write++;
  364.     if (my_pwrite(file,(byte*) buff,length,filepos,
  365.           MYF(MY_NABP | MY_WAIT_IF_FULL)))
  366.       error=1;
  367.   }
  368. end:
  369. #if !defined(DBUG_OFF) && defined(EXTRA_DEBUG)
  370.   DBUG_EXECUTE("exec",test_key_cache("end of key_cache_write",1););
  371. #endif
  372.   return(error);
  373. } /* key_cache_write */
  374.  
  375.  
  376.     /* Find block in cache */
  377.     /* IF found sector and error is set then next->changed is cleared */
  378.  
  379. static SEC_LINK *find_key_block(int file, my_off_t filepos, int *error)
  380. {
  381.   reg1 SEC_LINK *next,**start;
  382.  
  383. #if !defined(DBUG_OFF) && defined(EXTRA_DEBUG)
  384.   DBUG_EXECUTE("exec",test_key_cache("start of find_key_block",0););
  385. #endif
  386.  
  387.   *error=0;
  388.   next= *(start= &_my_hash_root[((ulong) (filepos/KEYCACHE_BLOCK_SIZE)+(ulong) file) &
  389.                 (_my_hash_blocks-1)]);
  390.   while (next && (next->diskpos != filepos || next->file != file))
  391.     next= next->next_hash;
  392.  
  393.   if (next)
  394.   {                        /* Found block */
  395.     if (next != _my_used_last)
  396.     {                        /* Relink used-chain */
  397.       if (next == _my_used_first)
  398.     _my_used_first=next->next_used;
  399.       else
  400.       {
  401.     next->prev_used->next_used = next->next_used;
  402.     next->next_used->prev_used = next->prev_used;
  403.       }
  404.       next->prev_used=_my_used_last;
  405.       _my_used_last->next_used=next;
  406.     }
  407.   }
  408.   else
  409.   {                        /* New block */
  410.     if (_my_disk_blocks_used+1 <= (uint) _my_disk_blocks)
  411.     {                        /* There are unused blocks */
  412.       next= &_my_block_root[_my_blocks_used++]; /* Link in hash-chain */
  413.       next->buffer=ADD_TO_PTR(_my_block_mem,
  414.                   (ulong) _my_disk_blocks_used*KEYCACHE_BLOCK_SIZE,byte*);
  415.       /* link first in file_blocks */
  416.       next->changed=0;
  417.       link_into_file_blocks(next,file);
  418.       _my_disk_blocks_used++;
  419.       if (!_my_used_first)
  420.     _my_used_first=next;
  421.       if (_my_used_last)
  422.     _my_used_last->next_used=next; /* Last in used-chain */
  423.     }
  424.     else
  425.     {                        /* Reuse old block */
  426.       next= _my_used_first;
  427.       if (next->changed)
  428.       {
  429.     if (my_pwrite(next->file,next->buffer,KEYCACHE_BLOCK_SIZE,next->diskpos,
  430.               MYF(MY_NABP | MY_WAIT_IF_FULL)))
  431.     {
  432.       *error=1;
  433.       return((SEC_LINK*) 0);
  434.     }
  435.     _my_cache_write++;
  436.     link_changed_to_file(next,file);
  437.       }
  438.       else
  439.       {
  440.     if (next->file == -1)
  441.       link_into_file_blocks(next,file);
  442.     else
  443.       relink_into_file_blocks(next,file);
  444.       }
  445.       if (next->prev_hash)            /* If in hash-link */
  446.     if ((*next->prev_hash=next->next_hash) != 0) /* Remove from link */
  447.       next->next_hash->prev_hash= next->prev_hash;
  448.  
  449.       _my_used_last->next_used=next;
  450.       _my_used_first=next->next_used;
  451.     }
  452.     if (*start)                    /* Link in first in h.-chain */
  453.       (*start)->prev_hash= &next->next_hash;
  454.     next->next_hash= *start; next->prev_hash=start; *start=next;
  455.     next->prev_used=_my_used_last;
  456.     next->file=file;
  457.     next->diskpos=filepos;
  458.     *error=1;                    /* Block wasn't in memory */
  459.   }
  460.   _my_used_last=next;
  461. #if !defined(DBUG_OFF) && defined(EXTRA_DEBUG)
  462.   DBUG_EXECUTE("exec",test_key_cache("end of find_key_block",0););
  463. #endif
  464.   return next;
  465. } /* find_key_block */
  466.  
  467.  
  468. static void free_block(SEC_LINK *used)
  469. {
  470.   used->file= -1;
  471.   used->changed=0;
  472.   if (used != _my_used_first)            /* Relink used-chain */
  473.   {
  474.     if (used == _my_used_last)
  475.       _my_used_last=used->prev_used;
  476.     else
  477.     {
  478.       used->prev_used->next_used = used->next_used;
  479.       used->next_used->prev_used = used->prev_used;
  480.     }
  481.     used->next_used=_my_used_first;
  482.     used->next_used->prev_used=used;
  483.     _my_used_first=used;
  484.   }
  485.   if ((*used->prev_hash=used->next_hash))    /* Relink hash-chain */
  486.     used->next_hash->prev_hash= used->prev_hash;
  487.   if (used->next_changed)            /* Relink changed/file list */
  488.     used->next_changed->prev_changed=used->prev_changed;
  489.   *used->prev_changed=used->next_changed;
  490.   used->prev_hash=0; used->next_hash=0;        /* Safety */
  491. }
  492.  
  493.  
  494.     /* Flush all changed blocks to disk. Free used blocks if requested */
  495.  
  496. static int cmp_sec_link(SEC_LINK **a, SEC_LINK **b)
  497. {
  498.   return (((*a)->diskpos < (*b)->diskpos) ? -1 :
  499.       ((*a)->diskpos > (*b)->diskpos) ? 1 : 0);
  500. }
  501.  
  502. static int flush_cached_blocks(File file, SEC_LINK **cache, uint count)
  503. {
  504.   uint last_errno=0;
  505.   qsort((byte*) cache, count, sizeof(*cache), (qsort_cmp) cmp_sec_link);
  506.   for ( ; count-- ; cache++)
  507.   {
  508.     if (my_pwrite(file,(*cache)->buffer,KEYCACHE_BLOCK_SIZE,(*cache)->diskpos,
  509.           MYF(MY_NABP | MY_WAIT_IF_FULL)))
  510.     {
  511.       if (!last_errno)
  512.     last_errno=errno ? errno : -1;
  513.     }
  514.   }
  515.   return last_errno;
  516. }
  517.  
  518.  
  519. int flush_key_blocks(File file, enum flush_type type)
  520. {
  521.   int error=0,last_errno=0;
  522.   uint count=0;
  523.   SEC_LINK *cache_buff[FLUSH_CACHE],**cache,**pos,**end;
  524.   SEC_LINK *used,*next;
  525.   DBUG_ENTER("flush_key_blocks");
  526.   DBUG_PRINT("enter",("file: %d  blocks_used: %d  blocks_changed: %d",
  527.               file,_my_blocks_used,_my_blocks_changed));
  528.  
  529.   pthread_mutex_lock(&THR_LOCK_keycache);
  530.  
  531. #if !defined(DBUG_OFF) && defined(EXTRA_DEBUG)
  532.   DBUG_EXECUTE("exec",test_key_cache("start of flush_key_blocks",0););
  533. #endif
  534.   cache=cache_buff;                /* If no key cache */
  535.   if (_my_disk_blocks > 0 &&
  536.       (!my_disable_flush_key_blocks || type != FLUSH_KEEP))
  537.   {
  538.     if (type != FLUSH_IGNORE_CHANGED)
  539.     {
  540.       /* Count how many key blocks we have to cache to be able to
  541.      write everything with so few seeks as possible */
  542.  
  543.       for (used=changed_blocks[(uint) file & CHANGED_BLOCKS_MASK];
  544.        used ;
  545.        used=used->next_changed)
  546.       {
  547.     if (used->file == file)
  548.       count++;
  549.       }
  550.       /* Only allocate a new buffer if its bigger than the one we have */
  551.       if (count <= FLUSH_CACHE ||
  552.       !(cache=(SEC_LINK**) my_malloc(sizeof(SEC_LINK*)*count,MYF(0))))
  553.       {
  554.     cache=cache_buff;        /* Fall back to safe buffer */
  555.     count=FLUSH_CACHE;
  556.       }
  557.       end=cache+count;
  558.     }
  559.  
  560.     /* Go through the keys and write them to buffer to be flushed */
  561.     end=(pos=cache)+count;
  562.     for (used=changed_blocks[(uint) file & CHANGED_BLOCKS_MASK];
  563.      used ;
  564.      used=next)
  565.     {
  566.       next=used->next_changed;
  567.       if (used->file == file)
  568.       {
  569.     if (type != FLUSH_IGNORE_CHANGED)
  570.     {
  571.       if (pos == end)
  572.       {
  573.         if ((error=flush_cached_blocks(file, cache, count)))
  574.           last_errno=error;
  575.         pos=cache;
  576.       }
  577.       *pos++=used;
  578.       _my_cache_write++;
  579.     }
  580.     if (type != FLUSH_KEEP && type != FLUSH_FORCE_WRITE)
  581.     {
  582.       /* This will not destroy position or data */
  583.       _my_blocks_changed--;
  584.       free_block(used);
  585.     }
  586.     else
  587.       link_changed_to_file(used,file);
  588.       }
  589.     }
  590.     if (pos != cache)
  591.     {
  592.       if ((error=flush_cached_blocks(file, cache, (uint) (pos-cache))))
  593.     last_errno=error;
  594.     }
  595.     /* The following happens very seldom */
  596.     if (type != FLUSH_KEEP && type != FLUSH_FORCE_WRITE)
  597.     {
  598.       for (used=file_blocks[(uint) file & CHANGED_BLOCKS_MASK];
  599.        used ;
  600.        used=next)
  601.       {
  602.     next=used->next_changed;
  603.     if (used->file == file && (!used->changed ||
  604.                    type == FLUSH_IGNORE_CHANGED))
  605.       free_block(used);
  606.       }
  607.     }
  608.   }
  609. #ifndef DBUG_OFF
  610.   DBUG_EXECUTE("exec",test_key_cache("end of flush_key_blocks",0););
  611. #endif
  612.   pthread_mutex_unlock(&THR_LOCK_keycache);
  613.   if (cache != cache_buff)
  614.     my_free((gptr) cache,MYF(0));
  615.   if (last_errno)
  616.     errno=last_errno;                /* Return first error */
  617.   DBUG_RETURN(last_errno != 0);
  618. } /* flush_key_blocks */
  619.  
  620.  
  621. #ifndef DBUG_OFF
  622.  
  623.     /* Test if disk-cachee is ok */
  624.  
  625. static void test_key_cache(char *where, my_bool lock)
  626. {
  627.   reg1 uint i,found,error,changed;
  628.   SEC_LINK *pos,**prev;
  629.  
  630.   if (lock)
  631.     pthread_mutex_lock(&THR_LOCK_keycache);
  632.   found=error=0;
  633.   for (i= 0 ; i < _my_hash_blocks ; i++)
  634.   {
  635.  
  636.     for (pos= *(prev= &_my_hash_root[i]) ;
  637.      pos && found < _my_blocks_used+2 ;
  638.      found++, pos= *(prev= &pos->next_hash))
  639.     {
  640.       if (prev != pos->prev_hash)
  641.       {
  642.     error=1;
  643.     DBUG_PRINT("error",
  644.            ("hash: %d  pos: %lx  : prev: %lx  !=  pos->prev: %lx",
  645.             i,pos,prev,pos->prev_hash));
  646.       }
  647.  
  648.       if (((pos->diskpos/KEYCACHE_BLOCK_SIZE)+pos->file) % _my_hash_blocks != i)
  649.       {
  650.     DBUG_PRINT("error",("hash: %d  pos: %lx  : Wrong disk_buffer %ld",
  651.                 i,pos,pos->diskpos));
  652.     error=1;
  653.       }
  654.     }
  655.   }
  656.   if (found > _my_blocks_used)
  657.   {
  658.     DBUG_PRINT("error",("Found too many hash_pointers"));
  659.     error=1;
  660.   }
  661.   if (error && !_my_printed)
  662.   {                        /* Write all hash-pointers */
  663.     _my_printed=1;
  664.     for (i=0 ; i < _my_hash_blocks ; i++)
  665.     {
  666.       DBUG_PRINT("loop",("hash: %d  _my_hash_root: %lx",i,&_my_hash_root[i]));
  667.       pos= _my_hash_root[i]; found=0;
  668.       while (pos && found < 10)
  669.       {
  670.     DBUG_PRINT("loop",("pos: %lx  prev: %lx  next: %lx  file: %d  disk_buffer: %ld", pos,pos->prev_hash,pos->next_hash,pos->file,pos->diskpos));
  671.     found++; pos= pos->next_hash;
  672.       }
  673.     }
  674.   }
  675.  
  676.   found=changed=0;
  677.  
  678.   if ((pos=_my_used_first))
  679.   {
  680.     while (pos != _my_used_last && found < _my_blocks_used+2)
  681.     {
  682.       found++;
  683.       if (pos->changed)
  684.     changed++;
  685.       if (pos->next_used->prev_used != pos)
  686.       {
  687.     DBUG_PRINT("error",("pos: %lx  next_used: %lx  next_used->prev: %lx",
  688.                 pos,pos->next_used,pos->next_used->prev_hash));
  689.     error=1;
  690.       }
  691.       pos=pos->next_used;
  692.     }
  693.     found++;
  694.     if (pos->changed)
  695.       changed++;
  696.   }
  697.   if (found != _my_blocks_used)
  698.   {
  699.     DBUG_PRINT("error",("Found %d of %d keyblocks",found,_my_blocks_used));
  700.     error=1;
  701.   }
  702.  
  703.   for (i= 0 ; i < CHANGED_BLOCKS_HASH ; i++)
  704.   {
  705.     found=0;
  706.     prev= &changed_blocks[i];
  707.     for (pos= *prev ;  pos && found < _my_blocks_used+2; pos=pos->next_changed)
  708.     {
  709.       found++;
  710.       if (pos->prev_changed != prev)
  711.       {
  712.     DBUG_PRINT("error",("changed_block list %d doesn't point backwards properly",i));
  713.     error=1;
  714.       }
  715.       prev= &pos->next_changed;
  716.       if (((uint) pos->file & CHANGED_BLOCKS_MASK) != i)
  717.       {
  718.     DBUG_PRINT("error",("Wrong file %d in changed blocks: %d",pos->file,i));
  719.     error=1;
  720.       }
  721.       changed--;
  722.     }
  723.     if (pos)
  724.     {
  725.       DBUG_PRINT("error",("changed_blocks %d has recursive link",i));
  726.       error=1;
  727.     }
  728.  
  729.     found=0;
  730.     prev= &file_blocks[i];
  731.     for (pos= *prev ;  pos && found < _my_blocks_used+2; pos=pos->next_changed)
  732.     {
  733.       found++;
  734.       if (pos->prev_changed != prev)
  735.       {
  736.     DBUG_PRINT("error",("file_block list %d doesn't point backwards properly",i));
  737.     error=1;
  738.       }
  739.       prev= &pos->next_changed;
  740.       if (((uint) pos->file & CHANGED_BLOCKS_MASK) != i)
  741.       {
  742.     DBUG_PRINT("error",("Wrong file %d in file_blocks: %d",pos->file,i));
  743.     error=1;
  744.       }
  745.     }
  746.     if (pos)
  747.     {
  748.       DBUG_PRINT("error",("File_blocks %d has recursive link",i));
  749.       error=1;
  750.     }
  751.   }
  752.   if (changed != 0)
  753.   {
  754.     DBUG_PRINT("error",("Found %d blocks that wasn't in changed blocks",
  755.             changed));
  756.     error=1;
  757.   }
  758.   if (error)
  759.     DBUG_PRINT("error",("Found error at %s",where));
  760.   if (lock)
  761.     pthread_mutex_unlock(&THR_LOCK_keycache);
  762.   return;
  763. } /* test_key_cache */
  764. #endif
  765.