#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#46642 | Jim Winstead | 19 Aug |