summaryrefslogtreecommitdiff
path: root/mysys
diff options
context:
space:
mode:
Diffstat (limited to 'mysys')
-rw-r--r--[-rwxr-xr-x]mysys/CMakeLists.txt9
-rw-r--r--mysys/Makefile.am29
-rw-r--r--mysys/array.c87
-rw-r--r--mysys/base64.c68
-rw-r--r--mysys/charset-def.c1
-rw-r--r--mysys/default.c28
-rw-r--r--mysys/default_modify.c58
-rw-r--r--mysys/errors.c6
-rw-r--r--mysys/hash.c22
-rwxr-xr-xmysys/make-ccc2
-rw-r--r--mysys/mf_dirname.c5
-rw-r--r--mysys/mf_format.c3
-rw-r--r--mysys/mf_iocache2.c45
-rw-r--r--mysys/mf_keycache.c2581
-rw-r--r--mysys/mf_pack.c27
-rw-r--r--mysys/mf_path.c2
-rw-r--r--mysys/mf_tempdir.c30
-rw-r--r--mysys/my_access.c106
-rw-r--r--mysys/my_alloc.c3
-rw-r--r--mysys/my_append.c1
-rw-r--r--mysys/my_atomic.c46
-rw-r--r--mysys/my_bit.c30
-rw-r--r--mysys/my_bitmap.c1038
-rw-r--r--mysys/my_clock.c5
-rw-r--r--mysys/my_compress.c129
-rw-r--r--mysys/my_copy.c10
-rw-r--r--mysys/my_create.c12
-rw-r--r--mysys/my_dup.c3
-rw-r--r--mysys/my_error.c16
-rw-r--r--mysys/my_file.c30
-rw-r--r--mysys/my_gethostbyname.c2
-rw-r--r--mysys/my_getncpus.c39
-rw-r--r--mysys/my_getopt.c105
-rw-r--r--mysys/my_getwd.c49
-rw-r--r--mysys/my_init.c33
-rw-r--r--mysys/my_lib.c121
-rw-r--r--mysys/my_lock.c10
-rw-r--r--mysys/my_lread.c54
-rw-r--r--mysys/my_lwrite.c47
-rw-r--r--mysys/my_malloc.c2
-rw-r--r--mysys/my_mkdir.c2
-rw-r--r--mysys/my_mmap.c5
-rw-r--r--mysys/my_net.c4
-rw-r--r--mysys/my_once.c2
-rw-r--r--mysys/my_open.c38
-rw-r--r--mysys/my_os2cond.c162
-rw-r--r--mysys/my_os2dirsrch.c184
-rw-r--r--mysys/my_os2dirsrch.h88
-rw-r--r--mysys/my_os2dlfcn.c74
-rw-r--r--mysys/my_os2dlfcn.h039
-rw-r--r--mysys/my_os2file64.c394
-rw-r--r--mysys/my_os2mutex.c83
-rw-r--r--mysys/my_os2thread.c128
-rw-r--r--mysys/my_os2tls.c151
-rw-r--r--mysys/my_pread.c70
-rw-r--r--mysys/my_pthread.c11
-rw-r--r--mysys/my_redel.c5
-rw-r--r--mysys/my_rename.c1
-rw-r--r--mysys/my_seek.c2
-rw-r--r--mysys/my_sleep.c2
-rw-r--r--mysys/my_static.c12
-rw-r--r--mysys/my_symlink2.c4
-rw-r--r--mysys/my_thr_init.c85
-rw-r--r--mysys/my_vle.c109
-rw-r--r--mysys/mysys_priv.h2
-rw-r--r--mysys/ptr_cmp.c2
-rw-r--r--mysys/queues.c465
-rw-r--r--mysys/raid.cc798
-rw-r--r--mysys/raid2.c31
-rw-r--r--mysys/safemalloc.c2
-rw-r--r--mysys/string.c6
-rw-r--r--mysys/test_dir.c1
-rw-r--r--mysys/testhash.c2
-rw-r--r--mysys/thr_alarm.c168
-rw-r--r--mysys/thr_lock.c257
-rw-r--r--mysys/thr_mutex.c81
-rw-r--r--mysys/trie.c236
-rw-r--r--mysys/typelib.c79
78 files changed, 5137 insertions, 3542 deletions
diff --git a/mysys/CMakeLists.txt b/mysys/CMakeLists.txt
index 608d7cb1ce9..1ae625c4c03 100755..100644
--- a/mysys/CMakeLists.txt
+++ b/mysys/CMakeLists.txt
@@ -22,11 +22,9 @@ SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX")
# Currently, USE_TLS crashes in Debug builds, so until that is fixed Debug
# .dlls cannot be loaded at runtime.
SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DUSE_TLS")
-SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -DUSE_TLS")
SET(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -DUSE_TLS")
-SET(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -DUSE_TLS")
-INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/zlib ${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/mysys )
+INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/zlib ${CMAKE_SOURCE_DIR}/include)
ADD_LIBRARY(mysys 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
@@ -37,11 +35,10 @@ ADD_LIBRARY(mysys array.c charset-def.c charset.c checksum.c default.c default_m
my_clock.c my_compress.c my_conio.c my_copy.c my_crc32.c my_create.c my_delete.c
my_div.c my_error.c my_file.c my_fopen.c my_fstream.c my_gethostbyname.c
my_gethwaddr.c my_getopt.c my_getsystime.c my_getwd.c my_handler.c my_init.c
- my_lib.c my_lock.c my_lockmem.c my_lread.c my_lwrite.c my_malloc.c my_messnc.c
+ my_lib.c my_lock.c my_lockmem.c my_malloc.c my_messnc.c
my_mkdir.c my_mmap.c my_net.c my_once.c my_open.c my_pread.c my_pthread.c
my_quick.c my_read.c my_realloc.c my_redel.c my_rename.c my_seek.c my_sleep.c
my_static.c my_symlink.c my_symlink2.c my_sync.c my_thr_init.c my_wincond.c
my_windac.c my_winthread.c my_write.c ptr_cmp.c queues.c
rijndael.c safemalloc.c sha1.c string.c thr_alarm.c thr_lock.c thr_mutex.c
- thr_rwlock.c tree.c typelib.c base64.c my_memmem.c
- my_getpagesize.c)
+ thr_rwlock.c tree.c typelib.c my_vle.c base64.c my_memmem.c my_getpagesize.c)
diff --git a/mysys/Makefile.am b/mysys/Makefile.am
index a835492e670..fca13ab52b8 100644
--- a/mysys/Makefile.am
+++ b/mysys/Makefile.am
@@ -19,12 +19,8 @@ MYSQLBASEdir= $(prefix)
INCLUDES = @ZLIB_INCLUDES@ -I$(top_builddir)/include \
-I$(top_srcdir)/include -I$(srcdir)
pkglib_LIBRARIES = libmysys.a
-LDADD = libmysys.a ../dbug/libdbug.a \
- ../strings/libmystrings.a
-noinst_HEADERS = mysys_priv.h my_static.h \
- my_os2cond.c my_os2dirsrch.c my_os2dirsrch.h \
- my_os2dlfcn.c my_os2file64.c my_os2mutex.c \
- my_os2thread.c my_os2tls.c
+LDADD = libmysys.a $(top_builddir)/strings/libmystrings.a $(top_builddir)/dbug/libdbug.a
+noinst_HEADERS = mysys_priv.h my_static.h
libmysys_a_SOURCES = my_init.c my_getwd.c mf_getdate.c my_mmap.c \
mf_path.c mf_loadpath.c my_file.c \
my_open.c my_create.c my_dup.c my_seek.c my_read.c \
@@ -34,6 +30,7 @@ libmysys_a_SOURCES = my_init.c my_getwd.c mf_getdate.c my_mmap.c \
mf_tempdir.c my_lock.c mf_brkhant.c my_alarm.c \
my_malloc.c my_realloc.c my_once.c mulalloc.c \
my_alloc.c safemalloc.c my_new.cc \
+ my_vle.c my_atomic.c \
my_fopen.c my_fstream.c my_getsystime.c \
my_error.c errors.c my_div.c my_messnc.c \
mf_format.c mf_same.c mf_dirname.c mf_fn_ext.c \
@@ -41,15 +38,15 @@ libmysys_a_SOURCES = my_init.c my_getwd.c mf_getdate.c my_mmap.c \
mf_pack.c mf_unixpath.c mf_strip.c \
mf_wcomp.c mf_wfile.c my_gethwaddr.c \
mf_qsort.c mf_qsort2.c mf_sort.c \
- ptr_cmp.c mf_radix.c queues.c \
- tree.c list.c hash.c array.c string.c typelib.c \
+ ptr_cmp.c mf_radix.c queues.c my_getncpus.c \
+ tree.c trie.c list.c hash.c array.c string.c typelib.c \
my_copy.c my_append.c my_lib.c \
my_delete.c my_rename.c my_redel.c \
- my_chsize.c my_lread.c my_lwrite.c my_clock.c \
+ my_chsize.c my_clock.c \
my_quick.c my_lockmem.c my_static.c \
my_sync.c my_getopt.c my_mkdir.c \
default_modify.c default.c \
- my_compress.c checksum.c raid.cc \
+ my_compress.c checksum.c \
my_net.c my_port.c my_sleep.c \
charset.c charset-def.c my_bitmap.c my_bit.c md5.c \
my_gethostbyname.c rijndael.c my_aes.c sha1.c \
@@ -57,14 +54,14 @@ libmysys_a_SOURCES = my_init.c my_getwd.c mf_getdate.c my_mmap.c \
my_memmem.c \
my_windac.c my_access.c base64.c my_libwrap.c
EXTRA_DIST = thr_alarm.c thr_lock.c my_pthread.c my_thr_init.c \
- thr_mutex.c thr_rwlock.c mf_soundex.c my_conio.c \
- my_wincond.c my_winthread.c CMakeLists.txt
+ thr_mutex.c thr_rwlock.c \
+ CMakeLists.txt mf_soundex.c \
+ my_conio.c my_wincond.c my_winthread.c
libmysys_a_LIBADD = @THREAD_LOBJECTS@
# test_dir_DEPENDENCIES= $(LIBRARIES)
# testhash_DEPENDENCIES= $(LIBRARIES)
# test_charset_DEPENDENCIES= $(LIBRARIES)
# charset2html_DEPENDENCIES= $(LIBRARIES)
-EXTRA_PROGRAMS =
DEFS = -DDEFAULT_BASEDIR=\"$(prefix)\" \
-DDATADIR="\"$(MYSQLDATAdir)\"" \
-DDEFAULT_CHARSET_HOME="\"$(MYSQLBASEdir)\"" \
@@ -84,6 +81,12 @@ FLAGS=$(DEFS) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) @NOINST_LDFLAGS@
# which automaticly removes the object files you use to compile a final program
#
+test_bitmap$(EXEEXT): my_bitmap.c $(LIBRARIES)
+ $(LINK) $(FLAGS) -DMAIN ./my_bitmap.c $(LDADD) $(LIBS)
+
+test_priority_queue$(EXEEXT): queues.c $(LIBRARIES)
+ $(LINK) $(FLAGS) -DMAIN ./queues.c $(LDADD) $(LIBS)
+
test_thr_alarm$(EXEEXT): thr_alarm.c $(LIBRARIES)
$(CP) $(srcdir)/thr_alarm.c ./test_thr_alarm.c
$(LINK) $(FLAGS) -DMAIN ./test_thr_alarm.c $(LDADD) $(LIBS)
diff --git a/mysys/array.c b/mysys/array.c
index 4ea1946d837..130a8f358de 100644
--- a/mysys/array.c
+++ b/mysys/array.c
@@ -22,9 +22,10 @@
Initiate dynamic array
SYNOPSIS
- init_dynamic_array()
+ init_dynamic_array2()
array Pointer to an array
element_size Size of element
+ init_buffer Initial buffer pointer
init_alloc Number of initial elements
alloc_increment Increment for adding new elements
@@ -32,14 +33,15 @@
init_dynamic_array() initiates array and allocate space for
init_alloc eilements.
Array is usable even if space allocation failed.
+ Static buffers must begin immediately after the array structure.
RETURN VALUE
TRUE my_malloc_ci() failed
FALSE Ok
*/
-my_bool init_dynamic_array(DYNAMIC_ARRAY *array, uint element_size,
- uint init_alloc,
+my_bool init_dynamic_array2(DYNAMIC_ARRAY *array, uint element_size,
+ void *init_buffer, uint init_alloc,
uint alloc_increment CALLER_INFO_PROTO)
{
DBUG_ENTER("init_dynamic_array");
@@ -51,11 +53,16 @@ my_bool init_dynamic_array(DYNAMIC_ARRAY *array, uint element_size,
}
if (!init_alloc)
+ {
init_alloc=alloc_increment;
+ init_buffer= 0;
+ }
array->elements=0;
array->max_element=init_alloc;
array->alloc_increment=alloc_increment;
array->size_of_element=element_size;
+ if ((array->buffer= init_buffer))
+ DBUG_RETURN(FALSE);
if (!(array->buffer=(char*) my_malloc_ci(element_size*init_alloc,MYF(MY_WME))))
{
array->max_element=0;
@@ -64,6 +71,14 @@ my_bool init_dynamic_array(DYNAMIC_ARRAY *array, uint element_size,
DBUG_RETURN(FALSE);
}
+my_bool init_dynamic_array(DYNAMIC_ARRAY *array, uint element_size,
+ uint init_alloc,
+ uint alloc_increment CALLER_INFO_PROTO)
+{
+ /* placeholder to preserve ABI */
+ return my_init_dynamic_array_ci(array, element_size, init_alloc,
+ alloc_increment);
+}
/*
Insert element at the end of array. Allocate memory if needed.
@@ -117,6 +132,21 @@ byte *alloc_dynamic(DYNAMIC_ARRAY *array)
if (array->elements == array->max_element)
{
char *new_ptr;
+ if (array->buffer == (char *)(array + 1))
+ {
+ /*
+ In this senerio, the buffer is statically preallocated,
+ so we have to create an all-new malloc since we overflowed
+ */
+ if (!(new_ptr= (char *) my_malloc((array->max_element+
+ array->alloc_increment) *
+ array->size_of_element,
+ MYF(MY_WME))))
+ return 0;
+ memcpy(new_ptr, array->buffer,
+ array->elements * array->size_of_element);
+ }
+ else
if (!(new_ptr=(char*) my_realloc(array->buffer,(array->max_element+
array->alloc_increment)*
array->size_of_element,
@@ -176,6 +206,20 @@ my_bool set_dynamic(DYNAMIC_ARRAY *array, gptr element, uint idx)
char *new_ptr;
size=(idx+array->alloc_increment)/array->alloc_increment;
size*= array->alloc_increment;
+ if (array->buffer == (char *)(array + 1))
+ {
+ /*
+ In this senerio, the buffer is statically preallocated,
+ so we have to create an all-new malloc since we overflowed
+ */
+ if (!(new_ptr= (char *) my_malloc(size *
+ array->size_of_element,
+ MYF(MY_WME))))
+ return 0;
+ memcpy(new_ptr, array->buffer,
+ array->elements * array->size_of_element);
+ }
+ else
if (!(new_ptr=(char*) my_realloc(array->buffer,size*
array->size_of_element,
MYF(MY_WME | MY_ALLOW_ZERO_PTR))))
@@ -226,6 +270,12 @@ void get_dynamic(DYNAMIC_ARRAY *array, gptr element, uint idx)
void delete_dynamic(DYNAMIC_ARRAY *array)
{
+ /*
+ Just mark as empty if we are using a static buffer
+ */
+ if (array->buffer == (char *)(array + 1))
+ array->elements= 0;
+ else
if (array->buffer)
{
my_free(array->buffer,MYF(MY_WME));
@@ -265,6 +315,12 @@ void freeze_size(DYNAMIC_ARRAY *array)
{
uint elements=max(array->elements,1);
+ /*
+ Do nothing if we are using a static buffer
+ */
+ if (array->buffer == (char *)(array + 1))
+ return;
+
if (array->buffer && array->max_element != elements)
{
array->buffer=(char*) my_realloc(array->buffer,
@@ -273,3 +329,28 @@ void freeze_size(DYNAMIC_ARRAY *array)
array->max_element=elements;
}
}
+
+
+/*
+ Get the index of a dynamic element
+
+ SYNOPSIS
+ get_index_dynamic()
+ array Array
+ element Whose element index
+
+*/
+
+int get_index_dynamic(DYNAMIC_ARRAY *array, gptr element)
+{
+ uint ret;
+ if (array->buffer > element)
+ return -1;
+
+ ret= (element - array->buffer) / array->size_of_element;
+ if (ret > array->elements)
+ return -1;
+
+ return ret;
+
+}
diff --git a/mysys/base64.c b/mysys/base64.c
index 47b93942784..dbe8927290d 100644
--- a/mysys/base64.c
+++ b/mysys/base64.c
@@ -13,8 +13,10 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-#include <base64.h>
+#include <my_global.h>
#include <m_string.h> /* strchr() */
+#include <m_ctype.h> /* my_isspace() */
+#include <base64.h>
#ifndef MAIN
@@ -114,7 +116,6 @@ pos(unsigned char c)
} \
if (i == size) \
{ \
- i= size + 1; \
break; \
} \
}
@@ -123,44 +124,69 @@ pos(unsigned char c)
/*
Decode a base64 string
- Note: We require that dst is pre-allocated to correct size.
- See base64_needed_decoded_length().
+ SYNOPSIS
+ base64_decode()
+ src Pointer to base64-encoded string
+ len Length of string at 'src'
+ dst Pointer to location where decoded data will be stored
+ end_ptr Pointer to variable that will refer to the character
+ after the end of the encoded data that were decoded. Can
+ be NULL.
+
+ DESCRIPTION
+
+ The base64-encoded data in the range ['src','*end_ptr') will be
+ decoded and stored starting at 'dst'. The decoding will stop
+ after 'len' characters have been read from 'src', or when padding
+ occurs in the base64-encoded data. In either case: if 'end_ptr' is
+ non-null, '*end_ptr' will be set to point to the character after
+ the last read character, even in the presence of error.
+
+ NOTE
+ We require that 'dst' is pre-allocated to correct size.
- RETURN Number of bytes produced in dst or -1 in case of failure
+ SEE ALSO
+ base64_needed_decoded_length().
+
+ RETURN VALUE
+ Number of bytes written at 'dst' or -1 in case of failure
*/
int
-base64_decode(const char *src, size_t size, void *dst)
+base64_decode(const char *src_base, size_t len,
+ void *dst, const char **end_ptr)
{
char b[3];
size_t i= 0;
char *dst_base= (char *)dst;
+ char const *src= src_base;
char *d= dst_base;
size_t j;
- while (i < size)
+ while (i < len)
{
unsigned c= 0;
size_t mark= 0;
- SKIP_SPACE(src, i, size);
+ SKIP_SPACE(src, i, len);
c += pos(*src++);
c <<= 6;
i++;
- SKIP_SPACE(src, i, size);
+ SKIP_SPACE(src, i, len);
c += pos(*src++);
c <<= 6;
i++;
- SKIP_SPACE(src, i, size);
+ SKIP_SPACE(src, i, len);
- if (* src != '=')
+ if (*src != '=')
c += pos(*src++);
else
{
- i= size;
+ src += 2; /* There should be two bytes padding */
+ i= len;
mark= 2;
c <<= 6;
goto end;
@@ -168,13 +194,14 @@ base64_decode(const char *src, size_t size, void *dst)
c <<= 6;
i++;
- SKIP_SPACE(src, i, size);
+ SKIP_SPACE(src, i, len);
if (*src != '=')
c += pos(*src++);
else
{
- i= size;
+ src += 1; /* There should be one byte padding */
+ i= len;
mark= 1;
goto end;
}
@@ -189,11 +216,14 @@ base64_decode(const char *src, size_t size, void *dst)
*d++= b[j];
}
- if (i != size)
- {
- return -1;
- }
- return d - dst_base;
+ if (end_ptr != NULL)
+ *end_ptr= src;
+
+ /*
+ The variable 'i' is set to 'len' when padding has been read, so it
+ does not actually reflect the number of bytes read from 'src'.
+ */
+ return i != len ? -1 : d - dst_base;
}
diff --git a/mysys/charset-def.c b/mysys/charset-def.c
index 394fe6de1f3..71833550ff4 100644
--- a/mysys/charset-def.c
+++ b/mysys/charset-def.c
@@ -77,6 +77,7 @@ my_bool init_compiled_charsets(myf flags __attribute__((unused)))
CHARSET_INFO *cs;
add_compiled_collation(&my_charset_bin);
+ add_compiled_collation(&my_charset_filename);
add_compiled_collation(&my_charset_latin1);
add_compiled_collation(&my_charset_latin1_bin);
diff --git a/mysys/default.c b/mysys/default.c
index dc1c5a698b8..926a8f530bf 100644
--- a/mysys/default.c
+++ b/mysys/default.c
@@ -243,7 +243,8 @@ err:
handle_option_ctx structure.
group_name The name of the group the option belongs to.
option The very option to be processed. It is already
- prepared to be used in argv (has -- prefix)
+ prepared to be used in argv (has -- prefix). If it
+ is NULL, we are handling a new group (section).
DESCRIPTION
This handler checks whether a group is one of the listed and adds an option
@@ -262,6 +263,9 @@ static int handle_default_option(void *in_ctx, const char *group_name,
char *tmp;
struct handle_option_ctx *ctx= (struct handle_option_ctx *) in_ctx;
+ if (!option)
+ return 0;
+
if (find_type((char *)group_name, ctx->group, 3))
{
if (!(tmp= alloc_root(ctx->alloc, (uint) strlen(option) + 1)))
@@ -600,7 +604,7 @@ static int search_default_file_with_ext(Process_option_func opt_handler,
strmov(name,config_file);
}
fn_format(name,name,"","",4);
-#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__)
+#if !defined(__WIN__) && !defined(__NETWARE__)
{
MY_STAT stat_info;
if (!my_stat(name,&stat_info,MYF(0)))
@@ -718,6 +722,10 @@ static int search_default_file_with_ext(Process_option_func opt_handler,
end[0]=0;
strnmov(curr_gr, ptr, min((uint) (end-ptr)+1, 4096));
+
+ /* signal that a new group is found */
+ opt_handler(handler_ctx, curr_gr, NULL);
+
continue;
}
if (!found_group)
@@ -877,8 +885,8 @@ void my_print_default_files(const char *conf_file)
fputs(name,stdout);
}
}
- puts("");
}
+ puts("");
}
void print_defaults(const char *conf_file, const char **groups)
@@ -978,10 +986,11 @@ static uint my_get_system_windows_directory(char *buffer, uint size)
Everywhere else, this is:
1. /etc/
- 2. getenv(DEFAULT_HOME_ENV)
- 3. ""
- 4. "~/"
- 5. --sysconfdir=<path>
+ 2. /etc/mysql/
+ 3. getenv(DEFAULT_HOME_ENV)
+ 4. ""
+ 5. "~/"
+ 6. --sysconfdir=<path>
*/
@@ -1002,11 +1011,8 @@ static void init_default_directories()
#elif defined(__NETWARE__)
*ptr++= "sys:/etc/";
#else
-#if defined(__EMX__) || defined(OS2)
- if ((env= getenv("ETC")))
- *ptr++= env;
-#endif
*ptr++= "/etc/";
+ *ptr++= "/etc/mysql/";
#endif
if ((env= getenv(STRINGIFY_ARG(DEFAULT_HOME_ENV))))
*ptr++= env;
diff --git a/mysys/default_modify.c b/mysys/default_modify.c
index be40d3b3660..f58d3e0da86 100644
--- a/mysys/default_modify.c
+++ b/mysys/default_modify.c
@@ -39,11 +39,13 @@ static char *add_option(char *dst, const char *option_value,
SYNOPSYS
modify_defaults_file()
file_location The location of configuration file to edit
- option option to look for
- option value The value of the option we would like to set
- section_name the name of the section
- remove_option This is true if we want to remove the option.
- False otherwise.
+ option The name of the option to look for (can be NULL)
+ option value The value of the option we would like to set (can be NULL)
+ section_name The name of the section (must be NOT NULL)
+ remove_option This defines what we want to remove:
+ - MY_REMOVE_NONE -- nothing to remove;
+ - MY_REMOVE_OPTION -- remove the specified option;
+ - MY_REMOVE_SECTION -- remove the specified section;
IMPLEMENTATION
We open the option file first, then read the file line-by-line,
looking for the section we need. At the same time we put these lines
@@ -66,7 +68,9 @@ int modify_defaults_file(const char *file_location, const char *option,
FILE *cnf_file;
MY_STAT file_stat;
char linebuff[BUFF_SIZE], *src_ptr, *dst_ptr, *file_buffer;
- uint opt_len, optval_len, sect_len, nr_newlines= 0, buffer_size;
+ uint opt_len= 0;
+ uint optval_len= 0;
+ uint sect_len, nr_newlines= 0, buffer_size;
my_bool in_section= FALSE, opt_applied= 0;
uint reserve_extended;
uint new_opt_len;
@@ -80,8 +84,11 @@ int modify_defaults_file(const char *file_location, const char *option,
if (my_fstat(fileno(cnf_file), &file_stat, MYF(0)))
goto malloc_err;
- opt_len= (uint) strlen(option);
- optval_len= (uint) strlen(option_value);
+ if (option && option_value)
+ {
+ opt_len= (uint) strlen(option);
+ optval_len= (uint) strlen(option_value);
+ }
new_opt_len= opt_len + 1 + optval_len + NEWLINE_LEN;
@@ -118,8 +125,8 @@ int modify_defaults_file(const char *file_location, const char *option,
continue;
}
- /* correct the option */
- if (in_section && !strncmp(src_ptr, option, opt_len) &&
+ /* correct the option (if requested) */
+ if (option && in_section && !strncmp(src_ptr, option, opt_len) &&
(*(src_ptr + opt_len) == '=' ||
my_isspace(&my_charset_latin1, *(src_ptr + opt_len)) ||
*(src_ptr + opt_len) == '\0'))
@@ -142,7 +149,12 @@ int modify_defaults_file(const char *file_location, const char *option,
}
else
{
- /* If going to new group and we have option to apply, do it now */
+ /*
+ If we are going to the new group and have an option to apply, do
+ it now. If we are removing a single option or the whole section
+ this will only trigger opt_applied flag.
+ */
+
if (in_section && !opt_applied && *src_ptr == '[')
{
dst_ptr= add_option(dst_ptr, option_value, option, remove_option);
@@ -152,7 +164,10 @@ int modify_defaults_file(const char *file_location, const char *option,
for (; nr_newlines; nr_newlines--)
dst_ptr= strmov(dst_ptr, NEWLINE);
- dst_ptr= strmov(dst_ptr, linebuff);
+
+ /* Skip the section if MY_REMOVE_SECTION was given */
+ if (!in_section || remove_option != MY_REMOVE_SECTION)
+ dst_ptr= strmov(dst_ptr, linebuff);
}
/* Look for a section */
if (*src_ptr == '[')
@@ -166,18 +181,31 @@ int modify_defaults_file(const char *file_location, const char *option,
{}
if (*src_ptr != ']')
+ {
+ in_section= FALSE;
continue; /* Missing closing parenthesis. Assume this was no group */
+ }
+
+ if (remove_option == MY_REMOVE_SECTION)
+ dst_ptr= dst_ptr - strlen(linebuff);
+
in_section= TRUE;
}
else
in_section= FALSE; /* mark that this section is of no interest to us */
}
}
- /* File ended. */
- if (!opt_applied && !remove_option && in_section)
+
+ /*
+ File ended. Apply an option or set opt_applied flag (in case of
+ MY_REMOVE_SECTION) so that the changes are saved. Do not do anything
+ if we are removing non-existent option.
+ */
+
+ if (!opt_applied && in_section && (remove_option != MY_REMOVE_OPTION))
{
/* New option still remains to apply at the end */
- if (*(dst_ptr - 1) != '\n')
+ if (!remove_option && *(dst_ptr - 1) != '\n')
dst_ptr= strmov(dst_ptr, NEWLINE);
dst_ptr= add_option(dst_ptr, option_value, option, remove_option);
opt_applied= 1;
diff --git a/mysys/errors.c b/mysys/errors.c
index 857de1325d4..889cf6d7fe3 100644
--- a/mysys/errors.c
+++ b/mysys/errors.c
@@ -38,7 +38,7 @@ const char * NEAR globerrs[GLOBERRS]=
"Can't get working dirctory (Errcode: %d)",
"Can't change dir to '%s' (Errcode: %d)",
"Warning: '%s' had %d links",
- "%d files and %d streams is left open\n",
+ "Warning: %d files and %d streams is left open\n",
"Disk is full writing '%s' (Errcode: %d). Waiting for someone to free space... Retry in %d secs",
"Can't create directory '%s' (Errcode: %d)",
"Character set '%s' is not a compiled character set and is not specified in the '%s' file",
@@ -49,6 +49,7 @@ const char * NEAR globerrs[GLOBERRS]=
"Can't sync file '%s' to disk (Errcode: %d)",
"Collation '%s' is not a compiled collation and is not specified in the '%s' file",
"File '%s' not found (Errcode: %d)",
+ "File '%s' (fileno: %d) was not closed"
};
void init_glob_errs(void)
@@ -77,7 +78,7 @@ void init_glob_errs()
EE(EE_GETWD) = "Can't get working dirctory (Errcode: %d)";
EE(EE_SETWD) = "Can't change dir to '%s' (Errcode: %d)";
EE(EE_LINK_WARNING) = "Warning: '%s' had %d links";
- EE(EE_OPEN_WARNING) = "%d files and %d streams is left open\n";
+ EE(EE_OPEN_WARNING) = "Warning: %d files and %d streams is left open\n";
EE(EE_DISK_FULL) = "Disk is full writing '%s'. Waiting for someone to free space...";
EE(EE_CANT_MKDIR) ="Can't create directory '%s' (Errcode: %d)";
EE(EE_UNKNOWN_CHARSET)= "Character set '%s' is not a compiled character set and is not specified in the %s file";
@@ -88,5 +89,6 @@ void init_glob_errs()
EE(EE_SYNC)= "Can't sync file '%s' to disk (Errcode: %d)";
EE(EE_UNKNOWN_COLLATION)= "Collation '%s' is not a compiled collation and is not specified in the %s file";
EE(EE_FILENOTFOUND) = "File '%s' not found (Errcode: %d)";
+ EE(EE_FILE_NOT_CLOSED) = "File '%s' (fileno: %d) was not closed";
}
#endif
diff --git a/mysys/hash.c b/mysys/hash.c
index 0c949b3a0d8..964ba6b1ca3 100644
--- a/mysys/hash.c
+++ b/mysys/hash.c
@@ -108,7 +108,7 @@ static inline void hash_free_elements(HASH *hash)
void hash_free(HASH *hash)
{
DBUG_ENTER("hash_free");
- DBUG_PRINT("enter",("hash: 0x%lxd", (long) hash));
+ DBUG_PRINT("enter",("hash: 0x%lx", (long) hash));
hash_free_elements(hash);
hash->free= 0;
@@ -315,6 +315,13 @@ my_bool my_hash_insert(HASH *info,const byte *record)
LINT_INIT(gpos); LINT_INIT(gpos2);
LINT_INIT(ptr_to_rec); LINT_INIT(ptr_to_rec2);
+ if (HASH_UNIQUE & info->flags)
+ {
+ byte *key= (byte*) hash_key(info, record, &idx, 1);
+ if (hash_search(info, key, idx))
+ return(TRUE); /* Duplicate entry */
+ }
+
flag=0;
if (!(empty=(HASH_LINK*) alloc_dynamic(&info->array)))
return(TRUE); /* No more memory */
@@ -530,6 +537,19 @@ my_bool hash_update(HASH *hash,byte *record,byte *old_key,uint old_key_length)
uint idx,new_index,new_pos_index,blength,records,empty;
HASH_LINK org_link,*data,*previous,*pos;
DBUG_ENTER("hash_update");
+
+ if (HASH_UNIQUE & hash->flags)
+ {
+ HASH_SEARCH_STATE state;
+ byte *found, *new_key= hash_key(hash, record, &idx, 1);
+ if ((found= hash_first(hash, new_key, idx, &state)))
+ do
+ {
+ if (found != record)
+ DBUG_RETURN(1); /* Duplicate entry */
+ }
+ while ((found= hash_next(hash, new_key, idx, &state)));
+ }
data=dynamic_element(&hash->array,0,HASH_LINK*);
blength=hash->blength; records=hash->records;
diff --git a/mysys/make-ccc b/mysys/make-ccc
index 9c54185682a..b34bd80e1d1 100755
--- a/mysys/make-ccc
+++ b/mysys/make-ccc
@@ -1,4 +1,4 @@
rm -f .deps/* raid.o mf_iocache.o libmysys.a
-ccc -DDEFAULT_BASEDIR="\"/usr/local/mysql\"" -DDATADIR="\"/usr/local/mysql/var\"" -DHAVE_CONFIG_H -I./../include -I../include -I.. -DDBUG_OFF -fast -O3 -fomit-frame-pointer -c array.c checksum.c default.c errors.c getopt.c getopt1.c getvar.c hash.c list.c mf_brkhant.c mf_cache.c mf_casecnv.c mf_dirname.c mf_fn_ext.c mf_format.c mf_getdate.c mf_keycache.c mf_loadpath.c mf_pack.c mf_pack2.c mf_path.c mf_qsort.c mf_qsort2.c mf_radix.c mf_reccache.c mf_same.c mf_sort.c mf_soundex.c mf_stripp.c mf_unixpath.c mf_wcomp.c mf_wfile.c mulalloc.c my_alarm.c my_alloc.c my_append.c my_chsize.c my_clock.c my_compress.c my_copy.c my_create.c my_delete.c my_div.c my_error.c my_fopen.c my_fstream.c my_getwd.c my_init.c my_lib.c my_lockmem.c my_lread.c my_lwrite.c my_malloc.c my_messnc.c my_mkdir.c my_net.c my_once.c my_open.c my_pread.c my_pthread.c my_quick.c my_read.c my_realloc.c my_redel.c my_rename.c my_seek.c my_static.c my_tempnam.c my_thr_init.c my_write.c ptr_cmp.c queues.c safemalloc.c string.c thr_alarm.c thr_lock.c thr_mutex.c thr_rwlock.c tree.c typelib.c
+ccc -DDEFAULT_BASEDIR="\"/usr/local/mysql\"" -DDATADIR="\"/usr/local/mysql/var\"" -DHAVE_CONFIG_H -I./../include -I../include -I.. -DDBUG_OFF -fast -O3 -fomit-frame-pointer -c array.c checksum.c default.c errors.c getopt.c getopt1.c getvar.c hash.c list.c mf_brkhant.c mf_cache.c mf_casecnv.c mf_dirname.c mf_fn_ext.c mf_format.c mf_getdate.c mf_keycache.c mf_loadpath.c mf_pack.c mf_pack2.c mf_path.c mf_qsort.c mf_qsort2.c mf_radix.c mf_reccache.c mf_same.c mf_sort.c mf_soundex.c mf_stripp.c mf_unixpath.c mf_wcomp.c mf_wfile.c mulalloc.c my_alarm.c my_alloc.c my_append.c my_chsize.c my_clock.c my_compress.c my_copy.c my_create.c my_delete.c my_div.c my_error.c my_fopen.c my_fstream.c my_getwd.c my_init.c my_lib.c my_lockmem.c my_malloc.c my_messnc.c my_mkdir.c my_net.c my_once.c my_open.c my_pread.c my_pthread.c my_quick.c my_read.c my_realloc.c my_redel.c my_rename.c my_seek.c my_static.c my_tempnam.c my_thr_init.c my_write.c ptr_cmp.c queues.c safemalloc.c string.c thr_alarm.c thr_lock.c thr_mutex.c thr_rwlock.c tree.c typelib.c
make raid.o mf_iocache.o my_lock.o
ar -cr libmysys.a array.o raid.o mf_iocache.o my_lock.o
diff --git a/mysys/mf_dirname.c b/mysys/mf_dirname.c
index d1672e55b65..87a4a453734 100644
--- a/mysys/mf_dirname.c
+++ b/mysys/mf_dirname.c
@@ -74,7 +74,7 @@ uint dirname_part(my_string to, const char *name)
to Store result here. Must be at least of size
min(FN_REFLEN, strlen(from) + 1) to make room
for adding FN_LIBCHAR at the end.
- from Original filename
+ from Original filename. May be == to
from_end Pointer at end of filename (normally end \0)
IMPLEMENTATION
@@ -102,6 +102,7 @@ char *convert_dirname(char *to, const char *from, const char *from_end)
#ifdef BACKSLASH_MBTAIL
CHARSET_INFO *fs= fs_character_set();
#endif
+ DBUG_ENTER("convert_dirname");
/* We use -2 here, becasue we need place for the last FN_LIBCHAR */
if (!from_end || (from_end - from) > FN_REFLEN-2)
@@ -150,5 +151,5 @@ char *convert_dirname(char *to, const char *from, const char *from_end)
*to++=FN_LIBCHAR;
*to=0;
}
- return to; /* Pointer to end of dir */
+ DBUG_RETURN(to); /* Pointer to end of dir */
} /* convert_dirname */
diff --git a/mysys/mf_format.c b/mysys/mf_format.c
index 7a861c9f1e6..3e5eeef6420 100644
--- a/mysys/mf_format.c
+++ b/mysys/mf_format.c
@@ -53,7 +53,8 @@ my_string fn_format(my_string to, const char *name, const char *dir,
if (flag & MY_UNPACK_FILENAME)
(void) unpack_dirname(dev,dev); /* Replace ~/.. with dir */
- if ((pos= (char*) strchr(name,FN_EXTCHAR)) != NullS)
+ if (!(flag & MY_APPEND_EXT) &&
+ (pos= (char*) strchr(name,FN_EXTCHAR)) != NullS)
{
if ((flag & MY_REPLACE_EXT) == 0) /* If we should keep old ext */
{
diff --git a/mysys/mf_iocache2.c b/mysys/mf_iocache2.c
index 30e9f19a189..bdb5d057a16 100644
--- a/mysys/mf_iocache2.c
+++ b/mysys/mf_iocache2.c
@@ -23,6 +23,51 @@
#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)
+{
+ uint 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)) == (uint) -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)
{
/*
diff --git a/mysys/mf_keycache.c b/mysys/mf_keycache.c
index 87f136dbf81..7ca07016823 100644
--- a/mysys/mf_keycache.c
+++ b/mysys/mf_keycache.c
@@ -36,6 +36,69 @@
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"
@@ -83,14 +146,6 @@
#define KEYCACHE_DEBUG_LOG "my_key_cache_debug.log"
*/
-#if defined(MSDOS) && !defined(M_IC80386)
-/* we nead much memory */
-#undef my_malloc_lock
-#undef my_free_lock
-#define my_malloc_lock(A,B) halloc((long) (A/IO_SIZE),IO_SIZE)
-#define my_free_lock(A,B) hfree(A)
-#endif /* defined(MSDOS) && !defined(M_IC80386) */
-
#define STRUCT_PTR(TYPE, MEMBER, a) \
(TYPE *) ((char *) (a) - offsetof(TYPE, MEMBER))
@@ -119,12 +174,16 @@ struct st_hash_link
};
/* simple states of a block */
-#define BLOCK_ERROR 1 /* an error occured when performing disk i/o */
-#define BLOCK_READ 2 /* the is page in the block buffer */
-#define BLOCK_IN_SWITCH 4 /* block is preparing to read new page */
-#define BLOCK_REASSIGNED 8 /* block does not accept requests for old page */
-#define BLOCK_IN_FLUSH 16 /* block is in flush operation */
-#define BLOCK_CHANGED 32 /* block buffer contains a dirty page */
+#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
@@ -161,13 +220,15 @@ KEY_CACHE *dflt_key_cache= &dflt_key_cache_var;
static int flush_all_key_blocks(KEY_CACHE *keycache);
#ifdef THREAD
-static void link_into_queue(KEYCACHE_WQUEUE *wqueue,
- struct st_my_thread_var *thread);
-static void unlink_from_queue(KEYCACHE_WQUEUE *wqueue,
- struct st_my_thread_var *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);
-#ifndef DBUG_OFF
+#if !defined(DBUG_OFF)
static void test_key_cache(KEY_CACHE *keycache,
const char *where, my_bool lock);
#endif
@@ -263,15 +324,16 @@ static int keycache_pthread_cond_signal(pthread_cond_t *cond);
#define keycache_pthread_cond_signal pthread_cond_signal
#endif /* defined(KEYCACHE_DEBUG) */
-static uint next_power(uint value)
+#if !defined(DBUG_OFF)
+#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)
{
- uint old_value= 1;
- while (value)
- {
- old_value= value;
- value&= value-1;
- }
- return (old_value << 1);
+ return (uint) my_round_up_to_next_power((uint32) value) << 1;
}
@@ -322,6 +384,14 @@ int init_key_cache(KEY_CACHE *keycache, uint key_cache_block_size,
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;
@@ -336,7 +406,7 @@ int init_key_cache(KEY_CACHE *keycache, uint key_cache_block_size,
blocks= (uint) (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 && keycache->disk_blocks < 0)
+ if (blocks >= 8)
{
for ( ; ; )
{
@@ -410,8 +480,6 @@ int init_key_cache(KEY_CACHE *keycache, uint key_cache_block_size,
blocks * age_threshold / 100 :
blocks);
- keycache->cnt_for_resize_op= 0;
- keycache->resize_in_flush= 0;
keycache->can_be_used= 1;
keycache->waiting_for_hash_link.last_thread= NULL;
@@ -427,6 +495,11 @@ int init_key_cache(KEY_CACHE *keycache, uint key_cache_block_size,
bzero((gptr) 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);
@@ -485,8 +558,6 @@ int resize_key_cache(KEY_CACHE *keycache, uint key_cache_block_size,
uint age_threshold)
{
int blocks;
- struct st_my_thread_var *thread;
- KEYCACHE_WQUEUE *wqueue;
DBUG_ENTER("resize_key_cache");
if (!keycache->key_cache_inited)
@@ -502,56 +573,88 @@ int resize_key_cache(KEY_CACHE *keycache, uint key_cache_block_size,
keycache_pthread_mutex_lock(&keycache->cache_lock);
#ifdef THREAD
- wqueue= &keycache->resize_queue;
- thread= my_thread_var;
- link_into_queue(wqueue, thread);
-
- while (wqueue->last_thread->next != 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)
{
- keycache_pthread_cond_wait(&thread->suspend, &keycache->cache_lock);
+ /* purecov: begin inspected */
+ wait_on_queue(&keycache->resize_queue, &keycache->cache_lock);
+ /* purecov: end */
}
#endif
- keycache->resize_in_flush= 1;
- if (flush_all_key_blocks(keycache))
+ /*
+ 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)
{
- /* TODO: if this happens, we should write a warning in the log file ! */
+ /* 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;
- blocks= 0;
- keycache->can_be_used= 0;
- goto finish;
}
- keycache->resize_in_flush= 0;
- keycache->can_be_used= 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)
- {
- KEYCACHE_DBUG_PRINT("resize_key_cache: wait",
- ("suspend thread %ld", thread->id));
- keycache_pthread_cond_wait(&thread->suspend, &keycache->cache_lock);
- }
+ 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:
-#ifdef THREAD
- unlink_from_queue(wqueue, thread);
- /* Signal for the next resize request to proceeed if any */
- if (wqueue->last_thread)
- {
- KEYCACHE_DBUG_PRINT("resize_key_cache: signal",
- ("thread %ld", wqueue->last_thread->next->id));
- keycache_pthread_cond_signal(&wqueue->last_thread->next->suspend);
- }
-#endif
+ /*
+ 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);
- return blocks;
+ DBUG_RETURN(blocks);
}
@@ -570,18 +673,8 @@ static inline void inc_counter_for_resize_op(KEY_CACHE *keycache)
*/
static inline void dec_counter_for_resize_op(KEY_CACHE *keycache)
{
-#ifdef THREAD
- struct st_my_thread_var *last_thread;
- if (!--keycache->cnt_for_resize_op &&
- (last_thread= keycache->resize_queue.last_thread))
- {
- KEYCACHE_DBUG_PRINT("dec_counter_for_resize_op: signal",
- ("thread %ld", last_thread->next->id));
- keycache_pthread_cond_signal(&last_thread->next->suspend);
- }
-#else
- keycache->cnt_for_resize_op--;
-#endif
+ if (!--keycache->cnt_for_resize_op)
+ release_whole_queue(&keycache->waiting_for_resize_cnt);
}
/*
@@ -672,6 +765,7 @@ void end_key_cache(KEY_CACHE *keycache, my_bool cleanup)
#ifdef THREAD
+
/*
Link a thread into double-linked queue of waiting threads.
@@ -693,6 +787,8 @@ 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 */
@@ -728,6 +824,7 @@ 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;
@@ -740,6 +837,13 @@ static void unlink_from_queue(KEYCACHE_WQUEUE *wqueue,
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
}
@@ -747,9 +851,9 @@ static void unlink_from_queue(KEYCACHE_WQUEUE *wqueue,
Add a thread to single-linked queue of waiting threads
SYNOPSIS
- add_to_queue()
- wqueue pointer to the queue structure
- thread pointer to the thread to be added to the queue
+ wait_on_queue()
+ wqueue Pointer to the queue structure.
+ mutex Cache_lock to acquire after awake.
RETURN VALUE
none
@@ -758,12 +862,23 @@ static void unlink_from_queue(KEYCACHE_WQUEUE *wqueue,
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 inline void add_to_queue(KEYCACHE_WQUEUE *wqueue,
- struct st_my_thread_var *thread)
+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
@@ -772,6 +887,17 @@ static inline void add_to_queue(KEYCACHE_WQUEUE *wqueue,
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);
}
@@ -779,36 +905,47 @@ static inline void add_to_queue(KEYCACHE_WQUEUE *wqueue,
Remove all threads from queue signaling them to proceed
SYNOPSIS
- realease_queue()
- wqueue pointer to the queue structure
- thread pointer to the thread to be added to the queue
+ release_whole_queue()
+ wqueue pointer to the queue structure
RETURN VALUE
none
NOTES.
- See notes for add_to_queue
+ See notes for wait_on_queue().
When removed from the queue each thread is signaled via condition
variable thread->suspend.
*/
-static void release_queue(KEYCACHE_WQUEUE *wqueue)
+static void release_whole_queue(KEYCACHE_WQUEUE *wqueue)
{
- struct st_my_thread_var *last= wqueue->last_thread;
- struct st_my_thread_var *next= last->next;
+ 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_queue: signal", ("thread %ld", thread->id));
+ 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
+
+#endif /* THREAD */
/*
@@ -817,9 +954,19 @@ static void release_queue(KEYCACHE_WQUEUE *wqueue)
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
}
@@ -829,6 +976,8 @@ static inline void unlink_changed(BLOCK_LINK *block)
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;
@@ -837,14 +986,37 @@ static inline void link_changed(BLOCK_LINK *block, BLOCK_LINK **phead)
/*
- Unlink a block from the chain of dirty/clean blocks, if it's asked for,
- and link it to the chain of clean blocks for the specified file
+ 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)]);
@@ -858,13 +1030,34 @@ static void link_to_file_list(KEY_CACHE *keycache,
/*
- Unlink a block from the chain of clean blocks for the specified
- file and link it to the chain of dirty blocks for this file
+ 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 inline void link_to_changed_list(KEY_CACHE *keycache,
- BLOCK_LINK *block)
+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)]);
@@ -889,13 +1082,13 @@ static inline void link_to_changed_list(KEY_CACHE *keycache,
none
NOTES.
- The LRU chain is represented by a curcular list of block structures.
+ 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 chain is divided into two parts - hot and warm.
+ 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 replacement.
+ 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)
@@ -908,6 +1101,9 @@ static inline void link_to_changed_list(KEY_CACHE *keycache,
+----| 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,
@@ -916,7 +1112,12 @@ static void link_block(KEY_CACHE *keycache, BLOCK_LINK *block, my_bool hot,
BLOCK_LINK *ins;
BLOCK_LINK **pins;
- KEYCACHE_DBUG_ASSERT(! (block->hash_link && block->hash_link->requests));
+ 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)
{
@@ -945,6 +1146,29 @@ static void link_block(KEY_CACHE *keycache, BLOCK_LINK *block, my_bool hot,
}
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",
@@ -971,7 +1195,7 @@ static void link_block(KEY_CACHE *keycache, BLOCK_LINK *block, my_bool hot,
}
else
{
- /* The LRU chain is empty */
+ /* 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;
}
@@ -1005,6 +1229,13 @@ static void link_block(KEY_CACHE *keycache, BLOCK_LINK *block, my_bool hot,
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;
@@ -1018,6 +1249,13 @@ static void unlink_block(KEY_CACHE *keycache, BLOCK_LINK *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)
@@ -1032,12 +1270,27 @@ static void unlink_block(KEY_CACHE *keycache, BLOCK_LINK *block)
/*
- Register requests for a block
+ 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)
{
- if (! block->requests)
- /* First request for the block unlinks it */
+ DBUG_ASSERT(block->status & BLOCK_IN_USE);
+ DBUG_ASSERT(block->hash_link);
+
+ if (!block->requests)
unlink_block(keycache, block);
block->requests+=count;
}
@@ -1057,7 +1310,7 @@ static void reg_requests(KEY_CACHE *keycache, BLOCK_LINK *block, int count)
none
NOTES.
- Every linking to the LRU chain decrements by one a special block
+ 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
@@ -1070,11 +1323,20 @@ static void reg_requests(KEY_CACHE *keycache, BLOCK_LINK *block, int count)
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);
if (! --block->requests)
{
my_bool hot;
@@ -1093,9 +1355,22 @@ static void unreg_request(KEY_CACHE *keycache,
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;
- /* Check if we should link a hot block to the warm block */
if (block && keycache->keycache_time - block->last_hit_time >
keycache->age_threshold)
{
@@ -1116,10 +1391,20 @@ static void unreg_request(KEY_CACHE *keycache,
Remove a reader of the page in block
*/
-static inline void remove_reader(BLOCK_LINK *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
}
@@ -1128,15 +1413,28 @@ static inline void remove_reader(BLOCK_LINK *block)
signals on its termination
*/
-static inline void wait_for_readers(KEY_CACHE *keycache, BLOCK_LINK *block)
+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_ERROR | 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;
@@ -1225,7 +1523,6 @@ static HASH_LINK *get_hash_link(KEY_CACHE *keycache,
int file, my_off_t filepos)
{
reg1 HASH_LINK *hash_link, **start;
- KEYCACHE_PAGE page;
#if defined(KEYCACHE_DEBUG)
int cnt;
#endif
@@ -1280,6 +1577,7 @@ restart:
#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;
@@ -1365,77 +1663,308 @@ static BLOCK_LINK *find_key_block(KEY_CACHE *keycache,
#endif
restart:
- /* Find the hash link for the requested page (file, filepos) */
+ /*
+ 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 (wrmode && keycache->resize_in_flush)
+ if (keycache->in_resize)
{
- /* This is a write request during the flush phase of a resize operation */
+ /* This is a request during a resize operation */
- if (page_status != PAGE_READ)
+ if (!block)
{
- /* We don't need the page in the cache: we are going to write on disk */
+ 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--;
- unlink_hash(keycache, hash_link);
- return 0;
+ 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)));
}
- if (!(block->status & BLOCK_IN_FLUSH))
+ /*
+ 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)))
{
- hash_link->requests--;
/*
- Remove block to invalidate the page in the block buffer
- as we are going to write directly on disk.
- Although we have an exlusive lock for the updated key part
- the control can be yieded by the current thread as we might
+ 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 BLOCL_REASSIGNED
+ removed from the cache as we set the BLOCK_REASSIGNED
flag (see the code below that handles reading requests).
*/
free_block(keycache, block);
- return 0;
}
- /* Wait intil the page is flushed on disk */
- hash_link->requests--;
+ else
{
-#ifdef THREAD
- struct st_my_thread_var *thread= my_thread_var;
- add_to_queue(&block->wqueue[COND_FOR_SAVED], 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);
-#else
- KEYCACHE_DBUG_ASSERT(0);
/*
- Given the use of "resize_in_flush", it seems impossible
- that this whole branch is ever entered in single-threaded case
- because "(wrmode && keycache->resize_in_flush)" cannot be true.
- TODO: Check this, and then put the whole branch into the
- "#ifdef THREAD" guard.
+ The block will be evicted/freed soon. Don't touch it in any way.
+ Unregister the request that we registered above.
*/
-#endif
+ 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));
}
- /* Invalidate page in the block if it has not been done yet */
- if (block->status)
- free_block(keycache, block);
- return 0;
+ DBUG_RETURN(0);
}
if (page_status == PAGE_READ &&
- (block->status & (BLOCK_IN_SWITCH | BLOCK_REASSIGNED)))
+ (block->status & (BLOCK_IN_EVICTION | BLOCK_IN_SWITCH |
+ BLOCK_REASSIGNED)))
{
- /* This is a request for a page to be removed from cache */
+ /*
+ 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 "
@@ -1446,43 +1975,58 @@ restart:
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"));
- {
-#ifdef THREAD
- struct st_my_thread_var *thread= my_thread_var;
- /* Put the request into the queue of those waiting for the old page */
- add_to_queue(&block->wqueue[COND_FOR_SAVED], thread);
- /* Wait until the request can be resubmitted */
- 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);
-#else
- KEYCACHE_DBUG_ASSERT(0);
- /* No parallel requests in single-threaded case */
-#endif
- }
+ wait_on_queue(&block->wqueue[COND_FOR_SAVED], &keycache->cache_lock);
KEYCACHE_DBUG_PRINT("find_key_block",
("request for old page resubmitted"));
- /* Resubmit the request */
+ /*
+ The block is no longer assigned to this hash_link.
+ Get another one.
+ */
goto restart;
}
}
else
{
- /* This is a request for a new page or for a page not to be removed */
+ /*
+ 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 for the page yet */
+ /* No block is assigned to the hash_link yet. */
if (keycache->blocks_unused)
{
if (keycache->free_block_list)
@@ -1495,24 +2039,32 @@ restart:
else
{
/* 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->buffer= ADD_TO_PTR(keycache->block_mem,
((ulong) keycache->blocks_used*
keycache->key_cache_block_size),
byte*);
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= 0;
+ 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;
- link_to_file_list(keycache, block, file, 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",
@@ -1520,17 +2072,26 @@ restart:
}
else
{
- /* There are no never used blocks, use a block from the LRU chain */
-
- /*
- Wait until a new block is added to the LRU chain;
- several threads might wait here for the same page,
- all of them must get the same block
+ /*
+ 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);
@@ -1543,24 +2104,50 @@ restart:
}
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)
{
- /*
- Take the first block from the LRU chain
- unlinking it from the chain
- */
+ /* 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;
- reg_requests(keycache, block,1);
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) )
{
@@ -1575,44 +2162,117 @@ restart:
/* 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= my_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_pthread_mutex_lock(&keycache->cache_lock);
- keycache->global_cache_write++;
+ 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= my_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_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 this page from the hash table */
+ /* Remove the hash link for the old file block from the hash. */
unlink_hash(keycache, block->hash_link);
- /* All pending requests for this page must be resubmitted */
- if (block->wqueue[COND_FOR_SAVED].last_thread)
- release_queue(&block->wqueue[COND_FOR_SAVED]);
+
+ /*
+ 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);
}
- link_to_file_list(keycache, block, file,
- (my_bool)(block->hash_link ? 1 : 0));
- block->status= error? BLOCK_ERROR : 0;
+ 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);
@@ -1620,7 +2280,20 @@ restart:
}
else
{
- /* This is for secondary requests for a new page only */
+ /*
+ 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,
@@ -1630,10 +2303,35 @@ restart:
PAGE_READ : PAGE_WAIT_TO_BE_READ);
}
}
- keycache->global_cache_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 "
@@ -1646,6 +2344,16 @@ restart:
}
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: %u",
@@ -1697,13 +2405,22 @@ static void read_block(KEY_CACHE *keycache,
if (primary)
{
/*
- This code is executed only by threads
- that submitted primary requests
+ 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);
/*
@@ -1713,45 +2430,50 @@ static void read_block(KEY_CACHE *keycache,
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->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 */
- if (block->wqueue[COND_FOR_REQUESTED].last_thread)
- release_queue(&block->wqueue[COND_FOR_REQUESTED]);
+ release_whole_queue(&block->wqueue[COND_FOR_REQUESTED]);
}
else
{
/*
- This code is executed only by threads
- that submitted secondary requests
+ 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"));
- {
-#ifdef THREAD
- struct st_my_thread_var *thread= my_thread_var;
- /* Put the request into a queue and wait until it can be processed */
- add_to_queue(&block->wqueue[COND_FOR_REQUESTED], thread);
- do
- {
- KEYCACHE_DBUG_PRINT("read_block: wait",
- ("suspend thread %ld", thread->id));
- keycache_pthread_cond_wait(&thread->suspend,
- &keycache->cache_lock);
- }
- while (thread->next);
-#else
- KEYCACHE_DBUG_ASSERT(0);
- /* No parallel requests in single-threaded case */
-#endif
- }
+ wait_on_queue(&block->wqueue[COND_FOR_REQUESTED], &keycache->cache_lock);
KEYCACHE_DBUG_PRINT("read_block",
("secondary request: new page in cache"));
}
@@ -1792,32 +2514,59 @@ byte *key_cache_read(KEY_CACHE *keycache,
uint block_length __attribute__((unused)),
int return_buffer __attribute__((unused)))
{
+ my_bool locked_and_incremented= FALSE;
int error=0;
- uint offset= 0;
byte *start= buff;
DBUG_ENTER("key_cache_read");
DBUG_PRINT("enter", ("fd: %u pos: %lu length: %u",
(uint) file, (ulong) filepos, length));
- if (keycache->can_be_used)
+ if (keycache->key_cache_inited)
{
/* Key cache is used */
reg1 BLOCK_LINK *block;
uint read_length;
+ uint offset;
uint status;
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-1));
/* Read data in key_cache_block_size increments */
do
{
- keycache_pthread_mutex_lock(&keycache->cache_lock);
+ /* Cache could be disabled in a later iteration. */
if (!keycache->can_be_used)
- {
- keycache_pthread_mutex_unlock(&keycache->cache_lock);
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);
@@ -1827,34 +2576,61 @@ byte *key_cache_read(KEY_CACHE *keycache,
return_buffer=0;
#endif
- inc_counter_for_resize_op(keycache);
+ /* 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->status != BLOCK_ERROR && 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));
- }
- else if (! (block->status & BLOCK_ERROR) &&
- block->length < read_length + offset)
+ if (!block)
{
/*
- 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
+ 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.
*/
- my_errno= -1;
- block->status|= BLOCK_ERROR;
+ keycache->global_cache_read++;
+ keycache_pthread_mutex_unlock(&keycache->cache_lock);
+ error= (my_pread(file, (byte*) 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;
+ }
}
- if (! ((status= block->status) & BLOCK_ERROR))
+ /* block status may have added BLOCK_ERROR in the above 'if'. */
+ if (!((status= 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
@@ -1867,44 +2643,57 @@ byte *key_cache_read(KEY_CACHE *keycache,
#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);
+
/*
- Link the block into the LRU chain
- if it's the last submitted request for the block
+ 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);
- dec_counter_for_resize_op(keycache);
-
- keycache_pthread_mutex_unlock(&keycache->cache_lock);
-
if (status & BLOCK_ERROR)
- DBUG_RETURN((byte *) 0);
+ {
+ 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));
- DBUG_RETURN(start);
+ goto end;
}
-no_key_cache: /* Key cache is not used */
+no_key_cache:
+ /* Key cache is not used */
- /* We can't use mutex here as the key cache may not be initialized */
keycache->global_cache_r_requests++;
keycache->global_cache_read++;
- if (my_pread(file, (byte*) buff, length, filepos+offset, MYF(MY_NABP)))
+ if (locked_and_incremented)
+ keycache_pthread_mutex_unlock(&keycache->cache_lock);
+ if (my_pread(file, (byte*) 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_RETURN(error ? (byte*) 0 : start);
}
@@ -1933,90 +2722,211 @@ int key_cache_insert(KEY_CACHE *keycache,
File file, my_off_t filepos, int level,
byte *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->can_be_used)
+ if (keycache->key_cache_inited)
{
/* Key cache is used */
reg1 BLOCK_LINK *block;
uint read_length;
- int page_st;
- int error;
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-1));
+ /* Load data in key_cache_block_size increments. */
do
{
- keycache_pthread_mutex_lock(&keycache->cache_lock);
- if (!keycache->can_be_used)
- {
- keycache_pthread_mutex_unlock(&keycache->cache_lock);
- DBUG_RETURN(0);
- }
- /* Read data into key cache from buff in key_cache_block_size incr. */
+ /* 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);
- inc_counter_for_resize_op(keycache);
+ /* 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->status != BLOCK_ERROR && page_st != PAGE_READ)
+ if (!block)
{
- /* The requested page is to be read into the block buffer */
-#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].
+ 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);
+ /* 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);
- /* Here we are alone again. */
+ 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
- block->status= BLOCK_READ;
- block->length= read_length+offset;
- KEYCACHE_DBUG_PRINT("key_cache_insert",
- ("primary request: new page in cache"));
- /* Signal that all pending requests for this now can be processed. */
- if (block->wqueue[COND_FOR_REQUESTED].last_thread)
- release_queue(&block->wqueue[COND_FOR_REQUESTED]);
- }
+ /*
+ 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);
+
/*
- Link the block into the LRU chain
- if it's the last submitted request for the block
+ 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);
error= (block->status & BLOCK_ERROR);
- dec_counter_for_resize_op(keycache);
-
- keycache_pthread_mutex_unlock(&keycache->cache_lock);
-
if (error)
- DBUG_RETURN(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(0);
+
+ DBUG_RETURN(error);
}
@@ -2045,6 +2955,8 @@ int key_cache_insert(KEY_CACHE *keycache,
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,
@@ -2053,7 +2965,7 @@ int key_cache_write(KEY_CACHE *keycache,
uint block_length __attribute__((unused)),
int dont_write)
{
- reg1 BLOCK_LINK *block;
+ my_bool locked_and_incremented= FALSE;
int error=0;
DBUG_ENTER("key_cache_write");
DBUG_PRINT("enter",
@@ -2063,10 +2975,14 @@ int key_cache_write(KEY_CACHE *keycache,
if (!dont_write)
{
- /* Force writing from buff into disk */
+ /* purecov: begin inspected */
+ /* Not used in the server. */
+ /* Force writing from buff into disk. */
+ keycache->global_cache_w_requests++;
keycache->global_cache_write++;
if (my_pwrite(file, buff, length, filepos, MYF(MY_NABP | MY_WAIT_IF_FULL)))
DBUG_RETURN(1);
+ /* purecov: end */
}
#if !defined(DBUG_OFF) && defined(EXTRA_DEBUG)
@@ -2074,92 +2990,222 @@ int key_cache_write(KEY_CACHE *keycache,
test_key_cache(keycache, "start of key_cache_write", 1););
#endif
- if (keycache->can_be_used)
+ if (keycache->key_cache_inited)
{
/* Key cache is used */
+ reg1 BLOCK_LINK *block;
uint read_length;
- int page_st;
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-1));
+ /* Write data in key_cache_block_size increments. */
do
{
- keycache_pthread_mutex_lock(&keycache->cache_lock);
+ /* Cache could be disabled in a later iteration. */
if (!keycache->can_be_used)
- {
- keycache_pthread_mutex_unlock(&keycache->cache_lock);
goto no_key_cache;
- }
- /* Write data in key_cache_block_size increments */
+ /* 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);
- inc_counter_for_resize_op(keycache);
+ /* 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)
{
- /* It happens only for requests submitted during resize operation */
- dec_counter_for_resize_op(keycache);
- keycache_pthread_mutex_unlock(&keycache->cache_lock);
- if (dont_write)
+ /*
+ 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)
{
- keycache->global_cache_w_requests++;
+ /* Used in the server. */
keycache->global_cache_write++;
- if (my_pwrite(file, (byte*) buff, length, filepos,
- MYF(MY_NABP | MY_WAIT_IF_FULL)))
+ keycache_pthread_mutex_unlock(&keycache->cache_lock);
+ if (my_pwrite(file, (byte*) buff, read_length, filepos + offset,
+ MYF(MY_NABP | MY_WAIT_IF_FULL)))
error=1;
- }
+ keycache_pthread_mutex_lock(&keycache->cache_lock);
+ }
goto next_block;
}
-
- if (block->status != BLOCK_ERROR && page_st != PAGE_READ &&
- (offset || read_length < keycache->key_cache_block_size))
+ /*
+ 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,(my_bool)(page_st == PAGE_TO_BE_READ));
+ 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))
+ {
+#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)
{
- /* buff has been written to disk at start */
+ /* 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);
- if (! (block->status & BLOCK_ERROR))
- {
- if (!(read_length & 511))
- bmove512(block->buffer+offset, buff, read_length);
- else
- memcpy(block->buffer+offset, buff, (size_t) read_length);
- }
+ /* Threads may be waiting for the changes to be complete. */
+ release_whole_queue(&block->wqueue[COND_FOR_REQUESTED]);
- block->status|=BLOCK_READ;
+ /*
+ 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);
- /* Unregister the request */
- block->hash_link->requests--;
+ /*
+ 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);
if (block->status & BLOCK_ERROR)
{
- keycache_pthread_mutex_unlock(&keycache->cache_lock);
error= 1;
break;
}
- dec_counter_for_resize_op(keycache);
-
- keycache_pthread_mutex_unlock(&keycache->cache_lock);
-
next_block:
buff+= read_length;
filepos+= read_length+offset;
@@ -2173,14 +3219,24 @@ 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 (my_pwrite(file, (byte*) buff, length, filepos,
MYF(MY_NABP | MY_WAIT_IF_FULL)))
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););
@@ -2190,9 +3246,30 @@ end:
/*
- Free block: remove reference to it from hash table,
- remove it from the chain file of dirty/clean blocks
- and add it to the free list.
+ 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)
@@ -2201,6 +3278,31 @@ static void free_block(KEY_CACHE *keycache, BLOCK_LINK *block)
KEYCACHE_DBUG_PRINT("free_block",
("block %u to be freed, hash_link %p",
BLOCK_NUMBER(block), block->hash_link));
+ /*
+ 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)
{
/*
@@ -2211,24 +3313,77 @@ static void free_block(KEY_CACHE *keycache, BLOCK_LINK *block)
*/
block->status|= BLOCK_REASSIGNED;
wait_for_readers(keycache, block);
- unlink_hash(keycache, block->hash_link);
+ /*
+ 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().
+ */
}
- unlink_changed(block);
- 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"));
+ /*
+ 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);
- block->hash_link= NULL;
+ /*
+ 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;
- /* Remove the free block from the LRU ring. */
+ /* 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;
@@ -2236,8 +3391,7 @@ static void free_block(KEY_CACHE *keycache, BLOCK_LINK *block)
keycache->blocks_unused++;
/* All pending requests for this page must be resubmitted. */
- if (block->wqueue[COND_FOR_SAVED].last_thread)
- release_queue(&block->wqueue[COND_FOR_SAVED]);
+ release_whole_queue(&block->wqueue[COND_FOR_SAVED]);
}
@@ -2271,54 +3425,99 @@ static int flush_cached_blocks(KEY_CACHE *keycache,
qsort((byte*) 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)));
- keycache_pthread_mutex_unlock(&keycache->cache_lock);
- error= my_pwrite(file,
- block->buffer+block->offset,
- block->length - block->offset,
- block->hash_link->diskpos+ block->offset,
- MYF(MY_NABP | MY_WAIT_IF_FULL));
- keycache_pthread_mutex_lock(&keycache->cache_lock);
- keycache->global_cache_write++;
- if (error)
+ /*
+ 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))
{
- block->status|= BLOCK_ERROR;
- if (!last_errno)
- last_errno= errno ? errno : -1;
+ /* 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= my_pwrite(file,
+ block->buffer+block->offset,
+ block->length - block->offset,
+ block->hash_link->diskpos+ block->offset,
+ MYF(MY_NABP | MY_WAIT_IF_FULL));
+ 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.
*/
- if (block->wqueue[COND_FOR_SAVED].last_thread)
- release_queue(&block->wqueue[COND_FOR_SAVED]);
+ release_whole_queue(&block->wqueue[COND_FOR_SAVED]);
/* type will never be FLUSH_IGNORE_CHANGED here */
- if (! (type == FLUSH_KEEP || type == FLUSH_FORCE_WRITE))
+ if (!(type == FLUSH_KEEP || type == FLUSH_FORCE_WRITE) &&
+ !(block->status & (BLOCK_IN_EVICTION | BLOCK_IN_SWITCH |
+ BLOCK_FOR_UPDATE)))
{
- keycache->blocks_changed--;
- keycache->global_blocks_changed--;
+ /*
+ Note that a request has been registered against the block in
+ flush_key_blocks_int().
+ */
free_block(keycache, block);
}
else
{
- block->status&= ~BLOCK_IN_FLUSH;
- link_to_file_list(keycache, block, file, 1);
+ /*
+ 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
+ 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
@@ -2329,6 +3528,10 @@ static int flush_cached_blocks(KEY_CACHE *keycache,
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
@@ -2339,6 +3542,7 @@ static int flush_key_blocks_int(KEY_CACHE *keycache,
{
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));
@@ -2354,9 +3558,11 @@ static int flush_key_blocks_int(KEY_CACHE *keycache,
{
/* Key cache exists and flush is not disabled */
int error= 0;
- uint count= 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;
@@ -2368,28 +3574,39 @@ static int flush_key_blocks_int(KEY_CACHE *keycache,
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)
+ 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 */
- if (count > FLUSH_CACHE &&
+ /*
+ 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 ;
@@ -2402,121 +3619,350 @@ restart:
next= block->next_changed;
if (block->hash_link->file == file)
{
- /*
- 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 ot flushing dirty file pages
- */
- block->status|= BLOCK_IN_FLUSH;
-
- if (! (block->status & BLOCK_IN_SWITCH))
+ if (!(block->status & (BLOCK_IN_FLUSH | BLOCK_FOR_UPDATE)))
{
- /*
- We care only for the blocks for which flushing was not
- initiated by other threads as a result of page swapping
+ /*
+ 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.
*/
- reg_requests(keycache, block, 1);
- if (type != FLUSH_IGNORE_CHANGED)
+ if (!(block->status & BLOCK_IN_SWITCH))
{
- /* It's not a temporary file */
- if (pos == end)
+ /*
+ 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)
{
- /*
- This happens only if there is not enough
- memory for the big block
+ /* 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'.
*/
- if ((error= flush_cached_blocks(keycache, file, cache,
- end,type)))
- last_errno=error;
+ 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));
/*
- 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
+ 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.
*/
- goto restart;
+ 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);
+ }
}
- *pos++= block;
}
else
{
- /* It's a temporary file */
- keycache->blocks_changed--;
- keycache->global_blocks_changed--;
- free_block(keycache, block);
+ /*
+ 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
+ else if (type != FLUSH_KEEP)
{
- /* Link the block into a list of blocks 'in switch' */
- unlink_changed(block);
- link_changed(block, &first_in_switch);
+ /*
+ 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;
}
- /* Wait until list of blocks in switch is empty */
+ 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
- block= first_in_switch;
- {
-#ifdef THREAD
- struct st_my_thread_var *thread= my_thread_var;
- add_to_queue(&block->wqueue[COND_FOR_SAVED], thread);
- do
- {
- KEYCACHE_DBUG_PRINT("flush_key_blocks_int: wait",
- ("suspend thread %ld", thread->id));
- keycache_pthread_cond_wait(&thread->suspend,
- &keycache->cache_lock);
- }
- while (thread->next);
-#else
- KEYCACHE_DBUG_ASSERT(0);
- /* No parallel requests in single-threaded case */
-#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.
+ */
}
- /* The following happens very seldom */
+
if (! (type == FLUSH_KEEP || type == FLUSH_FORCE_WRITE))
{
-#if defined(KEYCACHE_DEBUG)
- cnt=0;
-#endif
- for (block= keycache->file_blocks[FILE_HASH(file)] ;
- block ;
- block= next)
+ 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
{
-#if defined(KEYCACHE_DEBUG)
- cnt++;
- KEYCACHE_DBUG_ASSERT(cnt <= keycache->blocks_used);
-#endif
- next= block->next_changed;
- if (block->hash_link->file == file &&
- (! (block->status & BLOCK_CHANGED)
- || type == FLUSH_IGNORE_CHANGED))
+ found= 0;
+ for (block= keycache->file_blocks[FILE_HASH(file)] ;
+ block ;
+ block= next)
{
- reg_requests(keycache, block, 1);
- free_block(keycache, block);
- }
+ /* 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((gptr) cache, MYF(0));
if (last_errno)
@@ -2543,51 +3989,157 @@ restart:
int flush_key_blocks(KEY_CACHE *keycache,
File file, enum flush_type type)
{
- int res;
+ int res= 0;
DBUG_ENTER("flush_key_blocks");
DBUG_PRINT("enter", ("keycache: 0x%lx", (long) keycache));
- if (keycache->disk_blocks <= 0)
+ if (!keycache->key_cache_inited)
DBUG_RETURN(0);
+
keycache_pthread_mutex_lock(&keycache->cache_lock);
- inc_counter_for_resize_op(keycache);
- res= flush_key_blocks_int(keycache, file, type);
- dec_counter_for_resize_op(keycache);
+ /* 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
+ 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)
{
-#if defined(KEYCACHE_DEBUG)
- uint cnt=0;
-#endif
- while (keycache->blocks_changed > 0)
+ BLOCK_LINK *block;
+ uint total_found;
+ uint found;
+ uint idx;
+ DBUG_ENTER("flush_all_key_blocks");
+
+ do
{
- BLOCK_LINK *block;
- for (block= keycache->used_last->next_used ; ; block=block->next_used)
+ 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
{
- if (block->hash_link)
+ found= 0;
+ /* Step over the whole changed_blocks hash array. */
+ for (idx= 0; idx < CHANGED_BLOCKS_HASH; idx++)
{
-#if defined(KEYCACHE_DEBUG)
- cnt++;
- KEYCACHE_DBUG_ASSERT(cnt <= keycache->blocks_used);
-#endif
- if (flush_key_blocks_int(keycache, block->hash_link->file,
- FLUSH_RELEASE))
- return 1;
- break;
+ /*
+ 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);
+ }
}
- if (block == keycache->used_last)
- break;
- }
+
+ } 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]);
}
- return 0;
+#endif
+
+ DBUG_RETURN(0);
}
@@ -2856,3 +4408,70 @@ void keycache_debug_log_close(void)
#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
+
diff --git a/mysys/mf_pack.c b/mysys/mf_pack.c
index d14c24f35d7..1b83c6fda10 100644
--- a/mysys/mf_pack.c
+++ b/mysys/mf_pack.c
@@ -106,16 +106,27 @@ void pack_dirname(my_string to, const char *from)
} /* pack_dirname */
- /* remove unwanted chars from dirname */
- /* if "/../" removes prev dir; "/~/" removes all before ~ */
- /* "//" is same as "/", except on Win32 at start of a file */
- /* "/./" is removed */
- /* Unpacks home_dir if "~/.." used */
- /* Unpacks current dir if if "./.." used */
+/*
+ remove unwanted chars from dirname
-uint cleanup_dirname(register my_string to, const char *from)
- /* to may be == from */
+ SYNOPSIS
+ cleanup_dirname()
+ to Store result here
+ from Dirname to fix. May be same as to
+
+ IMPLEMENTATION
+ "/../" removes prev dir
+ "/~/" removes all before ~
+ //" is same as "/", except on Win32 at start of a file
+ "/./" is removed
+ Unpacks home_dir if "~/.." used
+ Unpacks current dir if if "./.." used
+ RETURN
+ # length of new name
+*/
+
+uint cleanup_dirname(register my_string to, const char *from)
{
reg5 uint length;
reg2 my_string pos;
diff --git a/mysys/mf_path.c b/mysys/mf_path.c
index 9af6173b417..00e6b2046b9 100644
--- a/mysys/mf_path.c
+++ b/mysys/mf_path.c
@@ -72,7 +72,7 @@ my_string my_path(my_string to, const char *progname,
/* test if file without filename is found in path */
/* Returns to if found and to has dirpart if found, else NullS */
-#if defined(MSDOS) || defined(__WIN__) || defined(__EMX__) || defined(OS2)
+#if defined(__WIN__)
#define F_OK 0
#define PATH_SEP ';'
#define PROGRAM_EXTENSION ".exe"
diff --git a/mysys/mf_tempdir.c b/mysys/mf_tempdir.c
index bcd003920f1..36eecbeac09 100644
--- a/mysys/mf_tempdir.c
+++ b/mysys/mf_tempdir.c
@@ -16,7 +16,7 @@
#include "mysys_priv.h"
#include <m_string.h>
-#if defined( __WIN__) || defined(OS2) || defined(__NETWARE__)
+#if defined( __WIN__) || defined(__NETWARE__)
#define DELIM ';'
#else
#define DELIM ':'
@@ -26,14 +26,17 @@ my_bool init_tmpdir(MY_TMPDIR *tmpdir, const char *pathlist)
{
char *end, *copy;
char buff[FN_REFLEN];
+ DBUG_ENTER("init_tmpdir");
+ DBUG_PRINT("enter", ("pathlist: %s", pathlist ? pathlist : "NULL"));
+
pthread_mutex_init(&tmpdir->mutex, MY_MUTEX_INIT_FAST);
if (my_init_dynamic_array(&tmpdir->full_list, sizeof(char*), 1, 5))
- return TRUE;
+ goto err;
if (!pathlist || !pathlist[0])
{
/* Get default temporary directory */
pathlist=getenv("TMPDIR"); /* Use this if possible */
-#if defined( __WIN__) || defined(OS2) || defined(__NETWARE__)
+#if defined( __WIN__) || defined(__NETWARE__)
if (!pathlist)
pathlist=getenv("TEMP");
if (!pathlist)
@@ -44,12 +47,13 @@ my_bool init_tmpdir(MY_TMPDIR *tmpdir, const char *pathlist)
}
do
{
+ uint length;
end=strcend(pathlist, DELIM);
- convert_dirname(buff, pathlist, end);
- if (!(copy=my_strdup(buff, MYF(MY_WME))))
- return TRUE;
- if (insert_dynamic(&tmpdir->full_list, (gptr)&copy))
- return TRUE;
+ strmake(buff, pathlist, (uint) (end-pathlist));
+ length= cleanup_dirname(buff, buff);
+ if (!(copy= my_strndup(buff, length, MYF(MY_WME))) ||
+ insert_dynamic(&tmpdir->full_list, (gptr) &copy))
+ DBUG_RETURN(TRUE);
pathlist=end+1;
}
while (*end);
@@ -57,12 +61,20 @@ my_bool init_tmpdir(MY_TMPDIR *tmpdir, const char *pathlist)
tmpdir->list=(char **)tmpdir->full_list.buffer;
tmpdir->max=tmpdir->full_list.elements-1;
tmpdir->cur=0;
- return FALSE;
+ DBUG_RETURN(FALSE);
+
+err:
+ delete_dynamic(&tmpdir->full_list); /* Safe to free */
+ pthread_mutex_destroy(&tmpdir->mutex);
+ DBUG_RETURN(TRUE);
}
+
char *my_tmpdir(MY_TMPDIR *tmpdir)
{
char *dir;
+ if (!tmpdir->max)
+ return tmpdir->list[0];
pthread_mutex_lock(&tmpdir->mutex);
dir=tmpdir->list[tmpdir->cur];
tmpdir->cur= (tmpdir->cur == tmpdir->max) ? 0 : tmpdir->cur+1;
diff --git a/mysys/my_access.c b/mysys/my_access.c
index 9ee20cc942b..210946d50a8 100644
--- a/mysys/my_access.c
+++ b/mysys/my_access.c
@@ -53,24 +53,111 @@ int my_access(const char *path, int amode)
#endif /* __WIN__ */
-#if defined(MSDOS) || defined(__WIN__) || defined(__EMX__)
/*
List of file names that causes problem on windows
NOTE that one can also not have file names of type CON.TXT
+
+ NOTE: it is important to keep "CLOCK$" on the first place,
+ we skip it in check_if_legal_tablename.
*/
-
static const char *reserved_names[]=
{
- "CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6",
- "COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6",
- "LPT7", "LPT8", "LPT9", "CLOCK$",
+ "CLOCK$",
+ "CON", "PRN", "AUX", "NUL",
+ "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
+ "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9",
NullS
};
#define MAX_RESERVED_NAME_LENGTH 6
+
+/*
+ Looks up a null-terminated string in a list,
+ case insensitively.
+
+ SYNOPSIS
+ str_list_find()
+ list list of items
+ str item to find
+
+ RETURN
+ 0 ok
+ 1 reserved file name
+*/
+static int str_list_find(const char **list, const char *str)
+{
+ const char **name;
+ for (name= list; *name; name++)
+ {
+ if (!my_strcasecmp(&my_charset_latin1, *name, str))
+ return 1;
+ }
+ return 0;
+}
+
+
+/*
+ A map for faster reserved_names lookup,
+ helps to avoid loops in many cases.
+ 1 - can be the first letter
+ 2 - can be the second letter
+ 4 - can be the third letter
+*/
+static char reserved_map[256]=
+{
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ................ */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ................ */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* !"#$%&'()*+,-./ */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0123456789:;<=>? */
+ 0,1,0,1,0,0,0,0,0,0,0,0,7,4,5,2, /* @ABCDEFGHIJKLMNO */
+ 3,0,2,0,4,2,0,0,4,0,0,0,0,0,0,0, /* PQRSTUVWXYZ[\]^_ */
+ 0,1,0,1,0,0,0,0,0,0,0,0,7,4,5,2, /* bcdefghijklmno */
+ 3,0,2,0,4,2,0,0,4,0,0,0,0,0,0,0, /* pqrstuvwxyz{|}~. */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ................ */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ................ */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ................ */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ................ */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ................ */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ................ */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ................ */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* ................ */
+};
+
+
+/*
+ Check if a table name may cause problems
+
+ SYNOPSIS
+ check_if_legal_tablename
+ name Table name (without any extensions)
+
+ DESCRIPTION
+ We don't check 'CLOCK$' because dollar sign is encoded as @0024,
+ making table file name 'CLOCK@0024', which is safe.
+ This is why we start lookup from the second element
+ (i.e. &reserver_name[1])
+
+ RETURN
+ 0 ok
+ 1 reserved file name
+*/
+
+int check_if_legal_tablename(const char *name)
+{
+ DBUG_ENTER("check_if_legal_tablename");
+ DBUG_RETURN((reserved_map[(uchar) name[0]] & 1) &&
+ (reserved_map[(uchar) name[1]] & 2) &&
+ (reserved_map[(uchar) name[2]] & 4) &&
+ str_list_find(&reserved_names[1], name));
+}
+
+
+#if defined(__WIN__) || defined(__EMX__)
+
+
/*
Check if a path will access a reserverd file name that may cause problems
@@ -110,12 +197,5 @@ int check_if_legal_filename(const char *path)
}
DBUG_RETURN(0);
}
-#endif
-
-#ifdef OS2
-int check_if_legal_filename(const char *path)
-{
- return 0;
-}
-#endif /* OS2 */
+#endif /* defined(__WIN__) || defined(__EMX__) */
diff --git a/mysys/my_alloc.c b/mysys/my_alloc.c
index 5983a29a3e1..b4ebaeba647 100644
--- a/mysys/my_alloc.c
+++ b/mysys/my_alloc.c
@@ -334,8 +334,6 @@ void free_root(MEM_ROOT *root, myf MyFlags)
DBUG_ENTER("free_root");
DBUG_PRINT("enter",("root: 0x%lx flags: %u", (long) root, (uint) MyFlags));
- if (!root) /* QQ: Should be deleted */
- DBUG_VOID_RETURN; /* purecov: inspected */
if (MyFlags & MY_MARK_BLOCKS_FREE)
{
mark_blocks_free(root);
@@ -400,6 +398,7 @@ char *strdup_root(MEM_ROOT *root,const char *str)
return strmake_root(root, str, (uint) strlen(str));
}
+
char *strmake_root(MEM_ROOT *root,const char *str, uint len)
{
char *pos;
diff --git a/mysys/my_append.c b/mysys/my_append.c
index 274f2110575..ddd3c91e832 100644
--- a/mysys/my_append.c
+++ b/mysys/my_append.c
@@ -13,7 +13,6 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-#define USES_TYPES /* sys/types is included */
#include "mysys_priv.h"
#include <sys/stat.h>
#include <m_string.h>
diff --git a/mysys/my_atomic.c b/mysys/my_atomic.c
new file mode 100644
index 00000000000..6a30267eb80
--- /dev/null
+++ b/mysys/my_atomic.c
@@ -0,0 +1,46 @@
+/* Copyright (C) 2006 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; 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 */
+
+#include <my_global.h>
+#include <my_pthread.h>
+
+#ifndef HAVE_INLINE
+/*
+ the following will cause all inline functions to be instantiated
+*/
+#define HAVE_INLINE
+#define static extern
+#endif
+
+#include <my_atomic.h>
+
+/*
+ checks that the current build of atomic ops
+ can run on this machine
+
+ RETURN
+ ATOMIC_xxx values, see my_atomic.h
+*/
+int my_atomic_initialize()
+{
+ DBUG_ASSERT(sizeof(intptr) == sizeof(void *));
+ /* currently the only thing worth checking is SMP/UP issue */
+#ifdef MY_ATOMIC_MODE_DUMMY
+ return my_getncpus() == 1 ? MY_ATOMIC_OK : MY_ATOMIC_NOT_1CPU;
+#else
+ return MY_ATOMIC_OK;
+#endif
+}
+
diff --git a/mysys/my_bit.c b/mysys/my_bit.c
index 81d63246e7a..5a9b1187c83 100644
--- a/mysys/my_bit.c
+++ b/mysys/my_bit.c
@@ -75,3 +75,33 @@ uint my_count_bits_ushort(ushort v)
return nbits[v];
}
+
+/*
+ Next highest power of two
+
+ SYNOPSIS
+ my_round_up_to_next_power()
+ v Value to check
+
+ RETURN
+ Next or equal power of 2
+ Note: 0 will return 0
+
+ NOTES
+ Algorithm by Sean Anderson, according to:
+ http://graphics.stanford.edu/~seander/bithacks.html
+ (Orignal code public domain)
+
+ Comments shows how this works with 01100000000000000000000000001011
+*/
+
+uint32 my_round_up_to_next_power(uint32 v)
+{
+ v--; /* 01100000000000000000000000001010 */
+ v|= v >> 1; /* 01110000000000000000000000001111 */
+ v|= v >> 2; /* 01111100000000000000000000001111 */
+ v|= v >> 4; /* 01111111110000000000000000001111 */
+ v|= v >> 8; /* 01111111111111111100000000001111 */
+ v|= v >> 16; /* 01111111111111111111111111111111 */
+ return v+1; /* 10000000000000000000000000000000 */
+}
diff --git a/mysys/my_bitmap.c b/mysys/my_bitmap.c
index beb67bb419b..10eff40b9ed 100644
--- a/mysys/my_bitmap.c
+++ b/mysys/my_bitmap.c
@@ -19,26 +19,69 @@
API limitations (or, rather asserted safety assumptions,
to encourage correct programming)
- * the size of the used bitmap is less than ~(uint) 0
- * it's a multiple of 8 (for efficiency reasons)
- * when arguments are a bitmap and a bit number, the number
- must be within bitmap size
- * bitmap_set_prefix() is an exception - one can use ~0 to set all bits
- * when both arguments are bitmaps, they must be of the same size
- * bitmap_intersect() is an exception :)
- (for for Bitmap::intersect(ulonglong map2buff))
-
- If THREAD is defined all bitmap operations except bitmap_init/bitmap_free
- are thread-safe.
+ * the internal size is a set of 32 bit words
+ * the number of bits specified in creation can be any number > 0
+ * there are THREAD safe versions of most calls called bitmap_lock_*
+ many of those are not used and not compiled normally but the code
+ already exist for them in an #ifdef:ed part. These can only be used
+ if THREAD was specified in bitmap_init
TODO:
Make assembler THREAD safe versions of these using test-and-set instructions
+
+ Original version created by Sergei Golubchik 2001 - 2004.
+ New version written and test program added and some changes to the interface
+ was made by Mikael Ronström 2005, with assistance of Tomas Ulin and Mats
+ Kindahl.
*/
#include "mysys_priv.h"
#include <my_bitmap.h>
#include <m_string.h>
+void create_last_word_mask(MY_BITMAP *map)
+{
+ /* Get the number of used bits (1..8) in the last byte */
+ unsigned int const used= 1U + ((map->n_bits-1U) & 0x7U);
+
+ /*
+ Create a mask with the upper 'unused' bits set and the lower 'used'
+ bits clear. The bits within each byte is stored in big-endian order.
+ */
+ unsigned char const mask= (~((1 << used) - 1)) & 255;
+
+ /*
+ The first bytes are to be set to zero since they represent real bits
+ in the bitvector. The last bytes are set to 0xFF since they represent
+ bytes not used by the bitvector. Finally the last byte contains bits
+ as set by the mask above.
+ */
+ unsigned char *ptr= (unsigned char*)&map->last_word_mask;
+
+ map->last_word_ptr= map->bitmap + no_words_in_map(map)-1;
+ switch (no_bytes_in_map(map) & 3) {
+ case 1:
+ map->last_word_mask= ~0U;
+ ptr[0]= mask;
+ return;
+ case 2:
+ map->last_word_mask= ~0U;
+ ptr[0]= 0;
+ ptr[1]= mask;
+ return;
+ case 3:
+ map->last_word_mask= 0U;
+ ptr[2]= mask;
+ ptr[3]= 0xFFU;
+ return;
+ case 0:
+ map->last_word_mask= 0U;
+ ptr[3]= mask;
+ return;
+ }
+}
+
+
static inline void bitmap_lock(MY_BITMAP *map __attribute__((unused)))
{
#ifdef THREAD
@@ -56,29 +99,43 @@ static inline void bitmap_unlock(MY_BITMAP *map __attribute__((unused)))
}
-my_bool bitmap_init(MY_BITMAP *map, uchar *buf, uint bitmap_size,
- my_bool thread_safe)
+my_bool bitmap_init(MY_BITMAP *map, my_bitmap_map *buf, uint n_bits,
+ my_bool thread_safe __attribute__((unused)))
{
DBUG_ENTER("bitmap_init");
-
- DBUG_ASSERT((bitmap_size & 7) == 0);
- bitmap_size/=8;
- if (!(map->bitmap=buf) &&
- !(map->bitmap= (uchar*) my_malloc(bitmap_size +
- (thread_safe ?
- sizeof(pthread_mutex_t) : 0),
- MYF(MY_WME | MY_ZEROFILL))))
- DBUG_RETURN(1);
- map->bitmap_size=bitmap_size;
-#ifdef THREAD
- if (thread_safe)
+ if (!buf)
{
- map->mutex=(pthread_mutex_t *)(map->bitmap+bitmap_size);
- pthread_mutex_init(map->mutex, MY_MUTEX_INIT_FAST);
+ uint size_in_bytes= bitmap_buffer_size(n_bits);
+ uint extra= 0;
+#ifdef THREAD
+ if (thread_safe)
+ {
+ size_in_bytes= ALIGN_SIZE(size_in_bytes);
+ extra= sizeof(pthread_mutex_t);
+ }
+ map->mutex= 0;
+#endif
+ if (!(buf= (my_bitmap_map*) my_malloc(size_in_bytes+extra, MYF(MY_WME))))
+ DBUG_RETURN(1);
+#ifdef THREAD
+ if (thread_safe)
+ {
+ map->mutex= (pthread_mutex_t *) ((char*) buf + size_in_bytes);
+ pthread_mutex_init(map->mutex, MY_MUTEX_INIT_FAST);
+ }
+#endif
}
+#ifdef THREAD
else
- map->mutex=0;
+ {
+ DBUG_ASSERT(thread_safe == 0);
+ }
#endif
+
+ map->bitmap= buf;
+ map->n_bits= n_bits;
+ create_last_word_mask(map);
+ bitmap_clear_all(map);
DBUG_RETURN(0);
}
@@ -99,15 +156,6 @@ void bitmap_free(MY_BITMAP *map)
}
-void bitmap_set_bit(MY_BITMAP *map, uint bitmap_bit)
-{
- DBUG_ASSERT(map->bitmap && bitmap_bit < map->bitmap_size*8);
- bitmap_lock(map);
- bitmap_fast_set_bit(map, bitmap_bit);
- bitmap_unlock(map);
-}
-
-
/*
test if bit already set and set it if it was not (thread unsafe method)
@@ -123,7 +171,7 @@ void bitmap_set_bit(MY_BITMAP *map, uint bitmap_bit)
my_bool bitmap_fast_test_and_set(MY_BITMAP *map, uint bitmap_bit)
{
- uchar *value= map->bitmap + (bitmap_bit / 8);
+ uchar *value= ((uchar*) map->bitmap) + (bitmap_bit / 8);
uchar bit= 1 << ((bitmap_bit) & 7);
uchar res= (*value) & bit;
*value|= bit;
@@ -147,182 +195,177 @@ my_bool bitmap_fast_test_and_set(MY_BITMAP *map, uint bitmap_bit)
my_bool bitmap_test_and_set(MY_BITMAP *map, uint bitmap_bit)
{
my_bool res;
- DBUG_ASSERT(map->bitmap && bitmap_bit < map->bitmap_size*8);
+ DBUG_ASSERT(map->bitmap && bitmap_bit < map->n_bits);
bitmap_lock(map);
res= bitmap_fast_test_and_set(map, bitmap_bit);
bitmap_unlock(map);
return res;
}
-uint bitmap_set_next(MY_BITMAP *map)
-{
- uchar *bitmap=map->bitmap;
- uint bit_found = MY_BIT_NONE;
- uint bitmap_size=map->bitmap_size;
- uint i;
+/*
+ test if bit already set and clear it if it was set(thread unsafe method)
- DBUG_ASSERT(map->bitmap);
- bitmap_lock(map);
- for (i=0; i < bitmap_size ; i++, bitmap++)
- {
- if (*bitmap != 0xff)
- { /* Found slot with free bit */
- uint b;
- for (b=0; ; b++)
- {
- if (!(*bitmap & (1 << b)))
- {
- *bitmap |= 1<<b;
- bit_found = (i*8)+b;
- break;
- }
- }
- break; /* Found bit */
- }
- }
- bitmap_unlock(map);
- return bit_found;
-}
+ SYNOPSIS
+ bitmap_fast_test_and_set()
+ MAP bit map struct
+ BIT bit number
+ RETURN
+ 0 bit was not set
+ !=0 bit was set
+*/
-void bitmap_clear_bit(MY_BITMAP *map, uint bitmap_bit)
+my_bool bitmap_fast_test_and_clear(MY_BITMAP *map, uint bitmap_bit)
{
- DBUG_ASSERT(map->bitmap && bitmap_bit < map->bitmap_size*8);
- bitmap_lock(map);
- bitmap_fast_clear_bit(map, bitmap_bit);
- bitmap_unlock(map);
+ uchar *byte= (uchar*) map->bitmap + (bitmap_bit / 8);
+ uchar bit= 1 << ((bitmap_bit) & 7);
+ uchar res= (*byte) & bit;
+ *byte&= ~bit;
+ return res;
}
-void bitmap_set_prefix(MY_BITMAP *map, uint prefix_size)
+my_bool bitmap_test_and_clear(MY_BITMAP *map, uint bitmap_bit)
{
- uint prefix_bytes, prefix_bits;
-
- DBUG_ASSERT(map->bitmap &&
- (prefix_size <= map->bitmap_size*8 || prefix_size == (uint) ~0));
+ my_bool res;
+ DBUG_ASSERT(map->bitmap && bitmap_bit < map->n_bits);
bitmap_lock(map);
- set_if_smaller(prefix_size, map->bitmap_size*8);
- if ((prefix_bytes= prefix_size / 8))
- memset(map->bitmap, 0xff, prefix_bytes);
- if ((prefix_bits= prefix_size & 7))
- map->bitmap[prefix_bytes++]= (1 << prefix_bits)-1;
- if (prefix_bytes < map->bitmap_size)
- bzero(map->bitmap+prefix_bytes, map->bitmap_size-prefix_bytes);
+ res= bitmap_fast_test_and_clear(map, bitmap_bit);
bitmap_unlock(map);
+ return res;
}
-void bitmap_clear_all(MY_BITMAP *map)
+uint bitmap_set_next(MY_BITMAP *map)
{
- bitmap_set_prefix(map, 0);
+ uint bit_found;
+ DBUG_ASSERT(map->bitmap);
+ if ((bit_found= bitmap_get_first(map)) != MY_BIT_NONE)
+ bitmap_set_bit(map, bit_found);
+ return bit_found;
}
-void bitmap_set_all(MY_BITMAP *map)
+void bitmap_set_prefix(MY_BITMAP *map, uint prefix_size)
{
- bitmap_set_prefix(map, ~0);
+ uint prefix_bytes, prefix_bits, d;
+ uchar *m= (uchar *)map->bitmap;
+
+ DBUG_ASSERT(map->bitmap &&
+ (prefix_size <= map->n_bits || prefix_size == (uint) ~0));
+ set_if_smaller(prefix_size, map->n_bits);
+ if ((prefix_bytes= prefix_size / 8))
+ memset(m, 0xff, prefix_bytes);
+ m+= prefix_bytes;
+ if ((prefix_bits= prefix_size & 7))
+ *m++= (1 << prefix_bits)-1;
+ if ((d= no_bytes_in_map(map)-prefix_bytes))
+ bzero(m, d);
}
my_bool bitmap_is_prefix(const MY_BITMAP *map, uint prefix_size)
{
- uint prefix_bits= prefix_size & 7, res= 0;
- uchar *m= map->bitmap, *end_prefix= map->bitmap+prefix_size/8,
- *end= map->bitmap+map->bitmap_size;
-
- DBUG_ASSERT(map->bitmap && prefix_size <= map->bitmap_size*8);
+ uint prefix_bits= prefix_size & 0x7, res;
+ uchar *m= (uchar*)map->bitmap;
+ uchar *end_prefix= m+prefix_size/8;
+ uchar *end;
+ DBUG_ASSERT(m && prefix_size <= map->n_bits);
+ end= m+no_bytes_in_map(map);
- bitmap_lock((MY_BITMAP *)map);
while (m < end_prefix)
if (*m++ != 0xff)
- goto ret;
+ return 0;
+ *map->last_word_ptr&= ~map->last_word_mask; /*Clear bits*/
+ res= 0;
if (prefix_bits && *m++ != (1 << prefix_bits)-1)
goto ret;
while (m < end)
if (*m++ != 0)
goto ret;
-
- res=1;
+ res= 1;
ret:
- bitmap_unlock((MY_BITMAP *)map);
- return res;
+ return res;
}
-my_bool bitmap_is_clear_all(const MY_BITMAP *map)
-{
- return bitmap_is_prefix(map, 0);
-}
-
my_bool bitmap_is_set_all(const MY_BITMAP *map)
{
- return bitmap_is_prefix(map, map->bitmap_size*8);
+ my_bitmap_map *data_ptr= map->bitmap;
+ my_bitmap_map *end= map->last_word_ptr;
+ *map->last_word_ptr |= map->last_word_mask;
+ for (; data_ptr <= end; data_ptr++)
+ if (*data_ptr != 0xFFFFFFFF)
+ return FALSE;
+ return TRUE;
}
-my_bool bitmap_is_set(const MY_BITMAP *map, uint bitmap_bit)
+my_bool bitmap_is_clear_all(const MY_BITMAP *map)
{
- DBUG_ASSERT(map->bitmap && bitmap_bit < map->bitmap_size*8);
- return bitmap_fast_is_set(map, bitmap_bit);
+ my_bitmap_map *data_ptr= map->bitmap;
+ my_bitmap_map *end;
+ if (*map->last_word_ptr & ~map->last_word_mask)
+ return FALSE;
+ end= map->last_word_ptr;
+ for (; data_ptr < end; data_ptr++)
+ if (*data_ptr)
+ return FALSE;
+ return TRUE;
}
+/* Return TRUE if map1 is a subset of map2 */
my_bool bitmap_is_subset(const MY_BITMAP *map1, const MY_BITMAP *map2)
{
- uint res=0;
- uchar *m1=map1->bitmap, *m2=map2->bitmap, *end;
+ my_bitmap_map *m1= map1->bitmap, *m2= map2->bitmap, *end;
DBUG_ASSERT(map1->bitmap && map2->bitmap &&
- map1->bitmap_size==map2->bitmap_size);
- bitmap_lock((MY_BITMAP *)map1);
- bitmap_lock((MY_BITMAP *)map2);
+ map1->n_bits==map2->n_bits);
- end= m1+map1->bitmap_size;
-
- while (m1 < end)
+ end= map1->last_word_ptr;
+ *map1->last_word_ptr &= ~map1->last_word_mask;
+ *map2->last_word_ptr &= ~map2->last_word_mask;
+ while (m1 <= end)
{
if ((*m1++) & ~(*m2++))
- goto ret;
+ return 0;
}
-
- res=1;
-ret:
- bitmap_unlock((MY_BITMAP *)map2);
- bitmap_unlock((MY_BITMAP *)map1);
- return res;
+ return 1;
}
+/* True if bitmaps has any common bits */
-my_bool bitmap_cmp(const MY_BITMAP *map1, const MY_BITMAP *map2)
+my_bool bitmap_is_overlapping(const MY_BITMAP *map1, const MY_BITMAP *map2)
{
- uint res;
+ my_bitmap_map *m1= map1->bitmap, *m2= map2->bitmap, *end;
DBUG_ASSERT(map1->bitmap && map2->bitmap &&
- map1->bitmap_size==map2->bitmap_size);
- bitmap_lock((MY_BITMAP *)map1);
- bitmap_lock((MY_BITMAP *)map2);
-
- res= memcmp(map1->bitmap, map2->bitmap, map1->bitmap_size)==0;
+ map1->n_bits==map2->n_bits);
- bitmap_unlock((MY_BITMAP *)map2);
- bitmap_unlock((MY_BITMAP *)map1);
- return res;
+ end= map1->last_word_ptr;
+ *map1->last_word_ptr &= ~map1->last_word_mask;
+ *map2->last_word_ptr &= ~map2->last_word_mask;
+ while (m1 <= end)
+ {
+ if ((*m1++) & (*m2++))
+ return 1;
+ }
+ return 0;
}
void bitmap_intersect(MY_BITMAP *map, const MY_BITMAP *map2)
{
- uchar *to=map->bitmap, *from=map2->bitmap, *end;
- uint len=map->bitmap_size, len2=map2->bitmap_size;
+ my_bitmap_map *to= map->bitmap, *from= map2->bitmap, *end;
+ uint len= no_words_in_map(map), len2 = no_words_in_map(map2);
DBUG_ASSERT(map->bitmap && map2->bitmap);
- bitmap_lock(map);
- bitmap_lock((MY_BITMAP *)map2);
end= to+min(len,len2);
-
+ *map2->last_word_ptr&= ~map2->last_word_mask; /*Clear last bits in map2*/
while (to < end)
*to++ &= *from++;
@@ -332,9 +375,6 @@ void bitmap_intersect(MY_BITMAP *map, const MY_BITMAP *map2)
while (to < end)
*to++=0;
}
-
- bitmap_unlock((MY_BITMAP *)map2);
- bitmap_unlock(map);
}
@@ -361,8 +401,8 @@ void bitmap_intersect(MY_BITMAP *map, const MY_BITMAP *map2)
void bitmap_set_above(MY_BITMAP *map, uint from_byte, uint use_bit)
{
uchar use_byte= use_bit ? 0xff : 0;
- uchar *to= map->bitmap + from_byte;
- uchar *end= map->bitmap + map->bitmap_size;
+ uchar *to= (uchar *)map->bitmap + from_byte;
+ uchar *end= (uchar *)map->bitmap + (map->n_bits+7)/8;
while (to < end)
*to++= use_byte;
@@ -371,37 +411,283 @@ void bitmap_set_above(MY_BITMAP *map, uint from_byte, uint use_bit)
void bitmap_subtract(MY_BITMAP *map, const MY_BITMAP *map2)
{
- uchar *to=map->bitmap, *from=map2->bitmap, *end;
+ my_bitmap_map *to= map->bitmap, *from= map2->bitmap, *end;
+ DBUG_ASSERT(map->bitmap && map2->bitmap &&
+ map->n_bits==map2->n_bits);
+
+ end= map->last_word_ptr;
+
+ while (to <= end)
+ *to++ &= ~(*from++);
+}
+
+
+void bitmap_union(MY_BITMAP *map, const MY_BITMAP *map2)
+{
+ my_bitmap_map *to= map->bitmap, *from= map2->bitmap, *end;
+
+ DBUG_ASSERT(map->bitmap && map2->bitmap &&
+ map->n_bits==map2->n_bits);
+ end= map->last_word_ptr;
+
+ while (to <= end)
+ *to++ |= *from++;
+}
+
+
+void bitmap_xor(MY_BITMAP *map, const MY_BITMAP *map2)
+{
+ my_bitmap_map *to= map->bitmap, *from= map2->bitmap, *end= map->last_word_ptr;
+ DBUG_ASSERT(map->bitmap && map2->bitmap &&
+ map->n_bits==map2->n_bits);
+ while (to <= end)
+ *to++ ^= *from++;
+}
+
+
+void bitmap_invert(MY_BITMAP *map)
+{
+ my_bitmap_map *to= map->bitmap, *end;
+
+ DBUG_ASSERT(map->bitmap);
+ end= map->last_word_ptr;
+
+ while (to <= end)
+ *to++ ^= 0xFFFFFFFF;
+}
+
+
+uint bitmap_bits_set(const MY_BITMAP *map)
+{
+ uchar *m= (uchar*)map->bitmap;
+ uchar *end= m + no_bytes_in_map(map);
+ uint res= 0;
+
+ DBUG_ASSERT(map->bitmap);
+ *map->last_word_ptr&= ~map->last_word_mask; /*Reset last bits to zero*/
+ while (m < end)
+ res+= my_count_bits_ushort(*m++);
+ return res;
+}
+
+
+void bitmap_copy(MY_BITMAP *map, const MY_BITMAP *map2)
+{
+ my_bitmap_map *to= map->bitmap, *from= map2->bitmap, *end;
DBUG_ASSERT(map->bitmap && map2->bitmap &&
- map->bitmap_size==map2->bitmap_size);
+ map->n_bits==map2->n_bits);
+ end= map->last_word_ptr;
+ while (to <= end)
+ *to++ = *from++;
+}
+
+
+uint bitmap_get_first_set(const MY_BITMAP *map)
+{
+ uchar *byte_ptr;
+ uint i,j,k;
+ my_bitmap_map *data_ptr, *end= map->last_word_ptr;
+
+ DBUG_ASSERT(map->bitmap);
+ data_ptr= map->bitmap;
+ *map->last_word_ptr &= ~map->last_word_mask;
+
+ for (i=0; data_ptr <= end; data_ptr++, i++)
+ {
+ if (*data_ptr)
+ {
+ byte_ptr= (uchar*)data_ptr;
+ for (j=0; ; j++, byte_ptr++)
+ {
+ if (*byte_ptr)
+ {
+ for (k=0; ; k++)
+ {
+ if (*byte_ptr & (1 << k))
+ return (i*32) + (j*8) + k;
+ }
+ DBUG_ASSERT(0);
+ }
+ }
+ DBUG_ASSERT(0);
+ }
+ }
+ return MY_BIT_NONE;
+}
+
+
+uint bitmap_get_first(const MY_BITMAP *map)
+{
+ uchar *byte_ptr;
+ uint i,j,k;
+ my_bitmap_map *data_ptr, *end= map->last_word_ptr;
+
+ DBUG_ASSERT(map->bitmap);
+ data_ptr= map->bitmap;
+ *map->last_word_ptr|= map->last_word_mask;
+
+ for (i=0; data_ptr <= end; data_ptr++, i++)
+ {
+ if (*data_ptr != 0xFFFFFFFF)
+ {
+ byte_ptr= (uchar*)data_ptr;
+ for (j=0; ; j++, byte_ptr++)
+ {
+ if (*byte_ptr != 0xFF)
+ {
+ for (k=0; ; k++)
+ {
+ if (!(*byte_ptr & (1 << k)))
+ return (i*32) + (j*8) + k;
+ }
+ DBUG_ASSERT(0);
+ }
+ }
+ DBUG_ASSERT(0);
+ }
+ }
+ return MY_BIT_NONE;
+}
+
+
+uint bitmap_lock_set_next(MY_BITMAP *map)
+{
+ uint bit_found;
bitmap_lock(map);
- bitmap_lock((MY_BITMAP *)map2);
+ bit_found= bitmap_set_next(map);
+ bitmap_unlock(map);
+ return bit_found;
+}
- end= to+map->bitmap_size;
- while (to < end)
- *to++ &= ~(*from++);
+void bitmap_lock_clear_bit(MY_BITMAP *map, uint bitmap_bit)
+{
+ bitmap_lock(map);
+ DBUG_ASSERT(map->bitmap && bitmap_bit < map->n_bits);
+ bitmap_clear_bit(map, bitmap_bit);
+ bitmap_unlock(map);
+}
- bitmap_unlock((MY_BITMAP *)map2);
+
+#ifdef NOT_USED
+my_bool bitmap_lock_is_prefix(const MY_BITMAP *map, uint prefix_size)
+{
+ my_bool res;
+ bitmap_lock((MY_BITMAP *)map);
+ res= bitmap_is_prefix(map, prefix_size);
+ bitmap_unlock((MY_BITMAP *)map);
+ return res;
+}
+
+
+void bitmap_lock_set_all(MY_BITMAP *map)
+{
+ bitmap_lock(map);
+ bitmap_set_all(map);
bitmap_unlock(map);
}
-void bitmap_union(MY_BITMAP *map, const MY_BITMAP *map2)
+void bitmap_lock_clear_all(MY_BITMAP *map)
{
- uchar *to=map->bitmap, *from=map2->bitmap, *end;
+ bitmap_lock(map);
+ bitmap_clear_all(map);
+ bitmap_unlock(map);
+}
- DBUG_ASSERT(map->bitmap && map2->bitmap &&
- map->bitmap_size==map2->bitmap_size);
+
+void bitmap_lock_set_prefix(MY_BITMAP *map, uint prefix_size)
+{
+ bitmap_lock(map);
+ bitmap_set_prefix(map, prefix_size);
+ bitmap_unlock(map);
+}
+
+
+my_bool bitmap_lock_is_clear_all(const MY_BITMAP *map)
+{
+ uint res;
+ bitmap_lock((MY_BITMAP *)map);
+ res= bitmap_is_clear_all(map);
+ bitmap_unlock((MY_BITMAP *)map);
+ return res;
+}
+
+
+my_bool bitmap_lock_is_set_all(const MY_BITMAP *map)
+{
+ uint res;
+ bitmap_lock((MY_BITMAP *)map);
+ res= bitmap_is_set_all(map);
+ bitmap_unlock((MY_BITMAP *)map);
+ return res;
+}
+
+
+my_bool bitmap_lock_is_set(const MY_BITMAP *map, uint bitmap_bit)
+{
+ my_bool res;
+ DBUG_ASSERT(map->bitmap && bitmap_bit < map->n_bits);
+ bitmap_lock((MY_BITMAP *)map);
+ res= bitmap_is_set(map, bitmap_bit);
+ bitmap_unlock((MY_BITMAP *)map);
+ return res;
+}
+
+
+my_bool bitmap_lock_is_subset(const MY_BITMAP *map1, const MY_BITMAP *map2)
+{
+ uint res;
+ bitmap_lock((MY_BITMAP *)map1);
+ bitmap_lock((MY_BITMAP *)map2);
+ res= bitmap_is_subset(map1, map2);
+ bitmap_unlock((MY_BITMAP *)map2);
+ bitmap_unlock((MY_BITMAP *)map1);
+ return res;
+}
+
+
+my_bool bitmap_lock_cmp(const MY_BITMAP *map1, const MY_BITMAP *map2)
+{
+ uint res;
+
+ DBUG_ASSERT(map1->bitmap && map2->bitmap &&
+ map1->n_bits==map2->n_bits);
+ bitmap_lock((MY_BITMAP *)map1);
+ bitmap_lock((MY_BITMAP *)map2);
+ res= bitmap_cmp(map1, map2);
+ bitmap_unlock((MY_BITMAP *)map2);
+ bitmap_unlock((MY_BITMAP *)map1);
+ return res;
+}
+
+
+void bitmap_lock_intersect(MY_BITMAP *map, const MY_BITMAP *map2)
+{
bitmap_lock(map);
bitmap_lock((MY_BITMAP *)map2);
+ bitmap_intersect(map, map2);
+ bitmap_unlock((MY_BITMAP *)map2);
+ bitmap_unlock(map);
+}
- end= to+map->bitmap_size;
- while (to < end)
- *to++ |= *from++;
+void bitmap_lock_subtract(MY_BITMAP *map, const MY_BITMAP *map2)
+{
+ bitmap_lock(map);
+ bitmap_lock((MY_BITMAP *)map2);
+ bitmap_subtract(map, map2);
+ bitmap_unlock((MY_BITMAP *)map2);
+ bitmap_unlock(map);
+}
+
+void bitmap_lock_union(MY_BITMAP *map, const MY_BITMAP *map2)
+{
+ bitmap_lock(map);
+ bitmap_lock((MY_BITMAP *)map2);
+ bitmap_union(map, map2);
bitmap_unlock((MY_BITMAP *)map2);
bitmap_unlock(map);
}
@@ -414,19 +700,12 @@ void bitmap_union(MY_BITMAP *map, const MY_BITMAP *map2)
RETURN
Number of set bits in the bitmap.
*/
-
-uint bitmap_bits_set(const MY_BITMAP *map)
-{
- uchar *m= map->bitmap;
- uchar *end= m + map->bitmap_size;
- uint res= 0;
-
- DBUG_ASSERT(map->bitmap);
+uint bitmap_lock_bits_set(const MY_BITMAP *map)
+{
+ uint res;
bitmap_lock((MY_BITMAP *)map);
- while (m < end)
- {
- res+= my_count_bits_ushort(*m++);
- }
+ DBUG_ASSERT(map->bitmap);
+ res= bitmap_bits_set(map);
bitmap_unlock((MY_BITMAP *)map);
return res;
}
@@ -439,33 +718,412 @@ uint bitmap_bits_set(const MY_BITMAP *map)
RETURN
Number of first unset bit in the bitmap or MY_BIT_NONE if all bits are set.
*/
+uint bitmap_lock_get_first(const MY_BITMAP *map)
+{
+ uint res;
+ bitmap_lock((MY_BITMAP*)map);
+ res= bitmap_get_first(map);
+ bitmap_unlock((MY_BITMAP*)map);
+ return res;
+}
-uint bitmap_get_first(const MY_BITMAP *map)
+
+uint bitmap_lock_get_first_set(const MY_BITMAP *map)
+{
+ uint res;
+ bitmap_lock((MY_BITMAP*)map);
+ res= bitmap_get_first_set(map);
+ bitmap_unlock((MY_BITMAP*)map);
+ return res;
+}
+
+
+void bitmap_lock_set_bit(MY_BITMAP *map, uint bitmap_bit)
+{
+ DBUG_ASSERT(map->bitmap && bitmap_bit < map->n_bits);
+ bitmap_lock(map);
+ bitmap_set_bit(map, bitmap_bit);
+ bitmap_unlock(map);
+}
+
+
+void bitmap_lock_flip_bit(MY_BITMAP *map, uint bitmap_bit)
+{
+ DBUG_ASSERT(map->bitmap && bitmap_bit < map->n_bits);
+ bitmap_lock(map);
+ bitmap_flip_bit(map, bitmap_bit);
+ bitmap_unlock(map);
+}
+#endif
+#ifdef MAIN
+
+uint get_rand_bit(uint bitsize)
+{
+ return (rand() % bitsize);
+}
+
+bool test_set_get_clear_bit(MY_BITMAP *map, uint bitsize)
+{
+ uint i, test_bit;
+ uint no_loops= bitsize > 128 ? 128 : bitsize;
+ for (i=0; i < no_loops; i++)
+ {
+ test_bit= get_rand_bit(bitsize);
+ bitmap_set_bit(map, test_bit);
+ if (!bitmap_is_set(map, test_bit))
+ goto error1;
+ bitmap_clear_bit(map, test_bit);
+ if (bitmap_is_set(map, test_bit))
+ goto error2;
+ }
+ return FALSE;
+error1:
+ printf("Error in set bit, bit %u, bitsize = %u", test_bit, bitsize);
+ return TRUE;
+error2:
+ printf("Error in clear bit, bit %u, bitsize = %u", test_bit, bitsize);
+ return TRUE;
+}
+
+bool test_flip_bit(MY_BITMAP *map, uint bitsize)
+{
+ uint i, test_bit;
+ uint no_loops= bitsize > 128 ? 128 : bitsize;
+ for (i=0; i < no_loops; i++)
+ {
+ test_bit= get_rand_bit(bitsize);
+ bitmap_flip_bit(map, test_bit);
+ if (!bitmap_is_set(map, test_bit))
+ goto error1;
+ bitmap_flip_bit(map, test_bit);
+ if (bitmap_is_set(map, test_bit))
+ goto error2;
+ }
+ return FALSE;
+error1:
+ printf("Error in flip bit 1, bit %u, bitsize = %u", test_bit, bitsize);
+ return TRUE;
+error2:
+ printf("Error in flip bit 2, bit %u, bitsize = %u", test_bit, bitsize);
+ return TRUE;
+}
+
+bool test_operators(MY_BITMAP *map __attribute__((unused)),
+ uint bitsize __attribute__((unused)))
+{
+ return FALSE;
+}
+
+bool test_get_all_bits(MY_BITMAP *map, uint bitsize)
{
- uchar *bitmap=map->bitmap;
- uint bit_found = MY_BIT_NONE;
- uint bitmap_size=map->bitmap_size;
uint i;
+ bitmap_set_all(map);
+ if (!bitmap_is_set_all(map))
+ goto error1;
+ if (!bitmap_is_prefix(map, bitsize))
+ goto error5;
+ bitmap_clear_all(map);
+ if (!bitmap_is_clear_all(map))
+ goto error2;
+ if (!bitmap_is_prefix(map, 0))
+ goto error6;
+ for (i=0; i<bitsize;i++)
+ bitmap_set_bit(map, i);
+ if (!bitmap_is_set_all(map))
+ goto error3;
+ for (i=0; i<bitsize;i++)
+ bitmap_clear_bit(map, i);
+ if (!bitmap_is_clear_all(map))
+ goto error4;
+ return FALSE;
+error1:
+ printf("Error in set_all, bitsize = %u", bitsize);
+ return TRUE;
+error2:
+ printf("Error in clear_all, bitsize = %u", bitsize);
+ return TRUE;
+error3:
+ printf("Error in bitmap_is_set_all, bitsize = %u", bitsize);
+ return TRUE;
+error4:
+ printf("Error in bitmap_is_clear_all, bitsize = %u", bitsize);
+ return TRUE;
+error5:
+ printf("Error in set_all through set_prefix, bitsize = %u", bitsize);
+ return TRUE;
+error6:
+ printf("Error in clear_all through set_prefix, bitsize = %u", bitsize);
+ return TRUE;
+}
- DBUG_ASSERT(map->bitmap);
- bitmap_lock((MY_BITMAP *)map);
- for (i=0; i < bitmap_size ; i++, bitmap++)
+bool test_compare_operators(MY_BITMAP *map, uint bitsize)
+{
+ uint i, j, test_bit1, test_bit2, test_bit3,test_bit4;
+ uint no_loops= bitsize > 128 ? 128 : bitsize;
+ MY_BITMAP map2_obj, map3_obj;
+ MY_BITMAP *map2= &map2_obj, *map3= &map3_obj;
+ my_bitmap_map map2buf[1024];
+ my_bitmap_map map3buf[1024];
+ bitmap_init(&map2_obj, map2buf, bitsize, FALSE);
+ bitmap_init(&map3_obj, map3buf, bitsize, FALSE);
+ bitmap_clear_all(map2);
+ bitmap_clear_all(map3);
+ for (i=0; i < no_loops; i++)
{
- if (*bitmap != 0xff)
- { /* Found slot with free bit */
- uint b;
- for (b=0; ; b++)
- {
- if (!(*bitmap & (1 << b)))
- {
- bit_found = (i*8)+b;
- break;
- }
- }
- break; /* Found bit */
+ test_bit1=get_rand_bit(bitsize);
+ bitmap_set_prefix(map, test_bit1);
+ test_bit2=get_rand_bit(bitsize);
+ bitmap_set_prefix(map2, test_bit2);
+ bitmap_intersect(map, map2);
+ test_bit3= test_bit2 < test_bit1 ? test_bit2 : test_bit1;
+ bitmap_set_prefix(map3, test_bit3);
+ if (!bitmap_cmp(map, map3))
+ goto error1;
+ bitmap_clear_all(map);
+ bitmap_clear_all(map2);
+ bitmap_clear_all(map3);
+ test_bit1=get_rand_bit(bitsize);
+ test_bit2=get_rand_bit(bitsize);
+ test_bit3=get_rand_bit(bitsize);
+ bitmap_set_prefix(map, test_bit1);
+ bitmap_set_prefix(map2, test_bit2);
+ test_bit3= test_bit2 > test_bit1 ? test_bit2 : test_bit1;
+ bitmap_set_prefix(map3, test_bit3);
+ bitmap_union(map, map2);
+ if (!bitmap_cmp(map, map3))
+ goto error2;
+ bitmap_clear_all(map);
+ bitmap_clear_all(map2);
+ bitmap_clear_all(map3);
+ test_bit1=get_rand_bit(bitsize);
+ test_bit2=get_rand_bit(bitsize);
+ test_bit3=get_rand_bit(bitsize);
+ bitmap_set_prefix(map, test_bit1);
+ bitmap_set_prefix(map2, test_bit2);
+ bitmap_xor(map, map2);
+ test_bit3= test_bit2 > test_bit1 ? test_bit2 : test_bit1;
+ test_bit4= test_bit2 < test_bit1 ? test_bit2 : test_bit1;
+ bitmap_set_prefix(map3, test_bit3);
+ for (j=0; j < test_bit4; j++)
+ bitmap_clear_bit(map3, j);
+ if (!bitmap_cmp(map, map3))
+ goto error3;
+ bitmap_clear_all(map);
+ bitmap_clear_all(map2);
+ bitmap_clear_all(map3);
+ test_bit1=get_rand_bit(bitsize);
+ test_bit2=get_rand_bit(bitsize);
+ test_bit3=get_rand_bit(bitsize);
+ bitmap_set_prefix(map, test_bit1);
+ bitmap_set_prefix(map2, test_bit2);
+ bitmap_subtract(map, map2);
+ if (test_bit2 < test_bit1)
+ {
+ bitmap_set_prefix(map3, test_bit1);
+ for (j=0; j < test_bit2; j++)
+ bitmap_clear_bit(map3, j);
}
+ if (!bitmap_cmp(map, map3))
+ goto error4;
+ bitmap_clear_all(map);
+ bitmap_clear_all(map2);
+ bitmap_clear_all(map3);
+ test_bit1=get_rand_bit(bitsize);
+ bitmap_set_prefix(map, test_bit1);
+ bitmap_invert(map);
+ bitmap_set_all(map3);
+ for (j=0; j < test_bit1; j++)
+ bitmap_clear_bit(map3, j);
+ if (!bitmap_cmp(map, map3))
+ goto error5;
+ bitmap_clear_all(map);
+ bitmap_clear_all(map3);
}
- bitmap_unlock((MY_BITMAP *)map);
- return bit_found;
+ return FALSE;
+error1:
+ printf("intersect error bitsize=%u,size1=%u,size2=%u", bitsize,
+ test_bit1,test_bit2);
+ return TRUE;
+error2:
+ printf("union error bitsize=%u,size1=%u,size2=%u", bitsize,
+ test_bit1,test_bit2);
+ return TRUE;
+error3:
+ printf("xor error bitsize=%u,size1=%u,size2=%u", bitsize,
+ test_bit1,test_bit2);
+ return TRUE;
+error4:
+ printf("subtract error bitsize=%u,size1=%u,size2=%u", bitsize,
+ test_bit1,test_bit2);
+ return TRUE;
+error5:
+ printf("invert error bitsize=%u,size=%u", bitsize,
+ test_bit1);
+ return TRUE;
}
+bool test_count_bits_set(MY_BITMAP *map, uint bitsize)
+{
+ uint i, bit_count=0, test_bit;
+ uint no_loops= bitsize > 128 ? 128 : bitsize;
+ for (i=0; i < no_loops; i++)
+ {
+ test_bit=get_rand_bit(bitsize);
+ if (!bitmap_is_set(map, test_bit))
+ {
+ bitmap_set_bit(map, test_bit);
+ bit_count++;
+ }
+ }
+ if (bit_count==0 && bitsize > 0)
+ goto error1;
+ if (bitmap_bits_set(map) != bit_count)
+ goto error2;
+ return FALSE;
+error1:
+ printf("No bits set bitsize = %u", bitsize);
+ return TRUE;
+error2:
+ printf("Wrong count of bits set, bitsize = %u", bitsize);
+ return TRUE;
+}
+
+bool test_get_first_bit(MY_BITMAP *map, uint bitsize)
+{
+ uint i, test_bit;
+ uint no_loops= bitsize > 128 ? 128 : bitsize;
+ for (i=0; i < no_loops; i++)
+ {
+ test_bit=get_rand_bit(bitsize);
+ bitmap_set_bit(map, test_bit);
+ if (bitmap_get_first_set(map) != test_bit)
+ goto error1;
+ bitmap_set_all(map);
+ bitmap_clear_bit(map, test_bit);
+ if (bitmap_get_first(map) != test_bit)
+ goto error2;
+ bitmap_clear_all(map);
+ }
+ return FALSE;
+error1:
+ printf("get_first_set error bitsize=%u,prefix_size=%u",bitsize,test_bit);
+ return TRUE;
+error2:
+ printf("get_first error bitsize= %u, prefix_size= %u",bitsize,test_bit);
+ return TRUE;
+}
+
+bool test_get_next_bit(MY_BITMAP *map, uint bitsize)
+{
+ uint i, j, test_bit;
+ uint no_loops= bitsize > 128 ? 128 : bitsize;
+ for (i=0; i < no_loops; i++)
+ {
+ test_bit=get_rand_bit(bitsize);
+ for (j=0; j < test_bit; j++)
+ bitmap_set_next(map);
+ if (!bitmap_is_prefix(map, test_bit))
+ goto error1;
+ bitmap_clear_all(map);
+ }
+ return FALSE;
+error1:
+ printf("get_next error bitsize= %u, prefix_size= %u", bitsize,test_bit);
+ return TRUE;
+}
+
+bool test_prefix(MY_BITMAP *map, uint bitsize)
+{
+ uint i, j, test_bit;
+ uint no_loops= bitsize > 128 ? 128 : bitsize;
+ for (i=0; i < no_loops; i++)
+ {
+ test_bit=get_rand_bit(bitsize);
+ bitmap_set_prefix(map, test_bit);
+ if (!bitmap_is_prefix(map, test_bit))
+ goto error1;
+ bitmap_clear_all(map);
+ for (j=0; j < test_bit; j++)
+ bitmap_set_bit(map, j);
+ if (!bitmap_is_prefix(map, test_bit))
+ goto error2;
+ bitmap_set_all(map);
+ for (j=bitsize - 1; ~(j-test_bit); j--)
+ bitmap_clear_bit(map, j);
+ if (!bitmap_is_prefix(map, test_bit))
+ goto error3;
+ bitmap_clear_all(map);
+ }
+ return FALSE;
+error1:
+ printf("prefix1 error bitsize = %u, prefix_size = %u", bitsize,test_bit);
+ return TRUE;
+error2:
+ printf("prefix2 error bitsize = %u, prefix_size = %u", bitsize,test_bit);
+ return TRUE;
+error3:
+ printf("prefix3 error bitsize = %u, prefix_size = %u", bitsize,test_bit);
+ return TRUE;
+}
+
+
+bool do_test(uint bitsize)
+{
+ MY_BITMAP map;
+ my_bitmap_map buf[1024];
+ if (bitmap_init(&map, buf, bitsize, FALSE))
+ {
+ printf("init error for bitsize %d", bitsize);
+ goto error;
+ }
+ if (test_set_get_clear_bit(&map,bitsize))
+ goto error;
+ bitmap_clear_all(&map);
+ if (test_flip_bit(&map,bitsize))
+ goto error;
+ bitmap_clear_all(&map);
+ if (test_operators(&map,bitsize))
+ goto error;
+ bitmap_clear_all(&map);
+ if (test_get_all_bits(&map, bitsize))
+ goto error;
+ bitmap_clear_all(&map);
+ if (test_compare_operators(&map,bitsize))
+ goto error;
+ bitmap_clear_all(&map);
+ if (test_count_bits_set(&map,bitsize))
+ goto error;
+ bitmap_clear_all(&map);
+ if (test_get_first_bit(&map,bitsize))
+ goto error;
+ bitmap_clear_all(&map);
+ if (test_get_next_bit(&map,bitsize))
+ goto error;
+ if (test_prefix(&map,bitsize))
+ goto error;
+ return FALSE;
+error:
+ printf("\n");
+ return TRUE;
+}
+
+int main()
+{
+ int i;
+ for (i= 1; i < 4096; i++)
+ {
+ printf("Start test for bitsize=%u\n",i);
+ if (do_test(i))
+ return -1;
+ }
+ printf("OK\n");
+ return 0;
+}
+
+/*
+ In directory mysys:
+ make test_bitmap
+ will build the bitmap tests and ./test_bitmap will execute it
+*/
+
+#endif
diff --git a/mysys/my_clock.c b/mysys/my_clock.c
index c5fa516a622..adc755028d5 100644
--- a/mysys/my_clock.c
+++ b/mysys/my_clock.c
@@ -13,17 +13,16 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-#define USES_TYPES
#include "my_global.h"
-#if !defined(_MSC_VER) && !defined(__BORLANDC__) && !defined(OS2) && !defined(__NETWARE__)
+#if !defined(_MSC_VER) && !defined(__BORLANDC__) && !defined(__NETWARE__)
#include "mysys_priv.h"
#include <sys/times.h>
#endif
long my_clock(void)
{
-#if !defined(MSDOS) && !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__)
+#if !defined(__WIN__) && !defined(__NETWARE__)
struct tms tmsbuf;
VOID(times(&tmsbuf));
return (tmsbuf.tms_utime + tmsbuf.tms_stime);
diff --git a/mysys/my_compress.c b/mysys/my_compress.c
index c054bf155b1..45e60552592 100644
--- a/mysys/my_compress.c
+++ b/mysys/my_compress.c
@@ -94,4 +94,133 @@ my_bool my_uncompress (byte *packet, ulong *len, ulong *complen)
}
DBUG_RETURN(0);
}
+
+/*
+ Internal representation of the frm blob
+*/
+
+struct frm_blob_header
+{
+ uint ver; /* Version of header */
+ uint orglen; /* Original length of compressed data */
+ uint complen; /* Compressed length of data, 0=uncompressed */
+};
+
+struct frm_blob_struct
+{
+ struct frm_blob_header head;
+ char data[1];
+};
+
+/*
+ packfrm is a method used to compress the frm file for storage in a
+ handler. This method was developed for the NDB handler and has been moved
+ here to serve also other uses.
+
+ SYNOPSIS
+ packfrm()
+ data Data reference to frm file data
+ len Length of frm file data
+ out:pack_data Reference to the pointer to the packed frm data
+ out:pack_len Length of packed frm file data
+
+ RETURN VALUES
+ 0 Success
+ >0 Failure
+*/
+
+int packfrm(const void *data, uint len,
+ const void **pack_data, uint *pack_len)
+{
+ int error;
+ ulong org_len, comp_len;
+ uint blob_len;
+ struct frm_blob_struct *blob;
+ DBUG_ENTER("packfrm");
+ DBUG_PRINT("enter", ("data: 0x%lx, len: %d", (long) data, len));
+
+ error= 1;
+ org_len= len;
+ if (my_compress((byte*)data, &org_len, &comp_len))
+ goto err;
+
+ DBUG_PRINT("info", ("org_len: %lu comp_len: %lu", org_len, comp_len));
+ DBUG_DUMP("compressed", (char*)data, org_len);
+
+ error= 2;
+ blob_len= sizeof(struct frm_blob_header)+org_len;
+ if (!(blob= (struct frm_blob_struct*) my_malloc(blob_len,MYF(MY_WME))))
+ goto err;
+
+ /* Store compressed blob in machine independent format */
+ int4store((char*)(&blob->head.ver), 1);
+ int4store((char*)(&blob->head.orglen), comp_len);
+ int4store((char*)(&blob->head.complen), org_len);
+
+ /* Copy frm data into blob, already in machine independent format */
+ memcpy(blob->data, data, org_len);
+
+ *pack_data= blob;
+ *pack_len= blob_len;
+ error= 0;
+
+ DBUG_PRINT("exit", ("pack_data: 0x%lx pack_len: %d",
+ (long) *pack_data, *pack_len));
+err:
+ DBUG_RETURN(error);
+
+}
+
+/*
+ unpackfrm is a method used to decompress the frm file received from a
+ handler. This method was developed for the NDB handler and has been moved
+ here to serve also other uses for other clustered storage engines.
+
+ SYNOPSIS
+ unpackfrm()
+ pack_data Data reference to packed frm file data
+ out:unpack_data Reference to the pointer to the unpacked frm data
+ out:unpack_len Length of unpacked frm file data
+
+ RETURN VALUES¨
+ 0 Success
+ >0 Failure
+*/
+
+int unpackfrm(const void **unpack_data, uint *unpack_len,
+ const void *pack_data)
+{
+ const struct frm_blob_struct *blob= (struct frm_blob_struct*)pack_data;
+ byte *data;
+ ulong complen, orglen, ver;
+ DBUG_ENTER("unpackfrm");
+ DBUG_PRINT("enter", ("pack_data: 0x%lx", (long) pack_data));
+
+ complen= uint4korr((char*)&blob->head.complen);
+ orglen= uint4korr((char*)&blob->head.orglen);
+ ver= uint4korr((char*)&blob->head.ver);
+
+ DBUG_PRINT("blob",("ver: %lu complen: %lu orglen: %lu",
+ ver,complen,orglen));
+ DBUG_DUMP("blob->data", (char*) blob->data, complen);
+
+ if (ver != 1)
+ DBUG_RETURN(1);
+ if (!(data= my_malloc(max(orglen, complen), MYF(MY_WME))))
+ DBUG_RETURN(2);
+ memcpy(data, blob->data, complen);
+
+
+ if (my_uncompress(data, &complen, &orglen))
+ {
+ my_free((char*)data, MYF(0));
+ DBUG_RETURN(3);
+ }
+
+ *unpack_data= data;
+ *unpack_len= complen;
+
+ DBUG_PRINT("exit", ("frmdata: 0x%lx len: %d", (long) *unpack_data, *unpack_len));
+ DBUG_RETURN(0);
+}
#endif /* HAVE_COMPRESS */
diff --git a/mysys/my_copy.c b/mysys/my_copy.c
index 6143700befc..3f8b0695a25 100644
--- a/mysys/my_copy.c
+++ b/mysys/my_copy.c
@@ -13,7 +13,6 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-#define USES_TYPES /* sys/types is included */
#include "mysys_priv.h"
#include <my_dir.h> /* for stat */
#include <m_string.h>
@@ -94,7 +93,7 @@ int my_copy(const char *from, const char *to, myf MyFlags)
if (MyFlags & MY_HOLD_ORIGINAL_MODES && !new_file_stat)
DBUG_RETURN(0); /* File copyed but not stat */
VOID(chmod(to, stat_buff.st_mode & 07777)); /* Copy modes */
-#if !defined(MSDOS) && !defined(__WIN__) && !defined(__EMX__) && !defined(OS2) && !defined(__NETWARE__)
+#if !defined(__WIN__) && !defined(__NETWARE__)
VOID(chown(to, stat_buff.st_uid,stat_buff.st_gid)); /* Copy ownership */
#endif
#if !defined(VMS) && !defined(__ZTC__)
@@ -111,6 +110,11 @@ int my_copy(const char *from, const char *to, myf MyFlags)
err:
if (from_file >= 0) VOID(my_close(from_file,MyFlags));
- if (to_file >= 0) VOID(my_close(to_file,MyFlags));
+ if (to_file >= 0)
+ {
+ VOID(my_close(to_file, MyFlags));
+ /* attempt to delete the to-file we've partially written */
+ VOID(my_delete(to, MyFlags));
+ }
DBUG_RETURN(-1);
} /* my_copy */
diff --git a/mysys/my_create.c b/mysys/my_create.c
index d612926c1a5..5639459f5a9 100644
--- a/mysys/my_create.c
+++ b/mysys/my_create.c
@@ -13,12 +13,11 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-#define USES_TYPES
#include "mysys_priv.h"
#include <my_dir.h>
#include "mysys_err.h"
#include <errno.h>
-#if defined(MSDOS) || defined(__WIN__) || defined(__EMX__) || defined(OS2)
+#if defined(__WIN__)
#include <share.h>
#endif
@@ -40,19 +39,12 @@ File my_create(const char *FileName, int CreateFlags, int access_flags,
DBUG_PRINT("my",("Name: '%s' CreateFlags: %d AccessFlags: %d MyFlags: %d",
FileName, CreateFlags, access_flags, MyFlags));
-#if !defined(NO_OPEN_3) && !defined(__EMX__)
+#if !defined(NO_OPEN_3)
fd = open((my_string) FileName, access_flags | O_CREAT,
CreateFlags ? CreateFlags : my_umask);
#elif defined(VMS)
fd = open((my_string) FileName, access_flags | O_CREAT, 0,
"ctx=stm","ctx=bin");
-#elif defined(MSDOS) || defined(__EMX__) || defined(OS2)
- if (access_flags & O_SHARE)
- fd = sopen((my_string) FileName, access_flags | O_CREAT | O_BINARY,
- SH_DENYNO, MY_S_IREAD | MY_S_IWRITE);
- else
- fd = open((my_string) FileName, access_flags | O_CREAT | O_BINARY,
- MY_S_IREAD | MY_S_IWRITE);
#elif defined(__WIN__)
fd= my_sopen((my_string) FileName, access_flags | O_CREAT | O_BINARY,
SH_DENYNO, MY_S_IREAD | MY_S_IWRITE);
diff --git a/mysys/my_dup.c b/mysys/my_dup.c
index 2c6a42726e4..55f5e0c0099 100644
--- a/mysys/my_dup.c
+++ b/mysys/my_dup.c
@@ -13,12 +13,11 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-#define USES_TYPES
#include "mysys_priv.h"
#include "mysys_err.h"
#include <my_dir.h>
#include <errno.h>
-#if defined(MSDOS) || defined(__WIN__) || defined(__EMX__)
+#if defined(__WIN__)
#include <share.h>
#endif
diff --git a/mysys/my_error.c b/mysys/my_error.c
index 836f851322b..48392fe84c3 100644
--- a/mysys/my_error.c
+++ b/mysys/my_error.c
@@ -52,8 +52,10 @@ static struct my_err_head
int meh_first; /* error number matching array slot 0 */
int meh_last; /* error number matching last slot */
} my_errmsgs_globerrs = {NULL, globerrs, EE_ERROR_FIRST, EE_ERROR_LAST};
+
static struct my_err_head *my_errmsgs_list= &my_errmsgs_globerrs;
+
/*
Error message to user
@@ -75,7 +77,6 @@ int my_error(int nr, myf MyFlags, ...)
va_list args;
char ebuff[ERRMSGSIZE + 20];
DBUG_ENTER("my_error");
-
DBUG_PRINT("my", ("nr: %d MyFlags: %d errno: %d", nr, MyFlags, errno));
/* Search for the error messages array, which could contain the message. */
@@ -101,6 +102,7 @@ int my_error(int nr, myf MyFlags, ...)
DBUG_RETURN((*error_handler_hook)(nr, ebuff, MyFlags));
}
+
/*
Error as printf
@@ -249,3 +251,15 @@ const char **my_error_unregister(int first, int last)
return errmsgs;
}
+
+
+void my_error_unregister_all(void)
+{
+ struct my_err_head *list, *next;
+ for (list= my_errmsgs_globerrs.meh_next; list; list= next)
+ {
+ next= list->meh_next;
+ my_free((gptr) list, MYF(0));
+ }
+ my_errmsgs_list= &my_errmsgs_globerrs;
+}
diff --git a/mysys/my_file.c b/mysys/my_file.c
index c97a512f8a1..44bacf55307 100644
--- a/mysys/my_file.c
+++ b/mysys/my_file.c
@@ -32,7 +32,7 @@
May be more or less than max_file_limit!
*/
-#if defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE) && !defined(HAVE_mit_thread)
+#if defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE)
#ifndef RLIM_INFINITY
#define RLIM_INFINITY ((uint) 0xffffffff)
@@ -71,25 +71,6 @@ static uint set_max_open_files(uint max_file_limit)
DBUG_RETURN(max_file_limit);
}
-#elif defined (OS2)
-
-static uint set_max_open_files(uint max_file_limit)
-{
- LONG cbReqCount;
- ULONG cbCurMaxFH0;
- APIRET ulrc;
- DBUG_ENTER("set_max_open_files");
-
- /* get current limit */
- cbReqCount = 0;
- DosSetRelMaxFH( &cbReqCount, &cbCurMaxFH0);
-
- /* set new limit */
- if ((cbReqCount = max_file_limit - cbCurMaxFH0) > 0)
- ulrc = DosSetRelMaxFH( &cbReqCount, &cbCurMaxFH);
- DBUG_RETURN(cbCurMaxFH0);
-}
-
#else
static int set_max_open_files(uint max_file_limit)
{
@@ -125,7 +106,10 @@ uint my_set_max_open_files(uint files)
DBUG_RETURN(MY_NFILE);
/* Copy any initialized files */
- memcpy((char*) tmp, (char*) my_file_info, sizeof(*tmp) * my_file_limit);
+ memcpy((char*) tmp, (char*) my_file_info,
+ sizeof(*tmp) * min(my_file_limit, files));
+ bzero((char*) (tmp + my_file_limit),
+ max((int) (files- my_file_limit), 0)*sizeof(*tmp));
my_free_open_file_info(); /* Free if already allocated */
my_file_info= tmp;
my_file_limit= files;
@@ -139,8 +123,12 @@ void my_free_open_file_info()
DBUG_ENTER("my_free_file_info");
if (my_file_info != my_file_info_default)
{
+ /* Copy data back for my_print_open_files */
+ memcpy((char*) my_file_info_default, my_file_info,
+ sizeof(*my_file_info_default)* MY_NFILE);
my_free((char*) my_file_info, MYF(0));
my_file_info= my_file_info_default;
+ my_file_limit= MY_NFILE;
}
DBUG_VOID_RETURN;
}
diff --git a/mysys/my_gethostbyname.c b/mysys/my_gethostbyname.c
index 434a00bab11..067fdfee9db 100644
--- a/mysys/my_gethostbyname.c
+++ b/mysys/my_gethostbyname.c
@@ -18,7 +18,7 @@
/* Thread safe version of gethostbyname_r() */
#include "mysys_priv.h"
-#if !defined(MSDOS) && !defined(__WIN__)
+#if !defined(__WIN__)
#include <netdb.h>
#endif
#include <my_net.h>
diff --git a/mysys/my_getncpus.c b/mysys/my_getncpus.c
new file mode 100644
index 00000000000..82e87dee2e4
--- /dev/null
+++ b/mysys/my_getncpus.c
@@ -0,0 +1,39 @@
+/* Copyright (C) 2006 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; 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 */
+
+/* get the number of (online) CPUs */
+
+#include "mysys_priv.h"
+#include <unistd.h>
+
+static int ncpus=0;
+
+#ifdef _SC_NPROCESSORS_ONLN
+int my_getncpus()
+{
+ if (!ncpus)
+ ncpus= sysconf(_SC_NPROCESSORS_ONLN);
+ return ncpus;
+}
+
+#else
+/* unknown */
+int my_getncpus()
+{
+ return 2;
+}
+
+#endif
+
diff --git a/mysys/my_getopt.c b/mysys/my_getopt.c
index 623c48b2e55..a23e84ab96a 100644
--- a/mysys/my_getopt.c
+++ b/mysys/my_getopt.c
@@ -58,6 +58,13 @@ char *disabled_my_option= (char*) "0";
my_bool my_getopt_print_errors= 1;
+/*
+ This is a flag that can be set in client programs. 1 means that
+ my_getopt will skip over options it does not know how to handle.
+*/
+
+my_bool my_getopt_skip_unknown= 0;
+
static void default_reporter(enum loglevel level,
const char *format, ...)
{
@@ -90,26 +97,30 @@ void my_getopt_register_get_addr(gptr* (*func_addr)(const char *, uint,
getopt_get_addr= func_addr;
}
-int handle_options(int *argc, char ***argv,
+int handle_options(int *argc, char ***argv,
const struct my_option *longopts,
my_get_one_option get_one_option)
{
- uint opt_found, argvpos= 0, length, i;
+ uint opt_found, argvpos= 0, length;
my_bool end_of_options= 0, must_be_var, set_maximum_value,
option_is_loose;
char **pos, **pos_end, *optend, *prev_found,
*opt_str, key_name[FN_REFLEN];
const struct my_option *optp;
gptr *value;
- int error;
+ int error, i;
LINT_INIT(opt_found);
+ /* handle_options() assumes arg0 (program name) always exists */
+ DBUG_ASSERT(argc && *argc >= 1);
+ DBUG_ASSERT(argv && *argv);
(*argc)--; /* Skip the program name */
(*argv)++; /* --- || ---- */
init_variables(longopts);
for (pos= *argv, pos_end=pos+ *argc; pos != pos_end ; pos++)
{
+ char **first= pos;
char *cur_arg= *pos;
if (cur_arg[0] == '-' && cur_arg[1] && !end_of_options) /* must be opt */
{
@@ -216,12 +227,11 @@ int handle_options(int *argc, char ***argv,
/*
We were called with a special prefix, we can reuse opt_found
*/
- opt_str+= (special_opt_prefix_lengths[i] + 1);
+ opt_str+= special_opt_prefix_lengths[i] + 1;
+ length-= special_opt_prefix_lengths[i] + 1;
if (i == OPT_LOOSE)
option_is_loose= 1;
- if ((opt_found= findopt(opt_str, length -
- (special_opt_prefix_lengths[i] + 1),
- &optp, &prev_found)))
+ if ((opt_found= findopt(opt_str, length, &optp, &prev_found)))
{
if (opt_found > 1)
{
@@ -245,7 +255,7 @@ int handle_options(int *argc, char ***argv,
break;
case OPT_ENABLE:
optend= (optend && *optend == '0' && !(*(optend + 1))) ?
- disabled_my_option : (char*) "1";
+ disabled_my_option : (char*) "1";
break;
case OPT_MAXIMUM:
set_maximum_value= 1;
@@ -254,11 +264,25 @@ int handle_options(int *argc, char ***argv,
}
break; /* break from the inner loop, main loop continues */
}
+ i= -1; /* restart the loop */
}
}
}
if (!opt_found)
{
+ if (my_getopt_skip_unknown)
+ {
+ /*
+ preserve all the components of this unknown option, this may
+ occurr when the user provides options like: "-O foo" or
+ "--set-variable foo" (note that theres a space in there)
+ Generally, these kind of options are to be avoided
+ */
+ do {
+ (*argv)[argvpos++]= *first++;
+ } while (first <= pos);
+ continue;
+ }
if (must_be_var)
{
if (my_getopt_print_errors)
@@ -491,7 +515,7 @@ invalid value '%s'\n",
}
get_one_option(optp->id, optp, argument);
- (*argc)--; /* option handled (short or long), decrease argument count */
+ (*argc)--; /* option handled (short or long), decrease argument count */
}
else /* non-option found */
(*argv)[argvpos++]= cur_arg;
@@ -596,6 +620,15 @@ static int setval(const struct my_option *opts, gptr *value, char *argument,
if (!(*((char**) result_pos)= my_strdup(argument, MYF(MY_WME))))
return EXIT_OUT_OF_MEMORY;
break;
+ case GET_ENUM:
+ if (((*(int*)result_pos)= find_type(argument, opts->typelib, 2) - 1) < 0)
+ return EXIT_ARGUMENT_INVALID;
+ break;
+ case GET_SET:
+ *((ulonglong*)result_pos)= find_typeset(argument, opts->typelib, &err);
+ if (err)
+ return EXIT_ARGUMENT_INVALID;
+ break;
default: /* dummy default to avoid compiler warnings */
break;
}
@@ -780,6 +813,7 @@ ulonglong getopt_ull_limit_value(ulonglong num, const struct my_option *optp)
static void init_one_value(const struct my_option *option, gptr *variable,
longlong value)
{
+ DBUG_ENTER("init_one_value");
switch ((option->var_type & GET_TYPE_MASK)) {
case GET_BOOL:
*((my_bool*) variable)= (my_bool) value;
@@ -788,6 +822,7 @@ static void init_one_value(const struct my_option *option, gptr *variable,
*((int*) variable)= (int) value;
break;
case GET_UINT:
+ case GET_ENUM:
*((uint*) variable)= (uint) value;
break;
case GET_LONG:
@@ -800,11 +835,36 @@ static void init_one_value(const struct my_option *option, gptr *variable,
*((longlong*) variable)= (longlong) value;
break;
case GET_ULL:
+ case GET_SET:
*((ulonglong*) variable)= (ulonglong) value;
break;
+ case GET_STR:
+ /*
+ Do not clear variable value if it has no default value.
+ The default value may already be set.
+ NOTE: To avoid compiler warnings, we first cast longlong to intptr,
+ so that the value has the same size as a pointer.
+ */
+ if ((char*) (intptr) value)
+ *((char**) variable)= (char*) (intptr) value;
+ break;
+ case GET_STR_ALLOC:
+ /*
+ Do not clear variable value if it has no default value.
+ The default value may already be set.
+ NOTE: To avoid compiler warnings, we first cast longlong to intptr,
+ so that the value has the same size as a pointer.
+ */
+ if ((char*) (intptr) value)
+ {
+ my_free((*(char**) variable), MYF(MY_ALLOW_ZERO_PTR));
+ *((char**) variable)= my_strdup((char*) (intptr) value, MYF(MY_WME));
+ }
+ break;
default: /* dummy default to avoid compiler warnings */
break;
}
+ DBUG_VOID_RETURN;
}
@@ -823,9 +883,11 @@ static void init_one_value(const struct my_option *option, gptr *variable,
static void init_variables(const struct my_option *options)
{
+ DBUG_ENTER("init_variables");
for (; options->name; options++)
{
gptr *variable;
+ DBUG_PRINT("options", ("name: '%s'", options->name));
/*
We must set u_max_value first as for some variables
options->u_max_value == options->value and in this case we want to
@@ -839,6 +901,7 @@ static void init_variables(const struct my_option *options)
(variable= (*getopt_get_addr)("", 0, options)))
init_one_value(options, variable, options->def_value);
}
+ DBUG_VOID_RETURN;
}
@@ -928,7 +991,8 @@ void my_print_help(const struct my_option *options)
void my_print_variables(const struct my_option *options)
{
- uint name_space= 34, length;
+ uint name_space= 34, length, nr;
+ ulonglong bit, llvalue;
char buff[255];
const struct my_option *optp;
@@ -941,11 +1005,26 @@ void my_print_variables(const struct my_option *options)
(*getopt_get_addr)("", 0, optp) : optp->value);
if (value)
{
- printf("%s", optp->name);
- length= (uint) strlen(optp->name);
+ printf("%s ", optp->name);
+ length= (uint) strlen(optp->name)+1;
for (; length < name_space; length++)
putchar(' ');
switch ((optp->var_type & GET_TYPE_MASK)) {
+ case GET_SET:
+ if (!(llvalue= *(ulonglong*) value))
+ printf("%s\n", "(No default value)");
+ else
+ for (nr= 0, bit= 1; llvalue && nr < optp->typelib->count; nr++, bit<<=1)
+ {
+ if (!(bit & llvalue))
+ continue;
+ llvalue&= ~bit;
+ printf( llvalue ? "%s," : "%s\n", get_type(optp->typelib, nr));
+ }
+ break;
+ case GET_ENUM:
+ printf("%s\n", get_type(optp->typelib, *(uint*) value));
+ break;
case GET_STR:
case GET_STR_ALLOC: /* fall through */
printf("%s\n", *((char**) value) ? *((char**) value) :
@@ -961,7 +1040,7 @@ void my_print_variables(const struct my_option *options)
printf("%d\n", *((uint*) value));
break;
case GET_LONG:
- printf("%lu\n", *((long*) value));
+ printf("%ld\n", *((long*) value));
break;
case GET_ULONG:
printf("%lu\n", *((ulong*) value));
diff --git a/mysys/my_getwd.c b/mysys/my_getwd.c
index 3870a5d61b0..c258db8b126 100644
--- a/mysys/my_getwd.c
+++ b/mysys/my_getwd.c
@@ -21,19 +21,11 @@
#ifdef HAVE_GETWD
#include <sys/param.h>
#endif
-#if defined(MSDOS) || defined(__WIN__)
+#if defined(__WIN__)
#include <m_ctype.h>
#include <dos.h>
#include <direct.h>
#endif
-#if defined(OS2)
-#include <direct.h>
-#endif
-
-#ifdef __EMX__
-/* chdir2 support also drive change */
-#define chdir _chdir2
-#endif
/* Gets current working directory in buff. Directory is allways ended
with FN_LIBCHAR */
@@ -46,11 +38,9 @@ int my_getwd(my_string buf, uint size, myf MyFlags)
DBUG_ENTER("my_getwd");
DBUG_PRINT("my",("buf: 0x%lx size: %d MyFlags %d", (long) buf,size,MyFlags));
-#if ! defined(MSDOS)
if (curr_dir[0]) /* Current pos is saved here */
VOID(strmake(buf,&curr_dir[0],size-1));
else
-#endif
{
#if defined(HAVE_GETCWD)
if (!getcwd(buf,size-2) && MyFlags & MY_WME)
@@ -94,48 +84,13 @@ int my_setwd(const char *dir, myf MyFlags)
int res;
size_s length;
my_string start,pos;
-#if defined(VMS) || defined(MSDOS) || defined(OS2)
+#if defined(VMS)
char buff[FN_REFLEN];
#endif
DBUG_ENTER("my_setwd");
DBUG_PRINT("my",("dir: '%s' MyFlags %d", dir, MyFlags));
start=(my_string) dir;
-#if defined(MSDOS) || defined(OS2) /* OS2/MSDOS chdir can't change drive */
-#if !defined(_DDL) && !defined(WIN32)
- if ((pos=(char*) strchr(dir,FN_DEVCHAR)) != 0)
- {
- uint drive,drives;
-
- pos++; /* Skip FN_DEVCHAR */
- drive=(uint) (my_toupper(&my_charset_latin1,dir[0])-'A'+1);
- drives= (uint) -1;
- if ((pos-(byte*) dir) == 2 && drive > 0 && drive < 32)
- {
-#ifdef OS2
- _chdrive(drive);
- drives = _getdrive();
-#else
- _dos_setdrive(drive,&drives);
- _dos_getdrive(&drives);
-#endif
- }
- if (drive != drives)
- {
- *pos='\0'; /* Dir is now only drive */
- my_errno=errno;
- my_error(EE_SETWD,MYF(ME_BELL+ME_WAITTANG),dir,ENOENT);
- DBUG_RETURN(-1);
- }
- dir=pos; /* drive changed, change now path */
- }
-#endif
- if (*((pos=strend(dir)-1)) == FN_LIBCHAR && pos != dir)
- {
- strmov(buff,dir)[-1]=0; /* Remove last '/' */
- dir=buff;
- }
-#endif /* MSDOS*/
if (! dir[0] || (dir[0] == FN_LIBCHAR && dir[1] == 0))
dir=FN_ROOTDIR;
#ifdef VMS
diff --git a/mysys/my_init.c b/mysys/my_init.c
index 2bcf5f44c4d..7784c09d9d6 100644
--- a/mysys/my_init.c
+++ b/mysys/my_init.c
@@ -86,13 +86,10 @@ my_bool my_init(void)
#endif
if (my_thread_global_init())
return 1;
-#if !defined( __WIN__) && !defined(OS2) && !defined(__NETWARE__)
+#if !defined( __WIN__) && !defined(__NETWARE__)
sigfillset(&my_signals); /* signals blocked by mf_brkhant */
#endif
#endif /* THREAD */
-#ifdef UNIXWARE_7
- (void) isatty(0); /* Go around connect() bug in UW7 */
-#endif
{
DBUG_ENTER("my_init");
DBUG_PROCESS((char*) (my_progname ? my_progname : "unknown"));
@@ -133,7 +130,10 @@ void my_end(int infoflag)
*/
FILE *info_file= DBUG_FILE;
my_bool print_info= (info_file != stderr);
- DBUG_ENTER("my_end");
+ /* We do not use DBUG_ENTER here, as after cleanup DBUG is no longer
+ operational, so we cannot use DBUG_RETURN.
+ */
+ DBUG_PRINT("info",("Shutting down"));
if (!info_file)
{
info_file= stderr;
@@ -149,9 +149,11 @@ void my_end(int infoflag)
sprintf(errbuff[0],EE(EE_OPEN_WARNING),my_file_opened,my_stream_opened);
(void) my_message_no_curses(EE_OPEN_WARNING,errbuff[0],ME_BELL);
DBUG_PRINT("error",("%s",errbuff[0]));
+ my_print_open_files();
}
}
free_charsets();
+ my_error_unregister_all();
my_once_free();
if ((infoflag & MY_GIVE_INFO) || print_info)
@@ -179,7 +181,7 @@ Voluntary context switches %ld, Involuntary context switches %ld\n",
rus.ru_msgsnd, rus.ru_msgrcv, rus.ru_nsignals,
rus.ru_nvcsw, rus.ru_nivcsw);
#endif
-#if ( defined(MSDOS) || defined(__NETWARE__) ) && !defined(__WIN__)
+#if defined(__NETWARE__) && !defined(__WIN__)
fprintf(info_file,"\nRun time: %.1f\n",(double) clock()/CLOCKS_PER_SEC);
#endif
#if defined(SAFEMALLOC)
@@ -208,7 +210,8 @@ Voluntary context switches %ld, Involuntary context switches %ld\n",
Check on destroying of mutexes. A few may be left that will get cleaned
up by C++ destructors
*/
- safe_mutex_end(infoflag & MY_GIVE_INFO ? stderr : (FILE *) 0);
+ safe_mutex_end((infoflag & (MY_GIVE_INFO | MY_CHECK_ERROR)) ? stderr :
+ (FILE *) 0);
#endif /* defined(SAFE_MUTEX) */
#endif /* THREAD */
@@ -247,14 +250,16 @@ void setEnvString(char *ret, const char *name, const char *value)
}
/*
- my_paramter_handler
- Invalid paramter handler we will use instead of the one "baked" into the CRT
- for MSC v8. This one just prints out what invalid parameter was encountered.
- By providing this routine, routines like lseek will return -1 when we expect them
- to instead of crash.
+ my_parameter_handler
+
+ Invalid parameter handler we will use instead of the one "baked"
+ into the CRT for MSC v8. This one just prints out what invalid
+ parameter was encountered. By providing this routine, routines like
+ lseek will return -1 when we expect them to instead of crash.
*/
-void my_parameter_handler(const wchar_t * expression, const wchar_t * function,
- const wchar_t * file, unsigned int line,
+
+void my_parameter_handler(const wchar_t * expression, const wchar_t * function,
+ const wchar_t * file, unsigned int line,
uintptr_t pReserved)
{
DBUG_PRINT("my",("Expression: %s function: %s file: %s, line: %d",
diff --git a/mysys/my_lib.c b/mysys/my_lib.c
index 61de4ff9065..783a0683731 100644
--- a/mysys/my_lib.c
+++ b/mysys/my_lib.c
@@ -14,9 +14,8 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
/* TODO: check for overun of memory for names. */
-/* Convert MSDOS-TIME to standar time_t */
+/* Convert MSDOS-TIME to standar time_t (still needed?) */
-#define USES_TYPES /* sys/types is included */
#include "mysys_priv.h"
#include <m_string.h>
#include <my_dir.h> /* Structs used by my_dir,includes sys/types */
@@ -25,9 +24,7 @@
# include <dirent.h>
# define NAMLEN(dirent) strlen((dirent)->d_name)
#else
-#ifndef OS2
# define dirent direct
-#endif
# define NAMLEN(dirent) (dirent)->d_namlen
# if defined(HAVE_SYS_NDIR_H)
# include <sys/ndir.h>
@@ -38,7 +35,7 @@
# if defined(HAVE_NDIR_H)
# include <ndir.h>
# endif
-# if defined(MSDOS) || defined(__WIN__)
+# if defined(__WIN__)
# include <dos.h>
# ifdef __BORLANDC__
# include <dir.h>
@@ -51,10 +48,6 @@
#include <descrip.h>
#endif
-#ifdef OS2
-#include "my_os2dirsrch.h"
-#endif
-
#if defined(THREAD) && defined(HAVE_READDIR_R)
#define READDIR(A,B,C) ((errno=readdir_r(A,B,&C)) != 0 || !C)
#else
@@ -99,7 +92,7 @@ static int comp_names(struct fileinfo *a, struct fileinfo *b)
} /* comp_names */
-#if !defined(MSDOS) && !defined(__WIN__)
+#if !defined(__WIN__)
MY_DIR *my_dir(const char *path, myf MyFlags)
{
@@ -352,7 +345,7 @@ my_string directory_file_name (my_string dst, const char *src)
#endif /* VMS */
} /* directory_file_name */
-#elif defined(WIN32)
+#else
/*
*****************************************************************************
@@ -391,7 +384,7 @@ MY_DIR *my_dir(const char *path, myf MyFlags)
*tmp_file++= '.'; /* From current dev-dir */
if (tmp_file[-1] != FN_LIBCHAR)
*tmp_file++ =FN_LIBCHAR;
- tmp_file[0]='*'; /* MSDOS needs this !??? */
+ tmp_file[0]='*'; /* Windows needs this !??? */
tmp_file[1]='.';
tmp_file[2]='*';
tmp_file[3]='\0';
@@ -514,109 +507,7 @@ error:
DBUG_RETURN((MY_DIR *) NULL);
} /* my_dir */
-#else /* MSDOS and not WIN32 */
-
-
-/******************************************************************************
-** At MSDOS you always get stat of files, but time is in packed MSDOS-format
-******************************************************************************/
-
-MY_DIR *my_dir(const char* path, myf MyFlags)
-{
- char *buffer;
- MY_DIR *result= 0;
- FILEINFO finfo;
- DYNAMIC_ARRAY *dir_entries_storage;
- MEM_ROOT *names_storage;
- struct find_t find;
- ushort mode;
- char tmp_path[FN_REFLEN],*tmp_file,attrib;
- DBUG_ENTER("my_dir");
- DBUG_PRINT("my",("path: '%s' stat: %d MyFlags: %d",path,MyFlags));
-
- /* Put LIB-CHAR as last path-character if not there */
-
- tmp_file=tmp_path;
- if (!*path)
- *tmp_file++ ='.'; /* From current dir */
- tmp_file= strmov(tmp_file,path);
- if (tmp_file[-1] == FN_DEVCHAR)
- *tmp_file++= '.'; /* From current dev-dir */
- if (tmp_file[-1] != FN_LIBCHAR)
- *tmp_file++ =FN_LIBCHAR;
- tmp_file[0]='*'; /* MSDOS needs this !??? */
- tmp_file[1]='.';
- tmp_file[2]='*';
- tmp_file[3]='\0';
-
- if (_dos_findfirst(tmp_path,_A_NORMAL | _A_SUBDIR, &find))
- goto error;
-
- if (!(buffer= my_malloc(ALIGN_SIZE(sizeof(MY_DIR)) +
- ALIGN_SIZE(sizeof(DYNAMIC_ARRAY)) +
- sizeof(MEM_ROOT), MyFlags)))
- goto error;
-
- dir_entries_storage= (DYNAMIC_ARRAY*)(buffer + ALIGN_SIZE(sizeof(MY_DIR)));
- names_storage= (MEM_ROOT*)(buffer + ALIGN_SIZE(sizeof(MY_DIR)) +
- ALIGN_SIZE(sizeof(DYNAMIC_ARRAY)));
-
- if (my_init_dynamic_array(dir_entries_storage, sizeof(FILEINFO),
- ENTRIES_START_SIZE, ENTRIES_INCREMENT))
- {
- my_free((gptr) buffer,MYF(0));
- goto error;
- }
- init_alloc_root(names_storage, NAMES_START_SIZE, NAMES_START_SIZE);
-
- /* MY_DIR structure is allocated and completly initialized at this point */
- result= (MY_DIR*)buffer;
-
- do
- {
- if (!(finfo.name= strdup_root(names_storage, find.name)))
- goto error;
-
- if (MyFlags & MY_WANT_STAT)
- {
- if (!(finfo.mystat= (MY_STAT*)alloc_root(names_storage,
- sizeof(MY_STAT))))
- goto error;
-
- bzero(finfo.mystat, sizeof(MY_STAT));
- finfo.mystat->st_size= find.size;
- mode= MY_S_IREAD; attrib= find.attrib;
- if (!(attrib & _A_RDONLY))
- mode|= MY_S_IWRITE;
- if (attrib & _A_SUBDIR)
- mode|= MY_S_IFDIR;
- finfo.mystat->st_mode= mode;
- finfo.mystat->st_mtime= ((uint32) find.wr_date << 16) + find.wr_time;
- }
- else
- finfo.mystat= NULL;
-
- if (push_dynamic(dir_entries_storage, (gptr)&finfo))
- goto error;
-
- } while (_dos_findnext(&find) == 0);
-
- result->dir_entry= (FILEINFO *)dir_entries_storage->buffer;
- result->number_off_files= dir_entries_storage->elements;
-
- if (!(MyFlags & MY_DONT_SORT))
- qsort((void *) result->dir_entry, result->number_off_files,
- sizeof(FILEINFO), (qsort_cmp) comp_names);
- DBUG_RETURN(result);
-
-error:
- my_dirend(result);
- if (MyFlags & MY_FAE+MY_WME)
- my_error(EE_DIR,MYF(ME_BELL+ME_WAITTANG),path,errno);
- DBUG_RETURN((MY_DIR *) NULL);
-} /* my_dir */
-
-#endif /* WIN32 && MSDOS */
+#endif /* __WIN__ */
/****************************************************************************
** File status
diff --git a/mysys/my_lock.c b/mysys/my_lock.c
index a07f0c072a0..c0522ee849d 100644
--- a/mysys/my_lock.c
+++ b/mysys/my_lock.c
@@ -25,11 +25,6 @@
#ifdef __WIN__
#include <sys/locking.h>
#endif
-#ifdef __EMX__
-#define INCL_BASE
-#define INCL_NOPMAPI
-#include <os2emx.h>
-#endif
#ifdef __NETWARE__
#include <nks/fsio.h>
#endif
@@ -102,11 +97,6 @@ int my_lock(File fd, int locktype, my_off_t start, my_off_t length,
DBUG_RETURN(0);
}
}
-#elif defined(__EMX__) || defined(OS2)
-
- if (!_lock64( fd, locktype, start, length, MyFlags))
- DBUG_RETURN(0);
-
#elif defined(HAVE_LOCKING)
/* Windows */
{
diff --git a/mysys/my_lread.c b/mysys/my_lread.c
deleted file mode 100644
index 1399b197119..00000000000
--- a/mysys/my_lread.c
+++ /dev/null
@@ -1,54 +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 */
-
-#include "mysys_priv.h"
-#include "mysys_err.h"
-
- /* Read a chunk of bytes from a file */
-
-uint32 my_lread(int Filedes, byte *Buffer, uint32 Count, myf MyFlags)
- /* File descriptor */
- /* Buffer must be at least count bytes */
- /* Max number of bytes returnd */
- /* Flags on what to do on error */
-{
- uint32 readbytes;
- DBUG_ENTER("my_lread");
- DBUG_PRINT("my",("Fd: %d Buffer: 0x%lx Count: %lu MyFlags: %d",
- Filedes, (long) Buffer, (ulong) Count, MyFlags));
-
- DBUG_PRINT("error", ("Deprecated my_lread() function should not be used."));
-
- /* Temp hack to get count to int32 while read wants int */
- if ((readbytes = (uint32) read(Filedes, Buffer, (uint) Count)) != Count)
- {
- my_errno=errno;
- if (MyFlags & (MY_WME | MY_FAE | MY_FNABP))
- {
- if (readbytes == MY_FILE_ERROR)
- my_error(EE_READ, MYF(ME_BELL+ME_WAITTANG),
- my_filename(Filedes),errno);
- else
- if (MyFlags & (MY_NABP | MY_FNABP))
- my_error(EE_EOFERR, MYF(ME_BELL+ME_WAITTANG),
- my_filename(Filedes),errno);
- }
- if (readbytes == MY_FILE_ERROR || MyFlags & (MY_NABP | MY_FNABP))
- DBUG_RETURN((uint32) -1); /* Return med felkod */
- }
- if (MyFlags & (MY_NABP | MY_FNABP))
- DBUG_RETURN(0); /* Ok vid l{sning */
- DBUG_RETURN(readbytes);
-} /* my_lread */
diff --git a/mysys/my_lwrite.c b/mysys/my_lwrite.c
deleted file mode 100644
index d26825397e3..00000000000
--- a/mysys/my_lwrite.c
+++ /dev/null
@@ -1,47 +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 */
-
-#include "mysys_priv.h"
-#include "mysys_err.h"
-
- /* Write a chunk of bytes to a file */
-
-uint32 my_lwrite(int Filedes, const byte *Buffer, uint32 Count, myf MyFlags)
-{
- uint32 writenbytes;
- DBUG_ENTER("my_lwrite");
- DBUG_PRINT("my",("Fd: %d Buffer: 0x%lx Count: %lu MyFlags: %d",
- Filedes, (long) Buffer, (ulong) Count, MyFlags));
-
- DBUG_PRINT("error", ("Deprecated my_lwrite() function should not be used."));
-
- /* Temp hack to get count to int32 while write wants int */
- if ((writenbytes = (uint32) write(Filedes, Buffer, (uint) Count)) != Count)
- {
- my_errno=errno;
- if (writenbytes == (uint32) -1 || MyFlags & (MY_NABP | MY_FNABP))
- {
- if (MyFlags & (MY_WME | MY_FAE | MY_FNABP))
- {
- my_error(EE_WRITE, MYF(ME_BELL+ME_WAITTANG),
- my_filename(Filedes),errno);
- }
- DBUG_RETURN((uint32) -1); /* Return med felkod */
- }
- }
- if (MyFlags & (MY_NABP | MY_FNABP))
- DBUG_RETURN(0); /* Ok vid l{sning */
- DBUG_RETURN(writenbytes);
-} /* my_lwrite */
diff --git a/mysys/my_malloc.c b/mysys/my_malloc.c
index 38d0263b495..b0e775177a6 100644
--- a/mysys/my_malloc.c
+++ b/mysys/my_malloc.c
@@ -82,7 +82,7 @@ char *my_strdup(const char *from, myf my_flags)
}
-char *my_strdup_with_length(const char *from, uint length, myf my_flags)
+char *my_strndup(const char *from, uint length, myf my_flags)
{
gptr ptr;
if ((ptr=my_malloc(length+1,my_flags)) != 0)
diff --git a/mysys/my_mkdir.c b/mysys/my_mkdir.c
index 25176e4b823..676c6c1cd51 100644
--- a/mysys/my_mkdir.c
+++ b/mysys/my_mkdir.c
@@ -26,7 +26,7 @@ int my_mkdir(const char *dir, int Flags, myf MyFlags)
DBUG_ENTER("my_dir");
DBUG_PRINT("enter",("dir: %s",dir));
-#if defined(__WIN__) || defined(OS2)
+#if defined(__WIN__)
if (mkdir((char*) dir))
#else
if (mkdir((char*) dir, Flags & my_umask_dir))
diff --git a/mysys/my_mmap.c b/mysys/my_mmap.c
index 147bdfdcbf2..023a06fd896 100644
--- a/mysys/my_mmap.c
+++ b/mysys/my_mmap.c
@@ -35,22 +35,19 @@ static SECURITY_ATTRIBUTES mmap_security_attributes=
void *my_mmap(void *addr, size_t len, int prot,
int flags, int fd, my_off_t offset)
{
- DWORD flProtect=0;
HANDLE hFileMap;
LPVOID ptr;
HANDLE hFile= (HANDLE)_get_osfhandle(fd);
if (hFile == INVALID_HANDLE_VALUE)
return MAP_FAILED;
- flProtect|=SEC_COMMIT;
-
hFileMap=CreateFileMapping(hFile, &mmap_security_attributes,
PAGE_READWRITE, 0, (DWORD) len, NULL);
if (hFileMap == 0)
return MAP_FAILED;
ptr=MapViewOfFile(hFileMap,
- flags & PROT_WRITE ? FILE_MAP_WRITE : FILE_MAP_READ,
+ prot & PROT_WRITE ? FILE_MAP_WRITE : FILE_MAP_READ,
(DWORD)(offset >> 32), (DWORD)offset, len);
/*
diff --git a/mysys/my_net.c b/mysys/my_net.c
index 136d987f500..81d977210f8 100644
--- a/mysys/my_net.c
+++ b/mysys/my_net.c
@@ -19,7 +19,7 @@
#include <m_string.h>
/* for thread safe my_inet_ntoa */
-#if !defined(MSDOS) && !defined(__WIN__)
+#if !defined(__WIN__)
#include <netdb.h>
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
@@ -30,7 +30,7 @@
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
-#endif /* !defined(MSDOS) && !defined(__WIN__) */
+#endif /* !defined(__WIN__) */
void my_inet_ntoa(struct in_addr in, char *buf)
{
diff --git a/mysys/my_once.c b/mysys/my_once.c
index d83eba95b9d..fcb5af3ccbd 100644
--- a/mysys/my_once.c
+++ b/mysys/my_once.c
@@ -74,6 +74,8 @@ gptr my_once_alloc(unsigned int Size, myf MyFlags)
point= (gptr) ((char*) next+ (next->size-next->left));
next->left-= Size;
+ if (MyFlags & MY_ZEROFILL)
+ bzero(point, Size);
return(point);
} /* my_once_alloc */
diff --git a/mysys/my_open.c b/mysys/my_open.c
index 9e1da1d3f07..71e243c0173 100644
--- a/mysys/my_open.c
+++ b/mysys/my_open.c
@@ -13,12 +13,11 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-#define USES_TYPES
#include "mysys_priv.h"
#include "mysys_err.h"
#include <my_dir.h>
#include <errno.h>
-#if defined(MSDOS) || defined(__WIN__) || defined(__EMX__) || defined(OS2)
+#if defined(__WIN__)
#include <share.h>
#endif
@@ -44,7 +43,7 @@ File my_open(const char *FileName, int Flags, myf MyFlags)
DBUG_ENTER("my_open");
DBUG_PRINT("my",("Name: '%s' Flags: %d MyFlags: %d",
FileName, Flags, MyFlags));
-#if defined(MSDOS) || defined(__WIN__) || defined(__EMX__) || defined(OS2)
+#if defined(__WIN__)
/*
Check that we don't try to open or create a file name that may
cause problems for us in the future (like PRN)
@@ -167,9 +166,17 @@ File my_register_filename(File fd, const char *FileName, enum file_type
else
my_errno=errno;
DBUG_PRINT("error",("Got error %d on open",my_errno));
- if (MyFlags & (MY_FFNF | MY_FAE | MY_WME))
- my_error(error_message_number, MYF(ME_BELL+ME_WAITTANG),
+ if (MyFlags & (MY_FFNF | MY_FAE | MY_WME)) {
+ if (my_errno == EMFILE) {
+ DBUG_PRINT("error",("print err: %d",EE_OUT_OF_FILERESOURCES));
+ my_error(EE_OUT_OF_FILERESOURCES, MYF(ME_BELL+ME_WAITTANG),
FileName, my_errno);
+ } else {
+ DBUG_PRINT("error",("print err: %d",error_message_number));
+ my_error(error_message_number, MYF(ME_BELL+ME_WAITTANG),
+ FileName, my_errno);
+ }
+ }
return(fd);
}
@@ -355,3 +362,24 @@ File my_sopen(const char *path, int oflag, int shflag, int pmode)
return fh; /* return handle */
}
#endif /* __WIN__ */
+
+
+#ifdef EXTRA_DEBUG
+
+void my_print_open_files(void)
+{
+ if (my_file_opened | my_stream_opened)
+ {
+ uint i;
+ for (i= 0 ; i < my_file_limit ; i++)
+ {
+ if (my_file_info[i].type != UNOPEN)
+ {
+ fprintf(stderr, EE(EE_FILE_NOT_CLOSED), my_file_info[i].name, i);
+ fputc('\n', stderr);
+ }
+ }
+ }
+}
+
+#endif
diff --git a/mysys/my_os2cond.c b/mysys/my_os2cond.c
deleted file mode 100644
index f0cf91404d2..00000000000
--- a/mysys/my_os2cond.c
+++ /dev/null
@@ -1,162 +0,0 @@
-/* Copyright (C) Yuri Dario & 2000 MySQL AB
- All the above parties has a full, independent copyright to
- the following code, including the right to use the code in
- any manner without any demands from the other parties.
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public
- License as published by the Free Software Foundation; version 2
- of the License.
-
- This library 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
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public
- License along with this library; if not, write to the Free
- Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
- MA 02111-1307, USA */
-
-/*****************************************************************************
-** The following is a simple implementation of posix conditions
-*****************************************************************************/
-
-#undef SAFE_MUTEX /* Avoid safe_mutex redefinitions */
-#include "mysys_priv.h"
-#if defined(THREAD) && defined(OS2)
-#include <m_string.h>
-#include <process.h>
-#include <sys/timeb.h>
-
-int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr)
-{
- cond->waiting= 0;
- /* Warp3 FP29 or Warp4 FP4 or better required */
- if (DosCreateEventSem(NULL, &cond->semaphore, 0x0800, 0))
- return ENOMEM;
- return 0;
-}
-
-int pthread_cond_destroy(pthread_cond_t *cond)
-{
- for (;;)
- {
- APIRET rc;
- if ((rc= DosCloseEventSem(cond->semaphore)) != 301)
- return rc ? EINVAL : 0;
- DosPostEventSem(cond->semaphore);
- }
-}
-
-
-int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
-{
- int rval= 0;
- cond->waiting++;
- if (mutex)
- pthread_mutex_unlock(mutex);
- if (DosWaitEventSem(cond->semaphore, SEM_INDEFINITE_WAIT))
- rval= EINVAL;
- if (mutex)
- pthread_mutex_lock(mutex);
- cond->waiting--;
- return rval;
-}
-
-int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
- struct timespec *abstime)
-{
- struct timeb curtime;
- int result;
- long timeout;
- int rval= 0;
-
- _ftime(&curtime);
- timeout= ((long) (abstime->ts_sec - curtime.time) * 1000L +
- (long) ((abstime->ts_nsec / 1000) - curtime.millitm) / 1000L);
- if (timeout < 0) /* Some safety */
- timeout= 0L;
-
- cond->waiting++;
-
- if (mutex)
- pthread_mutex_unlock(mutex);
- if (DosWaitEventSem(cond->semaphore, timeout) != 0)
- rval= ETIMEDOUT;
- if (mutex)
- pthread_mutex_lock(mutex);
-
- cond->waiting--;
-
- return rval;
-}
-
-
-int pthread_cond_signal(pthread_cond_t *cond)
-{
- /* Bring the next thread off the condition queue: */
- DosPostEventSem(cond->semaphore);
- return 0;
-}
-
-
-int pthread_cond_broadcast(pthread_cond_t *cond)
-{
- int i;
- /* Enter a loop to bring all threads off the condition queue */
- for (i= cond->waiting; i--;)
- DosPostEventSem(cond->semaphore);
- return 0;
-}
-
-
-int pthread_attr_init(pthread_attr_t *connect_att)
-{
- connect_att->dwStackSize= 0;
- connect_att->dwCreatingFlag= 0;
- connect_att->priority= 0;
- return 0;
-}
-
-int pthread_attr_setstacksize(pthread_attr_t *connect_att, DWORD stack)
-{
- connect_att->dwStackSize= stack;
- return 0;
-}
-
-int pthread_attr_setprio(pthread_attr_t *connect_att, int priority)
-{
- connect_att->priority= priority;
- return 0;
-}
-
-int pthread_attr_destroy(pthread_attr_t *connect_att)
-{
- bzero((gptr) connect_att, sizeof(*connect_att));
- return 0;
-}
-
-/****************************************************************************
-** Fix localtime_r() to be a bit safer
-****************************************************************************/
-
-struct tm *localtime_r(const time_t *timep, struct tm *tmp)
-{
- if (*timep == (time_t) - 1) /* This will crash win32 */
- {
- bzero(tmp, sizeof(*tmp));
- }
- else
- {
- struct tm *res= localtime(timep);
- if (!res) /* Wrong date */
- {
- bzero(tmp, sizeof(*tmp)); /* Keep things safe */
- return 0;
- }
- *tmp= *res;
- }
- return tmp;
-}
-#endif /* __WIN__ */
diff --git a/mysys/my_os2dirsrch.c b/mysys/my_os2dirsrch.c
deleted file mode 100644
index 27e774c8e04..00000000000
--- a/mysys/my_os2dirsrch.c
+++ /dev/null
@@ -1,184 +0,0 @@
-/* Copyright (C) Yuri Dario & 2000-2003 MySQL AB
- All the above parties has a full, independent copyright to
- the following code, including the right to use the code in
- any manner without any demands from the other parties.
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public
- License as published by the Free Software Foundation; version 2
- of the License.
-
- This library 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
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public
- License along with this library; if not, write to the Free
- Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
- MA 02111-1307, USA */
-
-
-/* Win32 directory search emulation */
-
-#if defined(OS2)
-
-long _findfirst( char* path, struct _finddata_t* dos_file)
-{
- HDIR hdir = HDIR_CREATE;
- APIRET rc;
- FILEFINDBUF3 buf3;
- ULONG entries = 1;
-
-#ifdef _DEBUG
- printf( "_findfirst path %s\n", path);
-#endif
-
- memset( &buf3, 0, sizeof( buf3));
- rc = DosFindFirst(
- path, /* The ASCIIZ path name of the file or subdirectory to be found. */
- &hdir, /* The handle associated with this DosFindFirst request. */
- FILE_NORMAL | FILE_DIRECTORY, /* Attribute value that determines the file objects to be searched for. */
- &buf3, /* Result buffer. */
- sizeof( buf3), /* The length, in bytes, of pfindbuf. */
- &entries, /* Pointer to the number of entries: */
- FIL_STANDARD); /* The level of file information required. */
-
-#ifdef _DEBUG
- printf( "_findfirst rc=%d hdir=%d entries=%d->%s\n", rc, hdir, entries,
- buf3.achName);
-#endif
-
- if (rc /* && entries == 0 */)
- return -1;
-
- if (dos_file)
- {
- memset( dos_file, 0, sizeof( struct _finddata_t));
- strcpy( dos_file->name, buf3.achName);
- dos_file->size = buf3.cbFile;
- dos_file->attrib = buf3.attrFile;
- }
- return (ULONG) hdir;
-}
-
-
-long _findnext( long hdir, struct _finddata_t* dos_file)
-{
- APIRET rc;
- FILEFINDBUF3 buf3;
- ULONG entries = 1;
-
- memset( &buf3, 0, sizeof( buf3));
- rc = DosFindNext(hdir,
- &buf3, /* Result buffer. */
- sizeof( buf3), /* Length, in bytes, of pfindbuf. */
- &entries); /* Pointer to the number of entries */
-
-#ifdef _DEBUG
- printf( "_findnext rc=%d hdir=%d entries=%d->%s\n", rc, hdir, entries,
- buf3.achName);
-#endif
-
- if (rc /* && entries == 0 */)
- return -1;
-
- if (dos_file)
- {
- memset( dos_file, 0, sizeof( struct _finddata_t));
- strcpy( dos_file->name, buf3.achName);
- dos_file->size = buf3.cbFile;
- dos_file->attrib = buf3.attrFile;
- }
- return 0;
-}
-
-void _findclose( long hdir)
-{
- APIRET rc;
-
- rc = DosFindClose( hdir);
-#ifdef _DEBUG
- printf( "_findclose rc=%d hdir=%d\n", rc, hdir);
-#endif
-}
-
-DIR* opendir(char* path)
-{
- DIR* dir = (DIR*) calloc(1, sizeof( DIR));
- char buffer[260];
- APIRET rc;
- ULONG entries = 1;
-
- strmov(strmov(buffer, path), "*.*");
-
-#ifdef _DEBUG
- printf( "_findfirst path %s\n", buffer);
-#endif
-
- dir->hdir = HDIR_CREATE;
- memset( &dir->buf3, 0, sizeof( dir->buf3));
- rc = DosFindFirst(
- buffer, /* Address of the ASCIIZ path name of the file or subdirectory to be found. */
- &dir->hdir, /* Address of the handle associated with this DosFindFirst request. */
- FILE_NORMAL | FILE_DIRECTORY, /* Attribute value that determines the file objects to be searched for. */
- &dir->buf3, /* Result buffer. */
- sizeof( dir->buf3), /* The length, in bytes, of pfindbuf. */
- &entries, /* Pointer to the number of entries: */
- FIL_STANDARD); /* The level of file information required. */
-
-#ifdef _DEBUG
- printf( "opendir rc=%d hdir=%d entries=%d->%s\n", rc, dir->hdir, entries, dir->buf3.achName);
-#endif
-
- if (rc /* && entries == 0 */)
- return NULL;
-
- return dir;
-}
-
-
-struct dirent* readdir( DIR* dir)
-{
- APIRET rc;
- ULONG entries = 1;
-
- if (!dir->buf3.achName[0]) /* file not found on previous query */
- return NULL;
-
- /* copy last file name */
- strcpy( dir->ent.d_name, dir->buf3.achName);
-
- /* query next file */
- memset( &dir->buf3, 0, sizeof( dir->buf3));
- rc= DosFindNext(
- dir->hdir,
- &dir->buf3, /* Result buffer. */
- sizeof(dir->buf3), /* Length, in bytes, of pfindbuf. */
- &entries); /* Pointer to the number of entries */
-
-#ifdef _DEBUG
- printf( "_findnext rc=%d hdir=%d entries=%d->%s\n", rc, dir->hdir, entries,
- dir->buf3.achName);
-#endif
-
- if (rc /* && entries == 0 */)
- *dir->buf3.achName= 0; /* reset name for next query */
-
- return &dir->ent;
-}
-
-
-int closedir (DIR *dir)
-{
- APIRET rc;
-
- rc = DosFindClose( dir->hdir);
-#ifdef _DEBUG
- printf( "_findclose rc=%d hdir=%d\n", rc, dir->hdir);
-#endif
- free(dir);
- return 0;
-}
-
-#endif /* OS2 */
diff --git a/mysys/my_os2dirsrch.h b/mysys/my_os2dirsrch.h
deleted file mode 100644
index e3af6740769..00000000000
--- a/mysys/my_os2dirsrch.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/* Copyright (C) Yuri Dario & 2000 MySQL AB
- All the above parties has a full, independent copyright to
- the following code, including the right to use the code in
- any manner without any demands from the other parties.
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public
- License as published by the Free Software Foundation; version 2
- of the License.
-
- This library 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
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public
- License along with this library; if not, write to the Free
- Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
- MA 02111-1307, USA */
-
-/* Win32 directory search emulation */
-
-#ifndef __MY_OS2DIRSRCH2_H__
-#define __MY_OS2DIRSRCH2_H__
-
-#ifdef __cplusplus_00
-extern "C" {
-#endif
-
-struct _finddata_t
-{
- unsigned attrib;
-#ifdef NOT_USED
- unsigned long time_create; /* -1 for FAT file systems */
- unsigned long time_access; /* -1 for FAT file systems */
- unsigned long time_write;
-#endif
- unsigned long size;
- char name[260];
-#ifdef NOT_USED
- uint16 wr_date;
- uint16 wr_time;
-#endif
-};
-
-
-struct dirent
-{
-#ifdef NOT_USED
- unsigned attrib;
- unsigned long time_create; /* -1 for FAT file systems */
- unsigned long time_access; /* -1 for FAT file systems */
- unsigned long time_write;
- unsigned long size;
-#endif
- char d_name[260];
-#ifdef NOT_USED
- uint16 wr_date;
- uint16 wr_time;
-#endif
-};
-
-struct DIR
-{
- HDIR hdir;
- FILEFINDBUF3 buf3;
- struct dirent ent;
-};
-
-DIR *opendir ( char *);
-struct dirent *readdir (DIR *);
-int closedir (DIR *);
-
-#ifdef NOT_USED
-#define _A_NORMAL FILE_NORMAL
-#define _A_SUBDIR FILE_DIRECTORY
-#define _A_RDONLY FILE_READONLY
-
-long _findfirst( char*, struct _finddata_t*);
-long _findnext( long, struct _finddata_t*);
-void _findclose( long);
-#endif
-
-#ifdef __cplusplus_00
-}
-#endif
-
-#endif /* __MY_OS2DIRSRCH2_H__ */
diff --git a/mysys/my_os2dlfcn.c b/mysys/my_os2dlfcn.c
deleted file mode 100644
index 74be940d0d4..00000000000
--- a/mysys/my_os2dlfcn.c
+++ /dev/null
@@ -1,74 +0,0 @@
-/* Copyright (C) Yuri Dario & 2000 MySQL AB
- All the above parties has a full, independent copyright to
- the following code, including the right to use the code in
- any manner without any demands from the other parties.
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public
- License as published by the Free Software Foundation; version 2
- of the License.
-
- This library 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
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public
- License along with this library; if not, write to the Free
- Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
- MA 02111-1307, USA */
-
-/*
- * dlfcn::Unix dynamic loading for OS/2
- *
- * Compatibility layer for dynamic loading.
- * Only minimal implementation
- *
-*/
-
-#define RTLD_LAZY 0
-#define RTLD_NOW 0
-
-void* dlopen( char* path, int flag);
-char* dlerror( void);
-void* dlsym( void* hmod, char* fn);
-void dlclose( void* hmod);
-
-char fail[ 256];
-
-void* dlopen( char* path, int flag)
-{
- APIRET rc;
- HMODULE hmod;
-
- rc = DosLoadModule( fail, sizeof( fail), path, &hmod);
- if (rc)
- return NULL;
-
- return (void*) hmod;
-}
-
-char* dlerror( void)
-{
- return fail;
-}
-
-void* dlsym( void* hmod, char* fn)
-{
- APIRET rc;
- PFN addr;
-
- rc = DosQueryProcAddr( (HMODULE) hmod, 0l, fn, &addr);
- if (rc)
- return NULL;
-
- return (void*) addr;
-}
-
-void dlclose( void* hmod)
-{
- APIRET rc;
-
- rc = DosFreeModule( (HMODULE) hmod);
-
-}
diff --git a/mysys/my_os2dlfcn.h0 b/mysys/my_os2dlfcn.h0
deleted file mode 100644
index ec05eebc47b..00000000000
--- a/mysys/my_os2dlfcn.h0
+++ /dev/null
@@ -1,39 +0,0 @@
-/* Copyright (C) Yuri Dario & 2000 MySQL AB
- All the above parties has a full, independent copyright to
- the following code, including the right to use the code in
- any manner without any demands from the other parties.
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public
- License as published by the Free Software Foundation; version 2
- of the License.
-
- This library 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
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public
- License along with this library; if not, write to the Free
- Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
- MA 02111-1307, USA */
-/*
- * dlfcn::Unix dynamic loading for OS/2
- *
- * Compatibility layer for dynamic loading.
- * Only minimal implementation
- *
-*/
-
-#ifndef __DLFCN_H__
-#define __DLFCN_H__
-
-#define RTLD_LAZY 0
-#define RTLD_NOW 0
-
-void* dlopen( char* path, int flag);
-char* dlerror( void);
-void* dlsym( void* hmod, char* fn);
-void dlclose( void* hmod);
-
-#endif
diff --git a/mysys/my_os2file64.c b/mysys/my_os2file64.c
deleted file mode 100644
index 52156903b80..00000000000
--- a/mysys/my_os2file64.c
+++ /dev/null
@@ -1,394 +0,0 @@
-/* Copyright (C) Yuri Dario & 2000 MySQL AB
- All the above parties has a full, independent copyright to
- the following code, including the right to use the code in
- any manner without any demands from the other parties.
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public
- License as published by the Free Software Foundation; version 2
- of the License.
-
- This library 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
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public
- License along with this library; if not, write to the Free
- Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
- MA 02111-1307, USA */
-
-void _OS2errno( APIRET rc);
-longlong _lseek64( int fd, longlong offset, int seektype);
-int _lock64( int fd, int locktype, my_off_t start,
- my_off_t length, myf MyFlags);
-int _sopen64( const char *name, int oflag, int shflag, int mask);
-
-/*
- This class is used to define a global c++ variable, that
- is initialized before main() gets called.
-*/
-
-class File64bit
-{
- public:
- File64bit(); /* default constructor */
-} initFile64bit;
-
-static APIRET (* APIENTRY _DosOpenL)(PCSZ pszFileName,
- PHFILE phf,
- PULONG pulAction,
- LONGLONG cbFile,
- ULONG ulAttribute,
- ULONG fsOpenFlags,
- ULONG fsOpenMode,
- PEAOP2 peaop2);
-static APIRET (* APIENTRY _DosSetFilePtrL)(HFILE hFile,
- LONGLONG ib,
- ULONG method,
- PLONGLONG ibActual);
-static APIRET (* APIENTRY _DosSetFileLocksL)(HFILE hFile,
- PFILELOCKL pflUnlock,
- PFILELOCKL pflLock,
- ULONG timeout,
- ULONG flags);
-
-#define EIO EINVAL
-#define ESPIPE EBADSEEK
-
-
-static unsigned char const errno_tab[] =
-{
- 0 , EINVAL, ENOENT, ENOENT, EMFILE, /* 0..4 */
- EACCES, EBADF, EIO, ENOMEM, EIO, /* 5..9 */
- EINVAL, ENOEXEC,EINVAL, EINVAL, EINVAL, /* 10..14 */
- ENOENT, EBUSY, EXDEV, ENOENT, EROFS, /* 15..19 */
- EIO, EIO, EIO, EIO, EIO, /* 20..24 */
- EIO, EIO, EIO, ENOSPC, EIO, /* 25..29 */
- EIO, EIO, EACCES, EACCES, EIO, /* 30..34 */
- EIO, EIO, EIO, EIO, ENOSPC, /* 35..39 */
- EIO, EIO, EIO, EIO, EIO, /* 40..44 */
- EIO, EIO, EIO, EIO, EIO, /* 45..49 */
- EIO, EIO, EIO, EIO, EBUSY, /* 50..54 */
- EIO, EIO, EIO, EIO, EIO, /* 55..59 */
- EIO, ENOSPC, ENOSPC, EIO, EIO, /* 60..64 */
- EACCES, EIO, EIO, EIO, EIO, /* 65..69 */
- EIO, EIO, EIO, EROFS, EIO, /* 70..74 */
- EIO, EIO, EIO, EIO, EIO, /* 75..79 */
- EEXIST, EIO, ENOENT, EIO, EIO, /* 80..84 */
- EIO, EIO, EINVAL, EIO, EAGAIN, /* 85..89 */
- EIO, EIO, EIO, EIO, EIO, /* 90..94 */
- EINTR, EIO, EIO, EIO, EACCES, /* 95..99 */
- ENOMEM, EINVAL, EINVAL, ENOMEM, EINVAL, /* 100..104 */
- EINVAL, ENOMEM, EIO, EACCES, EPIPE, /* 105..109 */
- ENOENT, E2BIG, ENOSPC, ENOMEM, EBADF, /* 110..114 */
- EINVAL, EINVAL, EINVAL, EINVAL, EINVAL, /* 115..119 */
- EINVAL, EINVAL, EINVAL, ENOENT, EINVAL, /* 120..124 */
- ENOENT, ENOENT, ENOENT, ECHILD, ECHILD, /* 125..129 */
- EACCES, EINVAL, ESPIPE, EINVAL, EINVAL, /* 130..134 */
- EINVAL, EINVAL, EINVAL, EINVAL, EINVAL, /* 135..139 */
- EINVAL, EINVAL, EBUSY, EINVAL, EINVAL, /* 140..144 */
- EINVAL, EINVAL, EINVAL, EBUSY, EINVAL, /* 145..149 */
- EINVAL, EINVAL, ENOMEM, EINVAL, EINVAL, /* 150..154 */
- EINVAL, EINVAL, EINVAL, EINVAL, EINVAL, /* 155..159 */
- EINVAL, EINVAL, EINVAL, EINVAL, EAGAIN, /* 160..164 */
- EINVAL, EINVAL, EACCES, EINVAL, EINVAL, /* 165..169 */
- EBUSY, EINVAL, EINVAL, EINVAL, EINVAL, /* 170..174 */
- EINVAL, EINVAL, EINVAL, EINVAL, EINVAL, /* 175..179 */
- EINVAL, EINVAL, EINVAL, EINVAL, ECHILD, /* 180..184 */
- EINVAL, EINVAL, ENOENT, EINVAL, EINVAL, /* 185..189 */
- ENOEXEC,ENOEXEC,ENOEXEC,ENOEXEC,ENOEXEC, /* 190..194 */
- ENOEXEC,ENOEXEC,ENOEXEC,ENOEXEC,ENOEXEC, /* 195..199 */
- ENOEXEC,ENOEXEC,ENOEXEC,ENOENT, EINVAL, /* 200..204 */
- EINVAL, ENAMETOOLONG, EINVAL, EINVAL, EINVAL, /* 205..209 */
- EINVAL, EINVAL, EACCES, ENOEXEC,ENOEXEC, /* 210..214 */
- EINVAL, EINVAL, EINVAL, EINVAL, EINVAL, /* 215..219 */
- EINVAL, EINVAL, EINVAL, EINVAL, EINVAL, /* 220..224 */
- EINVAL, EINVAL, EINVAL, ECHILD, EINVAL, /* 225..229 */
- EINVAL, EBUSY, EAGAIN, ENOTCONN, EINVAL, /* 230..234 */
- EINVAL, EINVAL, EINVAL, EINVAL, EINVAL, /* 235..239 */
- EINVAL, EINVAL, EINVAL, EINVAL, EINVAL, /* 240..244 */
- EINVAL, EINVAL, EINVAL, EINVAL, EINVAL, /* 245..249 */
- EACCES, EACCES, EINVAL, ENOENT, EINVAL, /* 250..254 */
- EINVAL, EINVAL, EINVAL, EINVAL, EINVAL, /* 255..259 */
- EINVAL, EINVAL, EINVAL, EINVAL, EINVAL, /* 260..264 */
- EINVAL, EINVAL, EINVAL, EINVAL, EINVAL, /* 265..269 */
- EINVAL, EINVAL, EINVAL, EINVAL, EINVAL, /* 270..274 */
- EINVAL, EINVAL, EINVAL, EINVAL, EINVAL, /* 275..279 */
- EINVAL, EINVAL, EINVAL, EINVAL, EEXIST, /* 280..284 */
- EEXIST, EINVAL, EINVAL, EINVAL, EINVAL, /* 285..289 */
- ENOMEM, EMFILE, EINVAL, EINVAL, EINVAL, /* 290..294 */
- EINVAL, EINVAL, EINVAL, EINVAL, EINVAL, /* 295..299 */
- EINVAL, EBUSY, EINVAL, ESRCH, EINVAL, /* 300..304 */
- ESRCH, EINVAL, EINVAL, EINVAL, ESRCH, /* 305..309 */
- EINVAL, ENOMEM, EINVAL, EINVAL, EINVAL, /* 310..314 */
- EINVAL, E2BIG, ENOENT, EIO, EIO, /* 315..319 */
- EINVAL, EINVAL, EINVAL, EINVAL, EAGAIN, /* 320..324 */
- EINVAL, EINVAL, EINVAL, EIO, ENOENT, /* 325..329 */
- EACCES, EACCES, EACCES, ENOENT, ENOMEM /* 330..334 */
-};
-
-/*
- * Initialize 64bit file access: dynamic load of WSeB API
-*/
- File64bit :: File64bit()
-{
- HMODULE hDoscalls;
-
- if (DosQueryModuleHandle("DOSCALLS", &hDoscalls) != NO_ERROR)
- return;
-
- if (DosQueryProcAddr(hDoscalls, 981, NULL, (PFN *)&_DosOpenL) != NO_ERROR)
- return;
-
- if (DosQueryProcAddr(hDoscalls, 988, NULL, (PFN *)&_DosSetFilePtrL) != NO_ERROR) {
- _DosOpenL = NULL;
- return;
- }
-
- if (DosQueryProcAddr(hDoscalls, 986, NULL, (PFN *)&_DosSetFileLocksL) != NO_ERROR) {
- _DosOpenL = NULL;
- _DosSetFilePtrL = NULL;
- return;
- }
- /* notify success */
-#ifdef MYSQL_SERVER
- printf( "WSeB 64bit file API loaded.\n");
-#endif
-}
-
-void _OS2errno( APIRET rc)
-{
- if (rc >= sizeof (errno_tab))
- errno = EINVAL;
- else
- errno = errno_tab[rc];
-}
-
-
-longlong _lseek64( int fd, longlong offset, int seektype)
-{
- APIRET rc;
- longlong actual;
-
- if (_DosSetFilePtrL)
- rc = _DosSetFilePtrL( fd, offset, seektype, &actual);
- else
- {
- ULONG ulActual;
- rc = DosSetFilePtr( fd, (long) offset, seektype, &ulActual);
- actual = ulActual;
- }
-
- if (!rc)
- return( actual); /* NO_ERROR */
-
- _OS2errno( rc); /* set errno */
- return(-1); /* seek failed */
-}
-
-
-inline APIRET _SetFileLocksL(HFILE hFile,
- PFILELOCKL pflUnlock,
- PFILELOCKL pflLock,
- ULONG timeout,
- ULONG flags)
-{
- if (_DosSetFileLocksL)
- {
- APIRET rc;
- rc = _DosSetFileLocksL( hFile, pflUnlock, pflLock, timeout, flags);
-
- /*
- on FAT/HPFS/LAN a INVALID_PARAMETER is returned, seems that
- only JFS can handle >2GB ranges.
- */
- if (rc != 87)
- return rc;
- /* got INVALID_PARAMETER, fallback to standard call */
- }
-
- FILELOCK flUnlock = { pflUnlock->lOffset, pflUnlock->lRange };
- FILELOCK flLock = { pflLock->lOffset, pflLock->lRange };
- return DosSetFileLocks( hFile, &flUnlock, &flLock, timeout, flags);
-}
-
-
-int _lock64( int fd, int locktype, my_off_t start,
- my_off_t length, myf MyFlags)
-{
- FILELOCKL LockArea = {0,0}, UnlockArea = {0,0};
- ULONG readonly = 0;
- APIRET rc = -1;
-
- switch (locktype) {
- case F_UNLCK:
- UnlockArea.lOffset = start;
- UnlockArea.lRange = length ? length : LONGLONG_MAX;
- break;
-
- case F_RDLCK:
- case F_WRLCK:
- LockArea.lOffset = start;
- LockArea.lRange = length ? length : LONGLONG_MAX;
- readonly = (locktype == F_RDLCK ? 1 : 0);
- break;
-
- default:
- errno = EINVAL;
- rc = -1;
- break;
- }
-
- if (MyFlags & MY_DONT_WAIT)
- {
- rc = _SetFileLocksL( fd, &UnlockArea, &LockArea, 0, readonly);
- /* printf("fd %d, locktype %d, rc %d (dont_wait)\n", fd, locktype, rc); */
- if (rc == 33) { /* Lock Violation */
-
- DBUG_PRINT("info",("Was locked, trying with timeout"));
- rc = _SetFileLocksL( fd, &UnlockArea, &LockArea, 1 * 1000, readonly);
- /* printf( "fd %d, locktype %d, rc %d (dont_wait with timeout)\n", fd, locktype, rc); */
- }
- }
- else
- {
- while (rc = _SetFileLocksL( fd, &UnlockArea, &LockArea, 0, readonly) &&
- (rc == 33))
- {
- printf(".");
- DosSleep(1 * 1000);
- }
- /* printf( "fd %d, locktype %d, rc %d (wait2)\n", fd, locktype, rc); */
- }
- if (!rc)
- return(0); /* NO_ERROR */
- _OS2errno( rc); /* set errno */
- return(-1); /* lock failed */
-}
-
-
-int sopen(const char *name, int oflag, int shflag, int mask)
-{
- int fail_errno;
- APIRET rc = 0;
- HFILE hf = 0;
- ULONG ulAction = 0;
- LONGLONG cbFile = 0;
- ULONG ulAttribute = FILE_NORMAL;
- ULONG fsOpenFlags = 0;
- ULONG fsOpenMode = 0;
-
- /* Extract the access mode and sharing mode bits. */
- fsOpenMode = (shflag & 0xFF) | (oflag & 0x03);
-
- /*
- Translate ERROR_OPEN_FAILED to ENOENT unless O_EXCL is set (see
- below).
- */
- fail_errno = ENOENT;
-
- /*
- Compute `open_flag' depending on `flags'. Note that _SO_CREAT is
- set for O_CREAT.
- */
-
- if (oflag & O_CREAT)
- {
- if (oflag & O_EXCL)
- {
- fsOpenFlags = OPEN_ACTION_FAIL_IF_EXISTS | OPEN_ACTION_CREATE_IF_NEW;
- fail_errno = EEXIST;
- }
- else if (oflag & O_TRUNC)
- fsOpenFlags = OPEN_ACTION_REPLACE_IF_EXISTS | OPEN_ACTION_CREATE_IF_NEW;
- else
- fsOpenFlags = OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_CREATE_IF_NEW;
-
- if (mask & S_IWRITE)
- ulAttribute = FILE_NORMAL;
- else
- ulAttribute = FILE_READONLY;
-
- }
- else if (oflag & O_TRUNC)
- fsOpenFlags = OPEN_ACTION_REPLACE_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW;
- else
- fsOpenFlags = OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW;
-
- /* Try to open the file and handle errors. */
- if (_DosOpenL)
- rc = _DosOpenL( name, &hf, &ulAction, cbFile,
- ulAttribute, fsOpenFlags, fsOpenMode, NULL);
- else
- rc = DosOpen( name, &hf, &ulAction, (LONG) cbFile,
- ulAttribute, fsOpenFlags, fsOpenMode, NULL);
-
- if (rc == ERROR_OPEN_FAILED)
- {
- errno = fail_errno;
- return -1;
- }
- if (rc != 0)
- {
- _OS2errno( rc); /* set errno */
- return -1;
- }
- if (oflag & O_APPEND)
- _lseek64( hf, 0L, SEEK_END);
- return hf;
-}
-
-
-int read(int fd, void *buffer, unsigned int count)
-{
- APIRET rc;
- ULONG actual;
-
- rc= DosRead( fd, (PVOID) buffer, count, &actual);
-
- if (!rc)
- return( actual); /* NO_ERROR */
- _OS2errno( rc); /* set errno */
- return(-1); /* read failed */
-}
-
-
-int write(int fd, const void *buffer, unsigned int count)
-{
- APIRET rc;
- ULONG actual;
-
- rc = DosWrite( fd, (PVOID) buffer, count, &actual);
-
- if (!rc)
- return( actual); /* NO_ERROR */
- _OS2errno( rc); /* set errno */
- return(-1); /* write failed */
-}
-
-
-int close( int fd)
-{
- APIRET rc;
- ULONG actual;
-
- rc = DosClose( fd);
-
- if (!rc)
- return( 0); /* NO_ERROR */
- _OS2errno( rc); /* set errno */
- return(-1); /* close failed */
-}
-
-
-int open( const char *name, int oflag)
-{
- return sopen( name, oflag, OPEN_SHARE_DENYNONE, S_IREAD | S_IWRITE);
-}
-
-
-int open( const char *name, int oflag, int mask)
-{
- return sopen( name, oflag, OPEN_SHARE_DENYNONE, mask);
-}
diff --git a/mysys/my_os2mutex.c b/mysys/my_os2mutex.c
deleted file mode 100644
index 5010d6e8dd5..00000000000
--- a/mysys/my_os2mutex.c
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>.
- * All rights reserved.
- *
- * Modified and extended by Antony T Curtis <antony.curtis@olcs.net>
- * for use with OS/2.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by John Birrell.
- * 4. Neither the name of the author nor the names of any co-contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- */
-#include <stdlib.h>
-#include <errno.h>
-#ifdef _THREAD_SAFE
-
-int
-pthread_mutex_init(pthread_mutex_t * mutex,
- const pthread_mutexattr_t * mutex_attr)
-{
- (void) DosCreateMutexSem(NULL,mutex,0,0);
- return (0); /* Return the completion status: */
-}
-
-
-int
-pthread_mutex_destroy(pthread_mutex_t * mutex)
-{
- APIRET rc;
-
- do
- {
- rc = DosCloseMutexSem(*mutex);
- if (rc == 301) DosReleaseMutexSem(*mutex);
- } while (rc == 301);
-
- *mutex = 0;
- return (0); /* Return the completion status: */
-}
-
-
-int
-pthread_mutex_lock(pthread_mutex_t * mutex)
-{
- APIRET rc;
-
- rc = DosRequestMutexSem(*mutex,SEM_INDEFINITE_WAIT);
- if (rc)
- return(EINVAL);
- return (0); /* Return the completion status: */
-}
-
-
-int
-pthread_mutex_unlock(pthread_mutex_t * mutex)
-{
- (void) DosReleaseMutexSem(*mutex);
- return (0); /* Return the completion status: */
-}
-#endif
diff --git a/mysys/my_os2thread.c b/mysys/my_os2thread.c
deleted file mode 100644
index e34fd18d596..00000000000
--- a/mysys/my_os2thread.c
+++ /dev/null
@@ -1,128 +0,0 @@
-/* Copyright (C) Yuri Dario & 2000 MySQL AB
- All the above parties has a full, independent copyright to
- the following code, including the right to use the code in
- any manner without any demands from the other parties.
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public
- License as published by the Free Software Foundation; version 2
- of the License.
-
- This library 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
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public
- License along with this library; if not, write to the Free
- Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
- MA 02111-1307, USA */
-
-/*****************************************************************************
-** Simulation of posix threads calls for OS/2
-*****************************************************************************/
-
-#include "mysys_priv.h"
-#if defined(THREAD) && defined(OS2)
-#include <m_string.h>
-#include <process.h>
-
-static pthread_mutex_t THR_LOCK_thread;
-
-struct pthread_map
-{
- HANDLE pthreadself;
- pthread_handler func;
- void * param;
-};
-
-void win_pthread_init(void)
-{
- pthread_mutex_init(&THR_LOCK_thread,NULL);
-}
-
-/*
-** We have tried to use '_beginthreadex' instead of '_beginthread' here
-** but in this case the program leaks about 512 characters for each
-** created thread !
-** As we want to save the created thread handler for other threads to
-** use and to be returned by pthread_self() (instead of the Win32 pseudo
-** handler), we have to go trough pthread_start() to catch the returned handler
-** in the new thread.
-*/
-
-pthread_handler_t pthread_start(void *param)
-{
- DBUG_ENTER("pthread_start");
- pthread_handler func=((struct pthread_map *) param)->func;
- void *func_param=((struct pthread_map *) param)->param;
- my_thread_init(); /* Will always succeed in windows */
- pthread_mutex_lock(&THR_LOCK_thread); /* Wait for beginthread to return */
- win_pthread_self=((struct pthread_map *) param)->pthreadself;
- pthread_mutex_unlock(&THR_LOCK_thread);
- free((char*) param); /* Free param from create */
- /* pthread_exit((void*) (*func)(func_param)); */
- (*func)(func_param);
- DBUG_RETURN(0);
-}
-
-
-int pthread_create(pthread_t *thread_id, pthread_attr_t *attr,
- pthread_handler func, void *param)
-{
- HANDLE hThread;
- struct pthread_map *map;
- DBUG_ENTER("pthread_create");
-
- if (!(map=(struct pthread_map *)malloc(sizeof(*map))))
- DBUG_RETURN(-1);
- map->func=func;
- map->param=param;
- pthread_mutex_lock(&THR_LOCK_thread);
-#ifdef __BORLANDC__
- hThread=(HANDLE)_beginthread((void(_USERENTRY *)(void *)) pthread_start,
- attr->dwStackSize ? attr->dwStackSize :
- 65535, (void*) map);
-#elif defined( OS2)
- hThread=(HANDLE)_beginthread((void( _Optlink *)(void *)) pthread_start, NULL,
- attr->dwStackSize ? attr->dwStackSize :
- 65535, (void*) map);
-#else
- hThread=(HANDLE)_beginthread((void( __cdecl *)(void *)) pthread_start,
- attr->dwStackSize ? attr->dwStackSize :
- 65535, (void*) map);
-#endif
- DBUG_PRINT("info", ("hThread=%lu",(long) hThread));
- *thread_id=map->pthreadself=hThread;
- pthread_mutex_unlock(&THR_LOCK_thread);
-
- if (hThread == (HANDLE) -1)
- {
- int error=errno;
- DBUG_PRINT("error",
- ("Can't create thread to handle request (error %d)",error));
- DBUG_RETURN(error ? error : -1);
- }
-#ifdef OS2
- my_pthread_setprio(hThread, attr->priority);
-#else
- VOID(SetThreadPriority(hThread, attr->priority)) ;
-#endif
- DBUG_RETURN(0);
-}
-
-
-void pthread_exit(void *a)
-{
- _endthread();
-}
-
-/* This is neaded to get the macro pthread_setspecific to work */
-
-int win_pthread_setspecific(void *a,void *b,uint length)
-{
- memcpy(a,b,length);
- return 0;
-}
-
-#endif
diff --git a/mysys/my_os2tls.c b/mysys/my_os2tls.c
deleted file mode 100644
index 49100c65516..00000000000
--- a/mysys/my_os2tls.c
+++ /dev/null
@@ -1,151 +0,0 @@
-/* Copyright (C) Yuri Dario & 2000 MySQL AB
- All the above parties has a full, independent copyright to
- the following code, including the right to use the code in
- any manner without any demands from the other parties.
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public
- License as published by the Free Software Foundation; version 2
- of the License.
-
- This library 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
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public
- License along with this library; if not, write to the Free
- Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
- MA 02111-1307, USA */
-
-DWORD TlsAlloc( void);
-BOOL TlsFree( DWORD);
-PVOID TlsGetValue( DWORD);
-BOOL TlsSetValue( DWORD, PVOID);
-
-#define TLS_MINIMUM_AVAILABLE 64
-
-
-PULONG tls_storage; /* TLS local storage */
-DWORD tls_bits[2]; /* TLS in-use bits */
-pthread_mutex_t tls_mutex; /* TLS mutex for in-use bits */
-
-
-DWORD TlsAlloc( void)
-{
- DWORD index = -1;
- DWORD mask, tibidx;
- int i;
-
- if (tls_storage == NULL)
- {
-
- APIRET rc;
-
- /* allocate memory for TLS storage */
- rc = DosAllocThreadLocalMemory( 1, &tls_storage);
- if (rc)
- fprintf( stderr, "DosAllocThreadLocalMemory error: return code = %u\n",
- rc);
- /* create a mutex */
- if (pthread_mutex_init( &tls_mutex, NULL))
- fprintf( stderr, "Failed to init TLS mutex\n");
- }
-
- pthread_mutex_lock( &tls_mutex);
-
- tibidx = 0;
- if (tls_bits[0] == 0xFFFFFFFF)
- {
- if (tls_bits[1] == 0xFFFFFFFF)
- {
- fprintf( stderr, "tid#%d, no more TLS bits available\n", _threadid);
- pthread_mutex_unlock( &tls_mutex);
- return -1;
- }
- tibidx = 1;
- }
-
- for (i=0; i<32; i++)
- {
- mask = (1 << i);
- if ((tls_bits[ tibidx] & mask) == 0)
- {
- tls_bits[ tibidx] |= mask;
- index = (tibidx*32) + i;
- break;
- }
- }
- tls_storage[index] = 0;
-
- pthread_mutex_unlock( &tls_mutex);
- /* fprintf( stderr, "tid#%d, TlsAlloc index %d\n", _threadid, index); */
- return index;
-}
-
-BOOL TlsFree( DWORD index)
-{
- int tlsidx;
- DWORD mask;
-
- if (index >= TLS_MINIMUM_AVAILABLE)
- return NULL;
-
- pthread_mutex_lock( &tls_mutex);
-
- tlsidx = 0;
- if (index > 32)
- tlsidx++;
-
- mask = (1 << index);
- if (tls_bits[ tlsidx] & mask)
- {
- tls_bits[tlsidx] &= ~mask;
- tls_storage[index] = 0;
- pthread_mutex_unlock( &tls_mutex);
- return TRUE;
- }
-
- pthread_mutex_unlock( &tls_mutex);
- return FALSE;
-}
-
-
-PVOID TlsGetValue( DWORD index)
-{
- if (index >= TLS_MINIMUM_AVAILABLE)
- return NULL;
-
- /* verify if memory has been allocated for this thread */
- if (*tls_storage == NULL)
- {
- /* allocate memory for indexes */
- *tls_storage = (ULONG)calloc( TLS_MINIMUM_AVAILABLE, sizeof(int));
- /* fprintf(stderr, "tid#%d, tls_storage %x\n", _threadid, *tls_storage); */
- }
-
- ULONG* tls_array = (ULONG*) *tls_storage;
- return (PVOID) tls_array[index];
-}
-
-
-BOOL TlsSetValue( DWORD index, PVOID val)
-{
-
- /* verify if memory has been allocated for this thread */
- if (*tls_storage == NULL)
- {
- /* allocate memory for indexes */
- *tls_storage = (ULONG)calloc( TLS_MINIMUM_AVAILABLE, sizeof(int));
- /* fprintf(stderr, "tid#%d, tls_storage %x\n", _threadid, *tls_storage); */
- }
-
- if (index >= TLS_MINIMUM_AVAILABLE)
- return FALSE;
-
- ULONG* tls_array = (ULONG*) *tls_storage;
- /* fprintf( stderr, "tid#%d, TlsSetValue array %08x index %d -> %08x (old)\n", _threadid, tls_array, index, tls_array[ index]); */
- tls_array[ index] = (ULONG) val;
- /* fprintf( stderr, "tid#%d, TlsSetValue array %08x index %d -> %08x\n", _threadid, tls_array, index, val); */
- return TRUE;
-}
diff --git a/mysys/my_pread.c b/mysys/my_pread.c
index 2d42a6ebbc4..f8f0fa49c10 100644
--- a/mysys/my_pread.c
+++ b/mysys/my_pread.c
@@ -26,7 +26,7 @@ uint my_pread(File Filedes, byte *Buffer, uint Count, my_off_t offset,
myf MyFlags)
{
uint readbytes;
- int error;
+ int error= 0;
DBUG_ENTER("my_pread");
DBUG_PRINT("my",("Fd: %d Seek: %lu Buffer: 0x%lx Count: %u MyFlags: %d",
Filedes, (ulong) offset, (long) Buffer, Count, MyFlags));
@@ -37,17 +37,40 @@ uint my_pread(File Filedes, byte *Buffer, uint Count, my_off_t offset,
errno=0; /* Linux doesn't reset this */
#endif
#ifndef HAVE_PREAD
+ os_off_t old_offset;
+
pthread_mutex_lock(&my_file_info[Filedes].mutex);
- readbytes= (uint) -1;
- error= (lseek(Filedes, offset, MY_SEEK_SET) == -1L ||
- (readbytes = (uint) read(Filedes, Buffer, Count)) != Count);
+ /*
+ As we cannot change the file pointer, we save the old position,
+ before seeking to the given offset
+ */
+
+ error= (old_offset= lseek(Filedes, 0L, MY_SEEK_CUR)) == -1L ||
+ lseek(Filedes, offset, MY_SEEK_SET) == -1L;
+
+ if (!error) /* Seek was successful */
+ {
+ if ((readbytes = (uint) read(Filedes, Buffer, Count)) == -1L)
+ my_errno= errno;
+
+ /*
+ We should seek back, even if read failed. If this fails,
+ we will return an error. If read failed as well, we will
+ save the errno from read, not from lseek().
+ */
+ if ((error= (lseek(Filedes, old_offset, MY_SEEK_SET) == -1L)) &&
+ readbytes != -1L)
+ my_errno= errno;
+ }
+
pthread_mutex_unlock(&my_file_info[Filedes].mutex);
#else
- error=((readbytes = (uint) pread(Filedes, Buffer, Count, offset)) != Count);
+ if ((error= ((readbytes =
+ (uint) pread(Filedes, Buffer, Count, offset)) != Count)))
+ my_errno= errno;
#endif
- if (error)
+ if (error || readbytes != Count)
{
- my_errno=errno;
DBUG_PRINT("warning",("Read only %d bytes off %u from %d, errno: %d",
(int) readbytes, Count,Filedes,my_errno));
#ifdef THREAD
@@ -92,17 +115,41 @@ uint my_pwrite(int Filedes, const byte *Buffer, uint Count, my_off_t offset,
for (;;)
{
#ifndef HAVE_PREAD
- int error;
+ int error= 0;
+ os_off_t old_offset;
writenbytes= (uint) -1;
pthread_mutex_lock(&my_file_info[Filedes].mutex);
- error=(lseek(Filedes, offset, MY_SEEK_SET) != -1L &&
- (writenbytes = (uint) write(Filedes, Buffer, Count)) == Count);
+
+ /*
+ As we cannot change the file pointer, we save the old position,
+ before seeking to the given offset
+ */
+ error= ((old_offset= lseek(Filedes, 0L, MY_SEEK_CUR)) == -1L ||
+ lseek(Filedes, offset, MY_SEEK_SET) == -1L);
+
+ if (!error) /* Seek was successful */
+ {
+ if ((writenbytes = (uint) write(Filedes, Buffer, Count)) == -1L)
+ my_errno= errno;
+
+ /*
+ We should seek back, even if write failed. If this fails,
+ we will return an error. If write failed as well, we will
+ save the errno from write, not from lseek().
+ */
+ if ((error= (lseek(Filedes, old_offset, MY_SEEK_SET) == -1L)) &&
+ writenbytes != -1L)
+ my_errno= errno;
+ }
pthread_mutex_unlock(&my_file_info[Filedes].mutex);
- if (error)
+
+ if (!error && writenbytes == Count)
break;
#else
if ((writenbytes = (uint) pwrite(Filedes, Buffer, Count,offset)) == Count)
break;
+ else
+ my_errno= errno;
#endif
if ((int) writenbytes != -1)
{ /* Safegueard */
@@ -111,7 +158,6 @@ uint my_pwrite(int Filedes, const byte *Buffer, uint Count, my_off_t offset,
Count-=writenbytes;
offset+=writenbytes;
}
- my_errno=errno;
DBUG_PRINT("error",("Write only %d bytes",writenbytes));
#ifndef NO_BACKGROUND
#ifdef THREAD
diff --git a/mysys/my_pthread.c b/mysys/my_pthread.c
index 0422e0c6512..aba3e47d754 100644
--- a/mysys/my_pthread.c
+++ b/mysys/my_pthread.c
@@ -23,7 +23,7 @@
#include <m_string.h>
#include <thr_alarm.h>
-#if (defined(__BSD__) || defined(_BSDI_VERSION)) && !defined(HAVE_mit_thread)
+#if (defined(__BSD__) || defined(_BSDI_VERSION))
#define SCHED_POLICY SCHED_RR
#else
#define SCHED_POLICY SCHED_OTHER
@@ -51,8 +51,6 @@ int my_pthread_getprio(pthread_t thread_id)
int policy;
if (!pthread_getschedparam(thread_id,&policy,&tmp_sched_param))
{
- DBUG_PRINT("thread",("policy: %d priority: %d",
- policy,tmp_sched_param.sched_priority));
return tmp_sched_param.sched_priority;
}
#endif
@@ -77,9 +75,6 @@ void my_pthread_attr_setprio(pthread_attr_t *attr, int priority)
#ifdef HAVE_NONPOSIX_PTHREAD_GETSPECIFIC
#undef pthread_getspecific
-#ifdef HAVE_UNIXWARE7_THREADS
-#define pthread_getspecific thr_getspecific
-#endif
void *my_pthread_getspecific_imp(pthread_key_t key)
{
@@ -191,7 +186,7 @@ struct tm *gmtime_r(const time_t *clock, struct tm *res)
** Author: Gary Wisniewski <garyw@spidereye.com.au>, much modified by Monty
****************************************************************************/
-#if !defined(HAVE_SIGWAIT) && !defined(HAVE_mit_thread) && !defined(sigwait) && !defined(__WIN__) && !defined(HAVE_rts_threads) && !defined(HAVE_NONPOSIX_SIGWAIT) && !defined(HAVE_DEC_3_2_THREADS) && !defined(OS2)
+#if !defined(HAVE_SIGWAIT) && !defined(sigwait) && !defined(__WIN__) && !defined(HAVE_rts_threads) && !defined(HAVE_NONPOSIX_SIGWAIT) && !defined(HAVE_DEC_3_2_THREADS)
#if !defined(DONT_USE_SIGSUSPEND)
@@ -430,7 +425,7 @@ int sigwait(sigset_t *setp, int *sigp)
** Patches for AIX and DEC OSF/1 3.2
*****************************************************************************/
-#if (defined(HAVE_NONPOSIX_PTHREAD_MUTEX_INIT) && !defined(HAVE_UNIXWARE7_THREADS)) || defined(HAVE_DEC_3_2_THREADS)
+#if defined(HAVE_NONPOSIX_PTHREAD_MUTEX_INIT)
#include <netdb.h>
diff --git a/mysys/my_redel.c b/mysys/my_redel.c
index 0aec395cf65..b12cf098283 100644
--- a/mysys/my_redel.c
+++ b/mysys/my_redel.c
@@ -13,7 +13,6 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-#define USES_TYPES /* sys/types is included */
#include "mysys_priv.h"
#include <my_dir.h>
#include <m_string.h>
@@ -89,14 +88,14 @@ int my_copystat(const char *from, const char *to, int MyFlags)
return 1;
VOID(chmod(to, statbuf.st_mode & 07777)); /* Copy modes */
-#if !defined(MSDOS) && !defined(__WIN__) && !defined(__EMX__) && !defined(OS2) && !defined(__NETWARE__)
+#if !defined(__WIN__) && !defined(__NETWARE__)
if (statbuf.st_nlink > 1 && MyFlags & MY_LINK_WARNING)
{
if (MyFlags & MY_LINK_WARNING)
my_error(EE_LINK_WARNING,MYF(ME_BELL+ME_WAITTANG),from,statbuf.st_nlink);
}
VOID(chown(to, statbuf.st_uid, statbuf.st_gid)); /* Copy ownership */
-#endif /* MSDOS */
+#endif /* !__WIN__ && !__NETWARE__ */
#ifndef VMS
#ifndef __ZTC__
diff --git a/mysys/my_rename.c b/mysys/my_rename.c
index c4aeb95b2c0..6a6aa6a5796 100644
--- a/mysys/my_rename.c
+++ b/mysys/my_rename.c
@@ -13,7 +13,6 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-#define USES_TYPES
#include "mysys_priv.h"
#include <my_dir.h>
#include "mysys_err.h"
diff --git a/mysys/my_seek.c b/mysys/my_seek.c
index 3d415400aa2..0d30ba203ae 100644
--- a/mysys/my_seek.c
+++ b/mysys/my_seek.c
@@ -45,7 +45,7 @@
my_off_t my_seek(File fd, my_off_t pos, int whence,
myf MyFlags __attribute__((unused)))
{
- reg1 os_off_t newpos;
+ reg1 os_off_t newpos= -1;
DBUG_ENTER("my_seek");
DBUG_PRINT("my",("Fd: %d Hpos: %lu Pos: %lu Whence: %d MyFlags: %d",
fd, (ulong) (((ulonglong) pos) >> 32), (ulong) pos,
diff --git a/mysys/my_sleep.c b/mysys/my_sleep.c
index 50e6fae17fc..87170e4af41 100644
--- a/mysys/my_sleep.c
+++ b/mysys/my_sleep.c
@@ -24,8 +24,6 @@ void my_sleep(ulong m_seconds)
delay(m_seconds/1000+1);
#elif defined(__WIN__)
Sleep(m_seconds/1000+1); /* Sleep() has millisecond arg */
-#elif defined(OS2)
- DosSleep(m_seconds/1000+1);
#elif defined(HAVE_SELECT)
struct timeval t;
t.tv_sec= m_seconds / 1000000L;
diff --git a/mysys/my_static.c b/mysys/my_static.c
index 77dbffb911e..77243611c03 100644
--- a/mysys/my_static.c
+++ b/mysys/my_static.c
@@ -18,7 +18,7 @@
a shared library
*/
-#if !defined(stdin) || defined(OS2)
+#if !defined(stdin)
#include "mysys_priv.h"
#include "my_static.h"
#include "my_alarm.h"
@@ -48,9 +48,6 @@ struct st_remember _my_sig_remember[MAX_SIGNALS]={{0,0}};
sigset_t my_signals; /* signals blocked by mf_brkhant */
#endif
- /* from mf_keycache.c */
-my_bool key_cache_inited=0;
-
/* from mf_reccache.c */
ulong my_default_record_cache_size=RECORD_CACHE_SIZE;
@@ -86,13 +83,6 @@ struct st_irem *sf_malloc_root = NULL;
int volatile my_have_got_alarm=0; /* declare variable to reset */
ulong my_time_to_wait_for_lock=2; /* In seconds */
- /*
- We need to have this define here as otherwise linking will fail
- on OSF1 when compiling --without-raid --with-debug
- */
-
-const char *raid_type_string[]={"none","striped"};
-
/* from errors.c */
#ifdef SHARED_LIBRARY
char * NEAR globerrs[GLOBERRS]; /* my_error_messages is here */
diff --git a/mysys/my_symlink2.c b/mysys/my_symlink2.c
index 2ad08ef67de..6ea22790998 100644
--- a/mysys/my_symlink2.c
+++ b/mysys/my_symlink2.c
@@ -33,9 +33,13 @@ File my_create_with_symlink(const char *linkname, const char *filename,
int create_link;
char abs_linkname[FN_REFLEN];
DBUG_ENTER("my_create_with_symlink");
+ DBUG_PRINT("enter", ("linkname: %s filename: %s",
+ linkname ? linkname : "(null)",
+ filename ? filename : "(null)"));
if (my_disable_symlinks)
{
+ DBUG_PRINT("info", ("Symlinks disabled"));
/* Create only the file, not the link and file */
create_link= 0;
if (linkname)
diff --git a/mysys/my_thr_init.c b/mysys/my_thr_init.c
index 07a92e34dd3..79a28ef04b0 100644
--- a/mysys/my_thr_init.c
+++ b/mysys/my_thr_init.c
@@ -47,23 +47,23 @@ pthread_mutexattr_t my_fast_mutexattr;
pthread_mutexattr_t my_errorcheck_mutexattr;
#endif
-#ifdef NPTL_PTHREAD_EXIT_BUG /* see my_pthread.h */
+#ifdef NPTL_PTHREAD_EXIT_BUG /* see my_pthread.h */
/*
- Dummy thread spawned in my_thread_global_init() below to avoid
- race conditions in NPTL pthread_exit code.
+ Dummy thread spawned in my_thread_global_init() below to avoid
+ race conditions in NPTL pthread_exit code.
*/
static pthread_handler_t
-nptl_pthread_exit_hack_handler(void *arg __attribute__((unused)))
+nptl_pthread_exit_hack_handler(void *arg __attribute((unused)))
{
/* Do nothing! */
pthread_exit(0);
return 0;
}
-
#endif
+
static uint get_thread_lib(void);
/*
@@ -89,25 +89,28 @@ my_bool my_thread_global_init(void)
}
#ifdef NPTL_PTHREAD_EXIT_BUG
+ /*
+ BUG#24507: Race conditions inside current NPTL pthread_exit()
+ implementation.
-/*
- BUG#24507: Race conditions inside current NPTL pthread_exit() implementation.
-
- To avoid a possible segmentation fault during concurrent executions of
- pthread_exit(), a dummy thread is spawned which initializes internal variables
- of pthread lib. See bug description for thoroughfull explanation.
+ To avoid a possible segmentation fault during concurrent
+ executions of pthread_exit(), a dummy thread is spawned which
+ initializes internal variables of pthread lib. See bug description
+ for a full explanation.
- TODO: Remove this code when fixed versions of glibc6 are in common use.
-*/
-
- pthread_t dummy_thread;
- pthread_attr_t dummy_thread_attr;
-
- pthread_attr_init(&dummy_thread_attr);
- pthread_attr_setdetachstate(&dummy_thread_attr,PTHREAD_CREATE_DETACHED);
+ TODO: Remove this code when fixed versions of glibc6 are in common
+ use.
+ */
+ {
+ pthread_t dummy_thread;
+ pthread_attr_t dummy_thread_attr;
- pthread_create(&dummy_thread,&dummy_thread_attr,nptl_pthread_exit_hack_handler,NULL);
+ pthread_attr_init(&dummy_thread_attr);
+ pthread_attr_setdetachstate(&dummy_thread_attr, PTHREAD_CREATE_DETACHED);
+ pthread_create(&dummy_thread,&dummy_thread_attr,
+ nptl_pthread_exit_hack_handler, NULL);
+ }
#endif
#ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
@@ -212,7 +215,7 @@ void my_thread_global_end(void)
#endif
}
-static long thread_id=0;
+static my_thread_id thread_id= 0;
/*
Allocate thread specific memory for the thread, used by mysys and dbug
@@ -240,15 +243,16 @@ my_bool my_thread_init(void)
my_bool error=0;
#ifdef EXTRA_DEBUG_THREADS
- fprintf(stderr,"my_thread_init(): thread_id=%ld\n",pthread_self());
+ fprintf(stderr,"my_thread_init(): thread_id: 0x%lx\n",
+ (ulong) pthread_self());
#endif
#if !defined(__WIN__) || defined(USE_TLS)
if (my_pthread_getspecific(struct st_my_thread_var *,THR_KEY_mysys))
{
#ifdef EXTRA_DEBUG_THREADS
- fprintf(stderr,"my_thread_init() called more than once in thread %ld\n",
- pthread_self());
+ fprintf(stderr,"my_thread_init() called more than once in thread 0x%lx\n",
+ (long) pthread_self());
#endif
goto end;
}
@@ -268,7 +272,9 @@ my_bool my_thread_init(void)
tmp= &THR_KEY_mysys;
#endif
#if defined(__WIN__) && defined(EMBEDDED_LIBRARY)
- tmp->thread_self= (pthread_t)getpid();
+ tmp->pthread_self= (pthread_t) getpid();
+#else
+ tmp->pthread_self= pthread_self();
#endif
pthread_mutex_init(&tmp->mutex,MY_MUTEX_INIT_FAST);
pthread_cond_init(&tmp->suspend, NULL);
@@ -278,6 +284,11 @@ my_bool my_thread_init(void)
tmp->id= ++thread_id;
++THR_thread_count;
pthread_mutex_unlock(&THR_LOCK_threads);
+#ifndef DBUG_OFF
+ /* Generate unique name for thread */
+ (void) my_thread_name();
+#endif
+
end:
return error;
}
@@ -301,8 +312,8 @@ void my_thread_end(void)
tmp= my_pthread_getspecific(struct st_my_thread_var*,THR_KEY_mysys);
#ifdef EXTRA_DEBUG_THREADS
- fprintf(stderr,"my_thread_end(): tmp: 0x%lx thread_id=%ld\n",
- (long) tmp, pthread_self());
+ fprintf(stderr,"my_thread_end(): tmp: 0x%lx pthread_self: 0x%lx thread_id: %ld\n",
+ (long) tmp, (long) pthread_self(), tmp ? (long) tmp->id : 0L);
#endif
if (tmp && tmp->init)
{
@@ -314,12 +325,12 @@ void my_thread_end(void)
tmp->dbug=0;
}
#endif
-#if !defined(__bsdi__) && !defined(__OpenBSD__) || defined(HAVE_mit_thread)
+#if !defined(__bsdi__) && !defined(__OpenBSD__)
/* bsdi and openbsd 3.5 dumps core here */
pthread_cond_destroy(&tmp->suspend);
#endif
pthread_mutex_destroy(&tmp->mutex);
-#if (!defined(__WIN__) && !defined(OS2)) || defined(USE_TLS)
+#if !defined(__WIN__) || defined(USE_TLS)
free(tmp);
#else
tmp->init= 0;
@@ -338,7 +349,7 @@ void my_thread_end(void)
pthread_mutex_unlock(&THR_LOCK_threads);
}
/* The following free has to be done, even if my_thread_var() is 0 */
-#if (!defined(__WIN__) && !defined(OS2)) || defined(USE_TLS)
+#if !defined(__WIN__) || defined(USE_TLS)
pthread_setspecific(THR_KEY_mysys,0);
#endif
}
@@ -363,17 +374,9 @@ struct st_my_thread_var *_my_thread_var(void)
Get name of current thread.
****************************************************************************/
-#define UNKNOWN_THREAD -1
-
-long my_thread_id()
+my_thread_id my_thread_dbug_id()
{
-#if defined(HAVE_PTHREAD_GETSEQUENCE_NP)
- return pthread_getsequence_np(pthread_self());
-#elif (defined(__sun) || defined(__sgi) || defined(__linux__)) && !defined(HAVE_mit_thread)
- return pthread_self();
-#else
return my_thread_var->id;
-#endif
}
#ifdef DBUG_OFF
@@ -390,8 +393,8 @@ const char *my_thread_name(void)
struct st_my_thread_var *tmp=my_thread_var;
if (!tmp->name[0])
{
- long id=my_thread_id();
- sprintf(name_buff,"T@%ld", id);
+ my_thread_id id= my_thread_dbug_id();
+ sprintf(name_buff,"T@%lu", (ulong) id);
strmake(tmp->name,name_buff,THREAD_NAME_SIZE);
}
return tmp->name;
diff --git a/mysys/my_vle.c b/mysys/my_vle.c
new file mode 100644
index 00000000000..18339c877df
--- /dev/null
+++ b/mysys/my_vle.c
@@ -0,0 +1,109 @@
+/* Copyright (C) 2005 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 */
+
+/*
+ Variable length encoding.
+
+ A method to store an arbitrary-size non-negative integer. We let the
+ most significant bit of the number indicate that the next byte
+ should be contatenated to form the real number.
+*/
+
+#include "my_vle.h"
+
+/*
+ Function to encode an unsigned long as VLE. The bytes for the VLE
+ will be written to the location pointed to by 'out'. The maximum
+ number of bytes written will be 'max'.
+
+ PARAMETERS
+
+ out Pointer to beginning of where to store VLE bytes.
+ max Maximum number of bytes to write.
+ n Number to encode.
+
+ RETURN VALUE
+ On success, one past the end of the array containing the VLE
+ bytes. On failure, the 'out' pointer is returned.
+*/
+
+byte*
+my_vle_encode(byte* out, my_size_t max, ulong n)
+{
+ byte buf[my_vle_sizeof(n)];
+ byte *ptr= buf;
+ my_size_t len;
+
+ do
+ {
+ *ptr++= (byte) (n & 0x7F);
+ n>>= 7;
+ }
+ while (n > 0);
+
+ len= ptr - buf;
+
+ if (len <= max)
+ {
+ /*
+ The bytes are stored in reverse order in 'buf'. Let's write them
+ in correct order to the output buffer and set the MSB at the
+ same time.
+ */
+ while (ptr-- > buf)
+ {
+ byte v= *ptr;
+ if (ptr > buf)
+ v|= 0x80;
+ *out++= v;
+ }
+ }
+
+ return out;
+}
+
+/*
+ Function to decode a VLE representation of an integral value.
+
+
+ PARAMETERS
+
+ result_ptr Pointer to an unsigned long where the value will be written.
+ vle Pointer to the VLE bytes.
+
+ RETURN VALUE
+
+ One-past the end of the VLE bytes. The routine will never read
+ more than sizeof(*result_ptr) + 1 bytes.
+*/
+
+byte const*
+my_vle_decode(ulong *result_ptr, byte const *vle)
+{
+ ulong result= 0;
+ my_size_t cnt= 1;
+
+ do
+ {
+ result<<= 7;
+ result|= (*vle & 0x7F);
+ }
+ while ((*vle++ & 0x80) && ++cnt <= sizeof(*result_ptr) + 1);
+
+ if (cnt <= sizeof(*result_ptr) + 1)
+ *result_ptr= result;
+
+ return vle;
+}
diff --git a/mysys/mysys_priv.h b/mysys/mysys_priv.h
index 8a636e94626..709cfed969a 100644
--- a/mysys/mysys_priv.h
+++ b/mysys/mysys_priv.h
@@ -40,3 +40,5 @@ extern pthread_mutex_t THR_LOCK_charset;
#ifndef EDQUOT
#define EDQUOT (-1)
#endif
+
+void my_error_unregister_all(void);
diff --git a/mysys/ptr_cmp.c b/mysys/ptr_cmp.c
index 33c28160a4c..4fdbad58aaf 100644
--- a/mysys/ptr_cmp.c
+++ b/mysys/ptr_cmp.c
@@ -184,7 +184,7 @@ my_off_t my_get_ptr(byte *ptr, uint pack_length)
case 4: pos= (my_off_t) mi_uint4korr(ptr); break;
case 3: pos= (my_off_t) mi_uint3korr(ptr); break;
case 2: pos= (my_off_t) mi_uint2korr(ptr); break;
- case 1: pos= (my_off_t) mi_uint2korr(ptr); break;
+ case 1: pos= (my_off_t) *(uchar*) ptr; break;
default: DBUG_ASSERT(0); return 0;
}
return pos;
diff --git a/mysys/queues.c b/mysys/queues.c
index 7809c97131c..5970922284f 100644
--- a/mysys/queues.c
+++ b/mysys/queues.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2000 MySQL AB
+/* Copyright (C) 2000, 2005 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
@@ -16,6 +16,10 @@
/*
Code for generell handling of priority Queues.
Implemention of queues from "Algoritms in C" by Robert Sedgewick.
+ An optimisation of _downheap suggested in Exercise 7.51 in "Data
+ Structures & Algorithms in C++" by Mark Allen Weiss, Second Edition
+ was implemented by Mikael Ronstrom 2005. Also the O(N) algorithm
+ of queue_fix was implemented.
*/
#include "mysys_priv.h"
@@ -62,6 +66,46 @@ int init_queue(QUEUE *queue, uint max_elements, uint offset_to_key,
}
+
+/*
+ Init queue, uses init_queue internally for init work but also accepts
+ auto_extent as parameter
+
+ SYNOPSIS
+ init_queue_ex()
+ queue Queue to initialise
+ max_elements Max elements that will be put in queue
+ offset_to_key Offset to key in element stored in queue
+ Used when sending pointers to compare function
+ max_at_top Set to 1 if you want biggest element on top.
+ compare Compare function for elements, takes 3 arguments.
+ first_cmp_arg First argument to compare function
+ auto_extent When the queue is full and there is insert operation
+ extend the queue.
+
+ NOTES
+ Will allocate max_element pointers for queue array
+
+ RETURN
+ 0 ok
+ 1 Could not allocate memory
+*/
+
+int init_queue_ex(QUEUE *queue, uint max_elements, uint offset_to_key,
+ pbool max_at_top, int (*compare) (void *, byte *, byte *),
+ void *first_cmp_arg, uint auto_extent)
+{
+ int ret;
+ DBUG_ENTER("init_queue_ex");
+
+ if ((ret= init_queue(queue, max_elements, offset_to_key, max_at_top, compare,
+ first_cmp_arg)))
+ DBUG_RETURN(ret);
+
+ queue->auto_extent= auto_extent;
+ DBUG_RETURN(0);
+}
+
/*
Reinitialize queue for other usage
@@ -181,6 +225,31 @@ void queue_insert(register QUEUE *queue, byte *element)
queue->root[idx]= element;
}
+/*
+ Does safe insert. If no more space left on the queue resize it.
+ Return codes:
+ 0 - OK
+ 1 - Cannot allocate more memory
+ 2 - auto_extend is 0, the operation would
+
+*/
+
+int queue_insert_safe(register QUEUE *queue, byte *element)
+{
+
+ if (queue->elements == queue->max_elements)
+ {
+ if (!queue->auto_extent)
+ return 2;
+ else if (resize_queue(queue, queue->max_elements + queue->auto_extent))
+ return 1;
+ }
+
+ queue_insert(queue, element);
+ return 0;
+}
+
+
/* Remove item from queue */
/* Returns pointer to removed element */
@@ -203,8 +272,64 @@ void queue_replaced(QUEUE *queue)
}
#endif
- /* Fix heap when index have changed */
+#ifndef OLD_VERSION
+
+void _downheap(register QUEUE *queue, uint idx)
+{
+ byte *element;
+ uint elements,half_queue,offset_to_key, next_index;
+ bool first= TRUE;
+ uint start_idx= idx;
+
+ offset_to_key=queue->offset_to_key;
+ element=queue->root[idx];
+ half_queue=(elements=queue->elements) >> 1;
+
+ while (idx <= half_queue)
+ {
+ int cmp;
+ next_index=idx+idx;
+ if (next_index < elements &&
+ (queue->compare(queue->first_cmp_arg,
+ queue->root[next_index]+offset_to_key,
+ queue->root[next_index+1]+offset_to_key) ^
+ queue->max_at_top) > 0)
+ next_index++;
+ if (first &&
+ (((cmp=queue->compare(queue->first_cmp_arg,
+ queue->root[next_index]+offset_to_key,
+ element+offset_to_key)) == 0) ||
+ ((cmp ^ queue->max_at_top) > 0)))
+ {
+ queue->root[idx]= element;
+ return;
+ }
+ queue->root[idx]=queue->root[next_index];
+ idx=next_index;
+ first= FALSE;
+ }
+
+ next_index= idx >> 1;
+ while (next_index > start_idx)
+ {
+ if ((queue->compare(queue->first_cmp_arg,
+ queue->root[next_index]+offset_to_key,
+ element+offset_to_key) ^
+ queue->max_at_top) < 0)
+ break;
+ queue->root[idx]=queue->root[next_index];
+ idx=next_index;
+ next_index= idx >> 1;
+ }
+ queue->root[idx]=element;
+}
+#else
+ /*
+ The old _downheap version is kept for comparisons with the benchmark
+ suit or new benchmarks anyone wants to run for comparisons.
+ */
+ /* Fix heap when index have changed */
void _downheap(register QUEUE *queue, uint idx)
{
byte *element;
@@ -236,20 +361,336 @@ void _downheap(register QUEUE *queue, uint idx)
}
-static int queue_fix_cmp(QUEUE *queue, void **a, void **b)
-{
- return queue->compare(queue->first_cmp_arg,
- (byte*) (*a)+queue->offset_to_key,
- (byte*) (*b)+queue->offset_to_key);
-}
+#endif
/*
- Fix heap when every element was changed,
- actually, it can be done better, in linear time, not in n*log(n)
+ Fix heap when every element was changed.
*/
void queue_fix(QUEUE *queue)
{
- qsort2(queue->root+1,queue->elements, sizeof(void *),
- (qsort2_cmp)queue_fix_cmp, queue);
+ uint i;
+ for (i= queue->elements >> 1; i > 0; i--)
+ _downheap(queue, i);
+}
+
+#ifdef MAIN
+ /*
+ A test program for the priority queue implementation.
+ It can also be used to benchmark changes of the implementation
+ Build by doing the following in the directory mysys
+ make test_priority_queue
+ ./test_priority_queue
+
+ Written by Mikael Ronström, 2005
+ */
+
+static uint num_array[1025];
+static uint tot_no_parts= 0;
+static uint tot_no_loops= 0;
+static uint expected_part= 0;
+static uint expected_num= 0;
+static bool max_ind= 0;
+static bool fix_used= 0;
+static ulonglong start_time= 0;
+
+static bool is_divisible_by(uint num, uint divisor)
+{
+ uint quotient= num / divisor;
+ if (quotient * divisor == num)
+ return TRUE;
+ return FALSE;
+}
+
+void calculate_next()
+{
+ uint part= expected_part, num= expected_num;
+ uint no_parts= tot_no_parts;
+ if (max_ind)
+ {
+ do
+ {
+ while (++part <= no_parts)
+ {
+ if (is_divisible_by(num, part) &&
+ (num <= ((1 << 21) + part)))
+ {
+ expected_part= part;
+ expected_num= num;
+ return;
+ }
+ }
+ part= 0;
+ } while (--num);
+ }
+ else
+ {
+ do
+ {
+ while (--part > 0)
+ {
+ if (is_divisible_by(num, part))
+ {
+ expected_part= part;
+ expected_num= num;
+ return;
+ }
+ }
+ part= no_parts + 1;
+ } while (++num);
+ }
+}
+
+void calculate_end_next(uint part)
+{
+ uint no_parts= tot_no_parts, num;
+ num_array[part]= 0;
+ if (max_ind)
+ {
+ expected_num= 0;
+ for (part= no_parts; part > 0 ; part--)
+ {
+ if (num_array[part])
+ {
+ num= num_array[part] & 0x3FFFFF;
+ if (num >= expected_num)
+ {
+ expected_num= num;
+ expected_part= part;
+ }
+ }
+ }
+ if (expected_num == 0)
+ expected_part= 0;
+ }
+ else
+ {
+ expected_num= 0xFFFFFFFF;
+ for (part= 1; part <= no_parts; part++)
+ {
+ if (num_array[part])
+ {
+ num= num_array[part] & 0x3FFFFF;
+ if (num <= expected_num)
+ {
+ expected_num= num;
+ expected_part= part;
+ }
+ }
+ }
+ if (expected_num == 0xFFFFFFFF)
+ expected_part= 0;
+ }
+ return;
+}
+static int test_compare(void *null_arg, byte *a, byte *b)
+{
+ uint a_num= (*(uint*)a) & 0x3FFFFF;
+ uint b_num= (*(uint*)b) & 0x3FFFFF;
+ uint a_part, b_part;
+ if (a_num > b_num)
+ return +1;
+ if (a_num < b_num)
+ return -1;
+ a_part= (*(uint*)a) >> 22;
+ b_part= (*(uint*)b) >> 22;
+ if (a_part < b_part)
+ return +1;
+ if (a_part > b_part)
+ return -1;
+ return 0;
+}
+
+bool check_num(uint num_part)
+{
+ uint part= num_part >> 22;
+ uint num= num_part & 0x3FFFFF;
+ if (part == expected_part)
+ if (num == expected_num)
+ return FALSE;
+ printf("Expect part %u Expect num 0x%x got part %u num 0x%x max_ind %u fix_used %u \n",
+ expected_part, expected_num, part, num, max_ind, fix_used);
+ return TRUE;
+}
+
+
+void perform_insert(QUEUE *queue)
+{
+ uint i= 1, no_parts= tot_no_parts;
+ uint backward_start= 0;
+
+ expected_part= 1;
+ expected_num= 1;
+
+ if (max_ind)
+ backward_start= 1 << 21;
+
+ do
+ {
+ uint num= (i + backward_start);
+ if (max_ind)
+ {
+ while (!is_divisible_by(num, i))
+ num--;
+ if (max_ind && (num > expected_num ||
+ (num == expected_num && i < expected_part)))
+ {
+ expected_num= num;
+ expected_part= i;
+ }
+ }
+ num_array[i]= num + (i << 22);
+ if (fix_used)
+ queue_element(queue, i-1)= (byte*)&num_array[i];
+ else
+ queue_insert(queue, (byte*)&num_array[i]);
+ } while (++i <= no_parts);
+ if (fix_used)
+ {
+ queue->elements= no_parts;
+ queue_fix(queue);
+ }
+}
+
+bool perform_ins_del(QUEUE *queue, bool max_ind)
+{
+ uint i= 0, no_loops= tot_no_loops, j= tot_no_parts;
+ do
+ {
+ uint num_part= *(uint*)queue_top(queue);
+ uint part= num_part >> 22;
+ if (check_num(num_part))
+ return TRUE;
+ if (j++ >= no_loops)
+ {
+ calculate_end_next(part);
+ queue_remove(queue, (uint) 0);
+ }
+ else
+ {
+ calculate_next();
+ if (max_ind)
+ num_array[part]-= part;
+ else
+ num_array[part]+= part;
+ queue_top(queue)= (byte*)&num_array[part];
+ queue_replaced(queue);
+ }
+ } while (++i < no_loops);
+ return FALSE;
+}
+
+bool do_test(uint no_parts, uint l_max_ind, bool l_fix_used)
+{
+ QUEUE queue;
+ bool result;
+ max_ind= l_max_ind;
+ fix_used= l_fix_used;
+ init_queue(&queue, no_parts, 0, max_ind, test_compare, NULL);
+ tot_no_parts= no_parts;
+ tot_no_loops= 1024;
+ perform_insert(&queue);
+ if ((result= perform_ins_del(&queue, max_ind)))
+ delete_queue(&queue);
+ if (result)
+ {
+ printf("Error\n");
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void start_measurement()
+{
+ start_time= my_getsystime();
}
+
+static void stop_measurement()
+{
+ ulonglong stop_time= my_getsystime();
+ uint time_in_micros;
+ stop_time-= start_time;
+ stop_time/= 10; /* Convert to microseconds */
+ time_in_micros= (uint)stop_time;
+ printf("Time expired is %u microseconds \n", time_in_micros);
+}
+
+static void benchmark_test()
+{
+ QUEUE queue_real;
+ QUEUE *queue= &queue_real;
+ uint i, add;
+ fix_used= TRUE;
+ max_ind= FALSE;
+ tot_no_parts= 1024;
+ init_queue(queue, tot_no_parts, 0, max_ind, test_compare, NULL);
+ /*
+ First benchmark whether queue_fix is faster than using queue_insert
+ for sizes of 16 partitions.
+ */
+ for (tot_no_parts= 2, add=2; tot_no_parts < 128;
+ tot_no_parts+= add, add++)
+ {
+ printf("Start benchmark queue_fix, tot_no_parts= %u \n", tot_no_parts);
+ start_measurement();
+ for (i= 0; i < 128; i++)
+ {
+ perform_insert(queue);
+ queue_remove_all(queue);
+ }
+ stop_measurement();
+
+ fix_used= FALSE;
+ printf("Start benchmark queue_insert\n");
+ start_measurement();
+ for (i= 0; i < 128; i++)
+ {
+ perform_insert(queue);
+ queue_remove_all(queue);
+ }
+ stop_measurement();
+ }
+ /*
+ Now benchmark insertion and deletion of 16400 elements.
+ Used in consecutive runs this shows whether the optimised _downheap
+ is faster than the standard implementation.
+ */
+ printf("Start benchmarking _downheap \n");
+ start_measurement();
+ perform_insert(queue);
+ for (i= 0; i < 65536; i++)
+ {
+ uint num, part;
+ num= *(uint*)queue_top(queue);
+ num+= 16;
+ part= num >> 22;
+ num_array[part]= num;
+ queue_top(queue)= (byte*)&num_array[part];
+ queue_replaced(queue);
+ }
+ for (i= 0; i < 16; i++)
+ queue_remove(queue, (uint) 0);
+ queue_remove_all(queue);
+ stop_measurement();
+}
+
+int main()
+{
+ int i, add= 1;
+ for (i= 1; i < 1024; i+=add, add++)
+ {
+ printf("Start test for priority queue of size %u\n", i);
+ if (do_test(i, 0, 1))
+ return -1;
+ if (do_test(i, 1, 1))
+ return -1;
+ if (do_test(i, 0, 0))
+ return -1;
+ if (do_test(i, 1, 0))
+ return -1;
+ }
+ benchmark_test();
+ printf("OK\n");
+ return 0;
+}
+#endif
diff --git a/mysys/raid.cc b/mysys/raid.cc
deleted file mode 100644
index 0fbe1810208..00000000000
--- a/mysys/raid.cc
+++ /dev/null
@@ -1,798 +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 */
-
-/*
-
- RAID support for MySQL. Raid 0 (stiping) only implemented yet.
-
- Why RAID? Why it must be in MySQL?
-
- This is because then you can:
- 1. Have bigger tables than your OS limit. In time of writing this
- we are hitting to 2GB limit under linux/ext2
- 2. You can get more speed from IO bottleneck by putting
- Raid dirs on different physical disks.
- 3. Getting more fault tolerance (not implemented yet)
-
- Why not to use RAID:
-
- 1. You are losing some processor power to calculate things,
- do more syscalls and interrupts.
-
- Functionality is supplied by two classes: RaidFd and RaidName.
- RaidFd supports funtionality over file descriptors like
- open/create/write/seek/close. RaidName supports functionality
- like rename/delete where we have no relations to filedescriptors.
- RaidName can be prorably unchanged for different Raid levels. RaidFd
- have to be virtual I think ;).
- You can speed up some calls in MySQL code by skipping RAID code.
- For example LOAD DATA INFILE never needs to read RAID-ed files.
- This can be done adding proper "#undef my_read" or similar undef-s
- in your code. Check out the raid.h!
-
- Some explanation about _seek_vector[]
- This is seek cache. RAID seeks too much and we cacheing this. We
- fool it and just storing new position in file to _seek_vector.
- When there is no seeks to do, we are putting RAID_SEEK_DONE into it.
- Any other value requires seeking to that position.
-
- TODO:
-
-
- - Implement other fancy things like RAID 1 (mirroring) and RAID 5.
- Should not to be very complex.
-
- - Optimize big blob writes by resorting write buffers and writing
- big chunks at once instead of doing many syscalls. - after thinking I
- found this is useless. This is because same thing one can do with just
- increasing RAID_CHUNKSIZE. Monty, what do you think? tonu.
-
- - If needed, then implement missing syscalls. One known to miss is stat();
-
- - Make and use a thread safe dynamic_array buffer. The used one
- will not work if needs to be extended at the same time someone is
- accessing it.
-
-
- tonu@mysql.com & monty@mysql.com
-*/
-
-#ifdef USE_PRAGMA_IMPLEMENTATION
-#pragma implementation // gcc: Class implementation
-#endif
-
-#include "mysys_priv.h"
-#include <my_dir.h>
-#include <m_string.h>
-#include <assert.h>
-
-#if defined(USE_RAID) && !defined(MYSQL_CLIENT)
-
-#define RAID_SEEK_DONE ~(off_t) 0
-#define RAID_SIZE_UNKNOWN ~(my_off_t) 0
-
-DYNAMIC_ARRAY RaidFd::_raid_map;
-
-
-/* --------------- C compatibility ---------------*/
-
-extern "C" {
-
- void init_raid(void)
- {
- /* Allocate memory for global file to raid map */
- my_init_dynamic_array(&RaidFd::_raid_map, sizeof(RaidFd*), 4096, 1024);
- }
- void end_raid(void)
- {
- /* Free memory used by raid */
- delete_dynamic(&RaidFd::_raid_map);
- }
-
- bool is_raid(File fd)
- {
- return RaidFd::IsRaid(fd);
- }
-
- File my_raid_create(const char *FileName, int CreateFlags, int access_flags,
- uint raid_type, uint raid_chunks, ulong raid_chunksize,
- myf MyFlags)
- {
- DBUG_ENTER("my_raid_create");
- DBUG_PRINT("enter",("Filename: %s CreateFlags: %d access_flags: %d MyFlags: %d",
- FileName, CreateFlags, access_flags, MyFlags));
- if (raid_type)
- {
- RaidFd *raid = new RaidFd(raid_type, raid_chunks , raid_chunksize);
- File res = raid->Create(FileName,CreateFlags,access_flags,MyFlags);
- if (res < 0 || set_dynamic(&RaidFd::_raid_map,(char*) &raid,res))
- {
- delete raid;
- DBUG_RETURN(-1);
- }
- DBUG_RETURN(res);
- }
- else
- DBUG_RETURN(my_create(FileName, CreateFlags, access_flags, MyFlags));
- }
-
- File my_raid_open(const char *FileName, int Flags,
- uint raid_type, uint raid_chunks, ulong raid_chunksize,
- myf MyFlags)
- {
- DBUG_ENTER("my_raid_open");
- DBUG_PRINT("enter",("Filename: %s Flags: %d MyFlags: %d",
- FileName, Flags, MyFlags));
- if (raid_type)
- {
- RaidFd *raid = new RaidFd(raid_type, raid_chunks , raid_chunksize);
- File res = raid->Open(FileName,Flags,MyFlags);
- if (res < 0 || set_dynamic(&RaidFd::_raid_map,(char*) &raid,res))
- {
- delete raid;
- DBUG_RETURN(-1);
- }
- DBUG_RETURN(res);
- }
- else
- DBUG_RETURN(my_open(FileName, Flags, MyFlags));
- }
-
- my_off_t my_raid_seek(File fd, my_off_t pos,int whence,myf MyFlags)
- {
- DBUG_ENTER("my_raid_seek");
- DBUG_PRINT("enter",("Fd: %d pos: %lu whence: %d MyFlags: %d",
- fd, (ulong) pos, whence, MyFlags));
-
- if (is_raid(fd))
- {
- assert(pos != MY_FILEPOS_ERROR);
-
- RaidFd *raid= (*dynamic_element(&RaidFd::_raid_map,fd,RaidFd**));
- DBUG_RETURN(raid->Seek(pos,whence,MyFlags));
- }
- else
- DBUG_RETURN(my_seek(fd, pos, whence, MyFlags));
- }
-
- my_off_t my_raid_tell(File fd,myf MyFlags)
- {
- DBUG_ENTER("my_raid_tell");
- DBUG_PRINT("enter",("Fd: %d MyFlags: %d",
- fd, MyFlags));
- if (is_raid(fd))
- {
- RaidFd *raid= (*dynamic_element(&RaidFd::_raid_map,fd,RaidFd**));
- DBUG_RETURN(raid->Tell(MyFlags));
- }
- else
- DBUG_RETURN(my_tell(fd, MyFlags));
- }
-
- uint my_raid_write(File fd,const byte *Buffer, uint Count, myf MyFlags)
- {
- DBUG_ENTER("my_raid_write");
- DBUG_PRINT("enter",("Fd: %d Buffer: 0x%lx Count: %u MyFlags: %d",
- fd, Buffer, Count, MyFlags));
- if (is_raid(fd))
- {
- RaidFd *raid= (*dynamic_element(&RaidFd::_raid_map,fd,RaidFd**));
- DBUG_RETURN(raid->Write(Buffer,Count,MyFlags));
- } else
- DBUG_RETURN(my_write(fd,Buffer,Count,MyFlags));
- }
-
- uint my_raid_read(File fd, byte *Buffer, uint Count, myf MyFlags)
- {
- DBUG_ENTER("my_raid_read");
- DBUG_PRINT("enter",("Fd: %d Buffer: 0x%lx Count: %u MyFlags: %d",
- fd, Buffer, Count, MyFlags));
- if (is_raid(fd))
- {
- RaidFd *raid= (*dynamic_element(&RaidFd::_raid_map,fd,RaidFd**));
- DBUG_RETURN(raid->Read(Buffer,Count,MyFlags));
- } else
- DBUG_RETURN(my_read(fd,Buffer,Count,MyFlags));
- }
-
- uint my_raid_pread(File Filedes, byte *Buffer, uint Count, my_off_t offset,
- myf MyFlags)
- {
- DBUG_ENTER("my_raid_pread");
- DBUG_PRINT("enter",
- ("Fd: %d Buffer: 0x%lx Count: %u offset: %u MyFlags: %d",
- Filedes, Buffer, Count, offset, MyFlags));
- if (is_raid(Filedes))
- {
- assert(offset != MY_FILEPOS_ERROR);
-
- RaidFd *raid= (*dynamic_element(&RaidFd::_raid_map,Filedes,RaidFd**));
- /* Returning value isn't important because real seek is done later. */
- raid->Seek(offset,MY_SEEK_SET,MyFlags);
- DBUG_RETURN(raid->Read(Buffer,Count,MyFlags));
- }
- else
- DBUG_RETURN(my_pread(Filedes, Buffer, Count, offset, MyFlags));
- }
-
- uint my_raid_pwrite(int Filedes, const byte *Buffer, uint Count,
- my_off_t offset, myf MyFlags)
- {
- DBUG_ENTER("my_raid_pwrite");
- DBUG_PRINT("enter",
- ("Fd: %d Buffer: 0x%lx Count: %u offset: %u MyFlags: %d",
- Filedes, Buffer, Count, offset, MyFlags));
- if (is_raid(Filedes))
- {
- assert(offset != MY_FILEPOS_ERROR);
-
- RaidFd *raid= (*dynamic_element(&RaidFd::_raid_map,Filedes,RaidFd**));
- /* Returning value isn't important because real seek is done later. */
- raid->Seek(offset,MY_SEEK_SET,MyFlags);
- DBUG_RETURN(raid->Write(Buffer,Count,MyFlags));
- }
- else
- DBUG_RETURN(my_pwrite(Filedes, Buffer, Count, offset, MyFlags));
- }
-
- int my_raid_lock(File fd, int locktype, my_off_t start, my_off_t length,
- myf MyFlags)
- {
- DBUG_ENTER("my_raid_lock");
- DBUG_PRINT("enter",("Fd: %d start: %lu length: %lu MyFlags: %d",
- fd, (ulong) start, (ulong) length, MyFlags));
- if (my_disable_locking)
- DBUG_RETURN(0);
- if (is_raid(fd))
- {
- RaidFd *raid= (*dynamic_element(&RaidFd::_raid_map,fd,RaidFd**));
- DBUG_RETURN(raid->Lock(locktype, start, length, MyFlags));
- }
- else
- DBUG_RETURN(my_lock(fd, locktype, start, length, MyFlags));
- }
-
- int my_raid_close(File fd, myf MyFlags)
- {
- DBUG_ENTER("my_raid_close");
- DBUG_PRINT("enter",("Fd: %d MyFlags: %d",
- fd, MyFlags));
- if (is_raid(fd))
- {
- RaidFd *raid= (*dynamic_element(&RaidFd::_raid_map,fd,RaidFd**));
- RaidFd *tmp=0;
- set_dynamic(&RaidFd::_raid_map,(char*) &tmp,fd);
- int res = raid->Close(MyFlags);
- delete raid;
- DBUG_RETURN(res);
- }
- else
- DBUG_RETURN(my_close(fd, MyFlags));
- }
-
- int my_raid_chsize(File fd, my_off_t newlength, int filler, myf MyFlags)
- {
- DBUG_ENTER("my_raid_chsize");
- DBUG_PRINT("enter",("Fd: %d newlength: %lu MyFlags: %d",
- fd, (ulong) newlength, MyFlags));
- if (is_raid(fd))
- {
- RaidFd *raid= (*dynamic_element(&RaidFd::_raid_map,fd,RaidFd**));
- DBUG_RETURN(raid->Chsize(fd, newlength, filler, MyFlags));
- }
- else
- DBUG_RETURN(my_chsize(fd, newlength, filler, MyFlags));
- }
-
- int my_raid_rename(const char *from, const char *to,
- uint raid_chunks, myf MyFlags)
- {
- char from_tmp[FN_REFLEN];
- char to_tmp[FN_REFLEN];
- DBUG_ENTER("my_raid_rename");
-
- uint from_pos = dirname_length(from);
- uint to_pos = dirname_length(to);
- memcpy(from_tmp, from, from_pos);
- memcpy(to_tmp, to, to_pos);
- for (uint i = 0 ; i < raid_chunks ; i++ )
- {
- sprintf(from_tmp+from_pos,"%02x/%s", i, from + from_pos);
- sprintf(to_tmp+to_pos,"%02x/%s", i, to+ to_pos);
- /* Convert if not unix */
- unpack_filename(from_tmp, from_tmp);
- unpack_filename(to_tmp,to_tmp);
- if (my_rename(from_tmp, to_tmp, MyFlags))
- DBUG_RETURN(-1);
- }
- DBUG_RETURN(0);
- }
-
- int my_raid_delete(const char *from, uint raid_chunks, myf MyFlags)
- {
- char from_tmp[FN_REFLEN];
- uint from_pos = dirname_length(from);
- DBUG_ENTER("my_raid_delete");
-
- if (!raid_chunks)
- DBUG_RETURN(my_delete(from,MyFlags));
- for (uint i = 0 ; i < raid_chunks ; i++ )
- {
- memcpy(from_tmp, from, from_pos);
- sprintf(from_tmp+from_pos,"%02x/%s", i, from + from_pos);
- /* Convert if not unix */
- unpack_filename(from_tmp, from_tmp);
- if (my_delete(from_tmp, MyFlags))
- DBUG_RETURN(-1);
- }
- DBUG_RETURN(0);
- }
-
- int my_raid_redel(const char *old_name, const char *new_name,
- uint raid_chunks, myf MyFlags)
- {
- char new_name_buff[FN_REFLEN], old_name_buff[FN_REFLEN];
- char *new_end, *old_end;
- uint i,old_length,new_length;
- int error=0;
- DBUG_ENTER("my_raid_redel");
-
- old_end=old_name_buff+dirname_part(old_name_buff,old_name);
- old_length=dirname_length(old_name);
- new_end=new_name_buff+dirname_part(new_name_buff,new_name);
- new_length=dirname_length(new_name);
- for (i=0 ; i < raid_chunks ; i++)
- {
- MY_STAT status;
- sprintf(new_end,"%02x",i);
- if (my_stat(new_name_buff,&status, MYF(0)))
- {
- DBUG_PRINT("info",("%02x exists, skipping directory creation",i));
- }
- else
- {
- if (my_mkdir(new_name_buff,0777,MYF(0)))
- {
- DBUG_PRINT("error",("mkdir failed for %02x",i));
- DBUG_RETURN(-1);
- }
- }
- strxmov(strend(new_end),"/",new_name+new_length,NullS);
- sprintf(old_end,"%02x/%s",i, old_name+old_length);
- if (my_redel(old_name_buff, new_name_buff, MyFlags))
- error=1;
- }
- DBUG_RETURN(error);
- }
-}
-
-int my_raid_fstat(int fd, MY_STAT *stat_area, myf MyFlags )
-{
- DBUG_ENTER("my_raid_fstat");
- if (is_raid(fd))
- {
- RaidFd *raid= (*dynamic_element(&RaidFd::_raid_map,fd,RaidFd**));
- DBUG_RETURN(raid->Fstat(fd, stat_area, MyFlags));
- }
- else
- DBUG_RETURN(my_fstat(fd, stat_area, MyFlags));
-}
-
-
-/* -------------- RaidFd base class begins ----------------*/
-/*
- RaidFd - raided file is identified by file descriptor
- this is useful when we open/write/read/close files
-*/
-
-
-bool RaidFd::
-IsRaid(File fd)
-{
- DBUG_ENTER("RaidFd::IsRaid");
- DBUG_RETURN((uint) fd < _raid_map.elements &&
- *dynamic_element(&RaidFd::_raid_map,fd,RaidFd**));
-}
-
-
-RaidFd::
-RaidFd(uint raid_type, uint raid_chunks, ulong raid_chunksize)
- :_raid_type(raid_type), _raid_chunks(raid_chunks),
- _raid_chunksize(raid_chunksize), _position(0), _size(RAID_SIZE_UNKNOWN),
- _fd_vector(0)
-{
- DBUG_ENTER("RaidFd::RaidFd");
- DBUG_PRINT("enter",("RaidFd_type: %u Disks: %u Chunksize: %lu",
- raid_type, raid_chunks, raid_chunksize));
-
- /* TODO: Here we should add checks if the malloc fails */
- _seek_vector=0; /* In case of errors */
- my_multi_malloc(MYF(MY_WME),
- &_seek_vector,sizeof(off_t)*_raid_chunks,
- &_fd_vector, sizeof(File) *_raid_chunks,
- NullS);
- if (!RaidFd::_raid_map.buffer)
- { /* Not initied */
- pthread_mutex_lock(&THR_LOCK_open); /* Ensure that no other thread */
- if (!RaidFd::_raid_map.buffer) /* has done init in between */
- init_raid();
- pthread_mutex_unlock(&THR_LOCK_open);
- }
- DBUG_VOID_RETURN;
-}
-
-
-RaidFd::
-~RaidFd() {
- DBUG_ENTER("RaidFd::~RaidFd");
- /* We don't have to free _fd_vector ! */
- my_free((char*) _seek_vector, MYF(MY_ALLOW_ZERO_PTR));
- DBUG_VOID_RETURN;
-}
-
-
-File RaidFd::
-Create(const char *FileName, int CreateFlags, int access_flags, myf MyFlags)
-{
- char RaidFdFileName[FN_REFLEN];
- DBUG_ENTER("RaidFd::Create");
- DBUG_PRINT("enter",
- ("FileName: %s CreateFlags: %d access_flags: %d MyFlags: %d",
- FileName, CreateFlags, access_flags, MyFlags));
- char DirName[FN_REFLEN];
- uint pos = dirname_part(DirName, FileName);
- MY_STAT status;
- if (!_seek_vector)
- DBUG_RETURN(-1); /* Not enough memory */
-
- uint i = _raid_chunks-1;
- do
- {
- /* Create subdir */
- (void)sprintf(RaidFdFileName,"%s%02x", DirName,i);
- unpack_dirname(RaidFdFileName,RaidFdFileName); /* Convert if not unix */
- if (my_stat(RaidFdFileName,&status, MYF(0)))
- {
- DBUG_PRINT("info",("%02x exists, skipping directory creation",i));
- }
- else
- {
- if (my_mkdir(RaidFdFileName,0777,MYF(0)))
- {
- DBUG_PRINT("error",("mkdir failed for %d",i));
- goto error;
- }
- }
- /* Create file */
- sprintf(RaidFdFileName,"%s%02x/%s",DirName, i, FileName + pos);
- unpack_filename(RaidFdFileName,RaidFdFileName); /* Convert if not unix */
- _fd = my_create(RaidFdFileName, CreateFlags ,access_flags, (myf)MyFlags);
- if (_fd < 0)
- goto error;
- _fd_vector[i]=_fd;
- _seek_vector[i]=RAID_SEEK_DONE;
- } while (i--);
- _size=0;
- DBUG_RETURN(_fd); /* Last filenr is pointer to map */
-
-error:
- {
- int save_errno=my_errno;
- while (++i < _raid_chunks)
- {
- my_close(_fd_vector[i],MYF(0));
- sprintf(RaidFdFileName,"%s%02x/%s",DirName, i, FileName + pos);
- unpack_filename(RaidFdFileName,RaidFdFileName);
- my_delete(RaidFdFileName,MYF(0));
- }
- my_errno=save_errno;
- }
- DBUG_RETURN(-1);
-}
-
-
-File RaidFd::
-Open(const char *FileName, int Flags, myf MyFlags)
-{
- DBUG_ENTER("RaidFd::Open");
- DBUG_PRINT("enter",("FileName: %s Flags: %d MyFlags: %d",
- FileName, Flags, MyFlags));
- char DirName[FN_REFLEN];
- uint pos = dirname_part(DirName, FileName);
- if (!_seek_vector)
- DBUG_RETURN(-1); /* Not enough memory */
-
- for( uint i = 0 ; i < _raid_chunks ; i++ )
- {
- char RaidFdFileName[FN_REFLEN];
- sprintf(RaidFdFileName,"%s%02x/%s",DirName, i, FileName + pos);
- unpack_filename(RaidFdFileName,RaidFdFileName); /* Convert if not unix */
- _fd = my_open(RaidFdFileName, Flags, MyFlags);
- if (_fd < 0)
- {
- int save_errno=my_errno;
- while (i-- != 0)
- my_close(_fd_vector[i],MYF(0));
- my_errno=save_errno;
- DBUG_RETURN(_fd);
- }
- _fd_vector[i]=_fd;
- _seek_vector[i]=RAID_SEEK_DONE;
- }
- Seek(0L,MY_SEEK_END,MYF(0)); // Trick. We just need to know, how big the file is
- DBUG_PRINT("info",("MYD file logical size: %llu", _size));
- DBUG_RETURN(_fd);
-}
-
-
-int RaidFd::
-Write(const byte *Buffer, uint Count, myf MyFlags)
-{
- DBUG_ENTER("RaidFd::Write");
- DBUG_PRINT("enter",("Count: %d MyFlags: %d",
- Count, MyFlags));
- const byte *bufptr = Buffer;
- uint res=0, GotBytes, ReadNowCount;
-
- // Loop until data is written
- do {
- Calculate();
- // Do seeks when neccessary
- if (_seek_vector[_this_block] != RAID_SEEK_DONE)
- {
- if (my_seek(_fd_vector[_this_block], _seek_vector[_this_block],
- MY_SEEK_SET,
- MyFlags) == MY_FILEPOS_ERROR)
- DBUG_RETURN(-1);
- _seek_vector[_this_block]=RAID_SEEK_DONE;
- }
- ReadNowCount = min(Count, _remaining_bytes);
- GotBytes = my_write(_fd_vector[_this_block], bufptr, ReadNowCount,
- MyFlags);
- DBUG_PRINT("loop",("Wrote bytes: %d", GotBytes));
- if (GotBytes == MY_FILE_ERROR)
- DBUG_RETURN(-1);
- res+= GotBytes;
- if (MyFlags & (MY_NABP | MY_FNABP))
- GotBytes=ReadNowCount;
- bufptr += GotBytes;
- Count -= GotBytes;
- _position += GotBytes;
- } while(Count);
- set_if_bigger(_size,_position);
- DBUG_RETURN(res);
-}
-
-
-int RaidFd::
-Read(const byte *Buffer, uint Count, myf MyFlags)
-{
- DBUG_ENTER("RaidFd::Read");
- DBUG_PRINT("enter",("Count: %d MyFlags: %d",
- Count, MyFlags));
- byte *bufptr = (byte *)Buffer;
- uint res= 0, GotBytes, ReadNowCount;
-
- // Loop until all data is read (Note that Count may be 0)
- while (Count)
- {
- Calculate();
- // Do seek when neccessary
- if (_seek_vector[_this_block] != RAID_SEEK_DONE)
- {
- if (my_seek(_fd_vector[_this_block], _seek_vector[_this_block],
- MY_SEEK_SET,
- MyFlags) == MY_FILEPOS_ERROR)
- DBUG_RETURN(-1);
- _seek_vector[_this_block]=RAID_SEEK_DONE;
- }
- // and read
- ReadNowCount = min(Count, _remaining_bytes);
- GotBytes = my_read(_fd_vector[_this_block], bufptr, ReadNowCount,
- MyFlags & ~(MY_NABP | MY_FNABP));
- DBUG_PRINT("loop",("Got bytes: %u", GotBytes));
- if (GotBytes == MY_FILE_ERROR)
- DBUG_RETURN(-1);
- if (!GotBytes) // End of file.
- {
- DBUG_RETURN((MyFlags & (MY_NABP | MY_FNABP)) ? -1 : (int) res);
- }
- res+= GotBytes;
- bufptr += GotBytes;
- Count -= GotBytes;
- _position += GotBytes;
- }
- DBUG_RETURN((MyFlags & (MY_NABP | MY_FNABP)) ? 0 : res);
-}
-
-
-int RaidFd::
-Lock(int locktype, my_off_t start, my_off_t length, myf MyFlags)
-{
- DBUG_ENTER("RaidFd::Lock");
- DBUG_PRINT("enter",("locktype: %d start: %lu length: %lu MyFlags: %d",
- locktype, (ulong) start, (ulong) length, MyFlags));
- my_off_t bufptr = start;
- // Loop until all data is locked
- while(length)
- {
- Calculate();
- for (uint i = _this_block ; (i < _raid_chunks) && length ; i++ )
- {
- uint ReadNowCount = min(length, _remaining_bytes);
- uint GotBytes = my_lock(_fd_vector[i], locktype, bufptr, ReadNowCount,
- MyFlags);
- if ((int) GotBytes == -1)
- DBUG_RETURN(-1);
- bufptr += ReadNowCount;
- length -= ReadNowCount;
- Calculate();
- }
- }
- DBUG_RETURN(0);
-}
-
-
-int RaidFd::
-Close(myf MyFlags)
-{
- DBUG_ENTER("RaidFd::Close");
- DBUG_PRINT("enter",("MyFlags: %d",
- MyFlags));
- for (uint i = 0 ; i < _raid_chunks ; ++i )
- {
- int err = my_close(_fd_vector[i], MyFlags);
- if (err != 0)
- DBUG_RETURN(err);
- }
- /* _fd_vector is erased when RaidFd is released */
- DBUG_RETURN(0);
-}
-
-
-my_off_t RaidFd::
-Seek(my_off_t pos,int whence,myf MyFlags)
-{
- DBUG_ENTER("RaidFd::Seek");
- DBUG_PRINT("enter",("Pos: %lu Whence: %d MyFlags: %d",
- (ulong) pos, whence, MyFlags));
- switch (whence) {
- case MY_SEEK_CUR:
- // FIXME: This is wrong, what is going on there
- // Just I am relied on fact that MySQL 3.23.7 never uses MY_SEEK_CUR
- // for anything else except things like ltell()
- break;
- case MY_SEEK_SET:
- if ( _position != pos) // we can be already in right place
- {
- uint i;
- off_t _rounds;
- _position = pos;
- Calculate();
- _rounds = _total_block / _raid_chunks; // INT() assumed
- _rounds*= _raid_chunksize;
- for (i = 0; i < _raid_chunks ; i++ )
- if ( i < _this_block )
- _seek_vector[i] = _rounds + _raid_chunksize;
- else if ( i == _this_block )
- _seek_vector[i] = _rounds + _raid_chunksize -_remaining_bytes;
- else // if ( i > _this_block )
- _seek_vector[i] = _rounds;
- }
- break;
- case MY_SEEK_END:
- if (_size==RAID_SIZE_UNKNOWN) // We don't know table size yet
- {
- uint i;
- _position = 0;
- for (i = 0; i < _raid_chunks ; i++ )
- {
- my_off_t newpos = my_seek(_fd_vector[i], 0L, MY_SEEK_END, MyFlags);
- if (newpos == MY_FILEPOS_ERROR)
- DBUG_RETURN (MY_FILEPOS_ERROR);
- _seek_vector[i]=RAID_SEEK_DONE;
- _position += newpos;
- }
- _size=_position;
- }
- else if (_position != _size) // Aren't we also already in the end?
- {
- uint i;
- off_t _rounds;
- _position = _size;
- Calculate();
- _rounds = _total_block / _raid_chunks; // INT() assumed
- _rounds*= _raid_chunksize;
- for (i = 0; i < _raid_chunks ; i++ )
- if ( i < _this_block )
- _seek_vector[i] = _rounds + _raid_chunksize;
- else if ( i == _this_block )
- _seek_vector[i] = _rounds + _raid_chunksize - _remaining_bytes;
- else // if ( i > _this_block )
- _seek_vector[i] = _rounds;
- _position=_size;
- }
- }
- DBUG_RETURN(_position);
-}
-
-
-my_off_t RaidFd::
-Tell(myf MyFlags)
-{
- DBUG_ENTER("RaidFd::Tell");
- DBUG_PRINT("enter",("MyFlags: %d _position: %lu",
- MyFlags, (ulong) _position));
- DBUG_RETURN(_position);
-}
-
-int RaidFd::
-Chsize(File fd, my_off_t newlength, int filler, myf MyFlags)
-{
- DBUG_ENTER("RaidFd::Chsize");
- DBUG_PRINT("enter",("Fd: %d newlength: %lu MyFlags: %d",
- fd, (ulong) newlength, MyFlags));
- _position = newlength;
- Calculate();
- uint _rounds = _total_block / _raid_chunks; // INT() assumed
- for (uint i = 0; i < _raid_chunks ; i++ )
- {
- int newpos;
- if ( i < _this_block )
- newpos = my_chsize(_fd_vector[i],
- _this_block * _raid_chunksize + (_rounds + 1) *
- _raid_chunksize, filler, MyFlags);
- else if ( i == _this_block )
- newpos = my_chsize(_fd_vector[i],
- _this_block * _raid_chunksize + _rounds *
- _raid_chunksize + (newlength % _raid_chunksize),
- filler, MyFlags);
- else // this means: i > _this_block
- newpos = my_chsize(_fd_vector[i],
- _this_block * _raid_chunksize + _rounds *
- _raid_chunksize, filler, MyFlags);
- if (newpos)
- DBUG_RETURN(1);
- }
- DBUG_RETURN(0);
-}
-
-
-int RaidFd::
-Fstat(int fd, MY_STAT *stat_area, myf MyFlags )
-{
- DBUG_ENTER("RaidFd::Fstat");
- DBUG_PRINT("enter",("fd: %d MyFlags: %d",fd,MyFlags));
- uint i;
- int error=0;
- MY_STAT status;
- stat_area->st_size=0;
- stat_area->st_mtime=0;
- stat_area->st_atime=0;
- stat_area->st_ctime=0;
-
- for(i=0 ; i < _raid_chunks ; i++)
- {
- if (my_fstat(_fd_vector[i],&status,MyFlags))
- error=1;
- stat_area->st_size+=status.st_size;
- set_if_bigger(stat_area->st_mtime,status.st_mtime);
- set_if_bigger(stat_area->st_atime,status.st_atime);
- set_if_bigger(stat_area->st_ctime,status.st_ctime);
- }
- DBUG_RETURN(error);
-}
-
-#endif /* defined(USE_RAID) && !defined(MYSQL_CLIENT) */
diff --git a/mysys/raid2.c b/mysys/raid2.c
deleted file mode 100644
index 4feace7410f..00000000000
--- a/mysys/raid2.c
+++ /dev/null
@@ -1,31 +0,0 @@
-/* Copyright (C) 2002 MySQL AB
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public
- License as published by the Free Software Foundation; version 2
- of the License.
-
- This library 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
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public
- License along with this library; if not, write to the Free
- Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
- MA 02111-1307, USA */
-
-/*
- RAID support for MySQL. For full comments, check raid.cc
- This is in a separate file to not cause problems on OS that can't
- put C++ files in archives.
-*/
-
-#include "mysys_priv.h"
-
-const char *raid_type_string[]={"none","striped"};
-
-const char *my_raid_type(int raid_type)
-{
- return raid_type_string[raid_type];
-}
diff --git a/mysys/safemalloc.c b/mysys/safemalloc.c
index a7d8f372151..f2a4c280610 100644
--- a/mysys/safemalloc.c
+++ b/mysys/safemalloc.c
@@ -524,7 +524,7 @@ char *_my_strdup(const char *from, const char *filename, uint lineno,
} /* _my_strdup */
-char *_my_strdup_with_length(const char *from, uint length,
+char *_my_strndup(const char *from, uint length,
const char *filename, uint lineno,
myf MyFlags)
{
diff --git a/mysys/string.c b/mysys/string.c
index df78f2b98b5..e1ff9ec8c26 100644
--- a/mysys/string.c
+++ b/mysys/string.c
@@ -114,6 +114,12 @@ my_bool dynstr_append_mem(DYNAMIC_STRING *str, const char *append,
return FALSE;
}
+my_bool dynstr_trunc(DYNAMIC_STRING *str, int n)
+{
+ str->length-=n;
+ str->str[str->length]= '\0';
+ return FALSE;
+}
/** Concatenates any number of strings, escapes any OS quote in the result then
* surround the whole affair in another set of quotes which is finally appended
diff --git a/mysys/test_dir.c b/mysys/test_dir.c
index f1e4987371b..54e6c99e21e 100644
--- a/mysys/test_dir.c
+++ b/mysys/test_dir.c
@@ -15,7 +15,6 @@
/* TODO: Test all functions */
-#define USES_TYPES
#include "mysys_priv.h"
#include "my_dir.h"
diff --git a/mysys/testhash.c b/mysys/testhash.c
index fd2bc73b2ae..0961558afd3 100644
--- a/mysys/testhash.c
+++ b/mysys/testhash.c
@@ -249,7 +249,6 @@ err:
static int get_options(int argc, char **argv)
{
char *pos,*progname;
- DEBUGGER_OFF;
progname= argv[0];
@@ -269,7 +268,6 @@ static int get_options(int argc, char **argv)
printf("Usage: %s [-?ABIKLWv] [-m#] [-t#]\n",progname);
exit(0);
case '#':
- DEBUGGER_ON;
DBUG_PUSH (++pos);
break;
}
diff --git a/mysys/thr_alarm.c b/mysys/thr_alarm.c
index d11883a4ea4..471ec0ab10d 100644
--- a/mysys/thr_alarm.c
+++ b/mysys/thr_alarm.c
@@ -14,8 +14,7 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
/* To avoid problems with alarms in debug code, we disable DBUG here */
-#undef DBUG_OFF
-#define DBUG_OFF
+#define FORCE_DBUG_OFF
#include <my_global.h>
#if defined(THREAD) && !defined(DONT_USE_THR_ALARM)
@@ -42,7 +41,7 @@ volatile my_bool alarm_thread_running= 0;
static sig_handler process_alarm_part2(int sig);
-#if !defined(__WIN__) && !defined(__EMX__) && !defined(OS2)
+#if !defined(__WIN__)
static pthread_mutex_t LOCK_alarm;
static pthread_cond_t COND_alarm;
@@ -154,6 +153,7 @@ my_bool thr_alarm(thr_alarm_t *alrm, uint sec, ALARM *alarm_data)
ulong now;
sigset_t old_mask;
my_bool reschedule;
+ struct st_my_thread_var *current_my_thread_var= my_thread_var;
DBUG_ENTER("thr_alarm");
DBUG_PRINT("enter",("thread: %s sec: %d",my_thread_name(),sec));
@@ -203,7 +203,8 @@ my_bool thr_alarm(thr_alarm_t *alrm, uint sec, ALARM *alarm_data)
alarm_data->malloced=0;
alarm_data->expire_time=now+sec;
alarm_data->alarmed=0;
- alarm_data->thread=pthread_self();
+ alarm_data->thread= current_my_thread_var->pthread_self;
+ alarm_data->thread_id= current_my_thread_var->id;
queue_insert(&alarm_queue,(byte*) alarm_data);
/* Reschedule alarm if the current one has more than sec left */
@@ -451,7 +452,7 @@ void end_thr_alarm(my_bool free_structures)
Remove another thread from the alarm
*/
-void thr_alarm_kill(pthread_t thread_id)
+void thr_alarm_kill(my_thread_id thread_id)
{
uint i;
if (alarm_aborted)
@@ -459,8 +460,7 @@ void thr_alarm_kill(pthread_t thread_id)
pthread_mutex_lock(&LOCK_alarm);
for (i=0 ; i < alarm_queue.elements ; i++)
{
- if (pthread_equal(((ALARM*) queue_element(&alarm_queue,i))->thread,
- thread_id))
+ if (((ALARM*) queue_element(&alarm_queue,i))->thread_id == thread_id)
{
ALARM *tmp=(ALARM*) queue_remove(&alarm_queue,i);
tmp->expire_time=0;
@@ -568,151 +568,12 @@ static void *alarm_handler(void *arg __attribute__((unused)))
#endif /* USE_ALARM_THREAD */
/*****************************************************************************
- thr_alarm for OS/2
-*****************************************************************************/
-
-#elif defined(__EMX__) || defined(OS2)
-
-#define INCL_BASE
-#define INCL_NOPMAPI
-#include <os2.h>
-
-static pthread_mutex_t LOCK_alarm;
-static sigset_t full_signal_set;
-static QUEUE alarm_queue;
-pthread_t alarm_thread;
-
-#ifdef USE_ALARM_THREAD
-static pthread_cond_t COND_alarm;
-static void *alarm_handler(void *arg);
-#define reschedule_alarms() pthread_cond_signal(&COND_alarm)
-#else
-#define reschedule_alarms() pthread_kill(alarm_thread,THR_SERVER_ALARM)
-#endif
-
-sig_handler process_alarm(int sig __attribute__((unused)))
-{
- sigset_t old_mask;
- ALARM *alarm_data;
- DBUG_PRINT("info",("sig: %d active alarms: %d",sig,alarm_queue.elements));
-}
-
-
-/*
- Remove another thread from the alarm
-*/
-
-void thr_alarm_kill(pthread_t thread_id)
-{
- uint i;
-
- pthread_mutex_lock(&LOCK_alarm);
- for (i=0 ; i < alarm_queue.elements ; i++)
- {
- if (pthread_equal(((ALARM*) queue_element(&alarm_queue,i))->thread,
- thread_id))
- {
- ALARM *tmp=(ALARM*) queue_remove(&alarm_queue,i);
- tmp->expire_time=0;
- queue_insert(&alarm_queue,(byte*) tmp);
- reschedule_alarms();
- break;
- }
- }
- pthread_mutex_unlock(&LOCK_alarm);
-}
-
-bool thr_alarm(thr_alarm_t *alrm, uint sec, ALARM *alarm)
-{
- APIRET rc;
- if (alarm_aborted)
- {
- alarm->alarmed.crono=0;
- alarm->alarmed.event=0;
- return 1;
- }
- if (rc = DosCreateEventSem(NULL,(HEV *) &alarm->alarmed.event,
- DC_SEM_SHARED,FALSE))
- {
- printf("Error creating event semaphore! [%d] \n",rc);
- alarm->alarmed.crono=0;
- alarm->alarmed.event=0;
- return 1;
- }
- if (rc = DosAsyncTimer((long) sec*1000L, (HSEM) alarm->alarmed.event,
- (HTIMER *) &alarm->alarmed.crono))
- {
- printf("Error starting async timer! [%d] \n",rc);
- DosCloseEventSem((HEV) alarm->alarmed.event);
- alarm->alarmed.crono=0;
- alarm->alarmed.event=0;
- return 1;
- } /* endif */
- (*alrm)= &alarm->alarmed;
- return 1;
-}
-
-
-bool thr_got_alarm(thr_alarm_t *alrm_ptr)
-{
- thr_alarm_t alrm= *alrm_ptr;
- APIRET rc;
-
- if (alrm->crono)
- {
- rc = DosWaitEventSem((HEV) alrm->event, SEM_IMMEDIATE_RETURN);
- if (rc == 0) {
- DosCloseEventSem((HEV) alrm->event);
- alrm->crono = 0;
- alrm->event = 0;
- } /* endif */
- }
- return !alrm->crono || alarm_aborted;
-}
-
-
-void thr_end_alarm(thr_alarm_t *alrm_ptr)
-{
- thr_alarm_t alrm= *alrm_ptr;
- if (alrm->crono)
- {
- DosStopTimer((HTIMER) alrm->crono);
- DosCloseEventSem((HEV) alrm->event);
- alrm->crono = 0;
- alrm->event = 0;
- }
-}
-
-void end_thr_alarm(my_bool free_structures)
-{
- DBUG_ENTER("end_thr_alarm");
- alarm_aborted=1; /* No more alarms */
- DBUG_VOID_RETURN;
-}
-
-void init_thr_alarm(uint max_alarm)
-{
- DBUG_ENTER("init_thr_alarm");
- alarm_aborted=0; /* Yes, Gimmie alarms */
- DBUG_VOID_RETURN;
-}
-
-void thr_alarm_info(ALARM_INFO *info)
-{
- bzero((char*) info, sizeof(*info));
-}
-
-void resize_thr_alarm(uint max_alarms)
-{
-}
-
-/*****************************************************************************
thr_alarm for win95
*****************************************************************************/
#else /* __WIN__ */
-void thr_alarm_kill(pthread_t thread_id)
+void thr_alarm_kill(my_thread_id thread_id)
{
/* Can't do this yet */
}
@@ -906,10 +767,8 @@ static sig_handler print_signal_warning(int sig)
#ifdef DONT_REMEMBER_SIGNAL
my_sigset(sig,print_signal_warning); /* int. thread system calls */
#endif
-#ifndef OS2
if (sig == SIGALRM)
alarm(2); /* reschedule alarm */
-#endif
}
#endif /* USE_ONE_SIGNAL_HAND */
@@ -926,7 +785,6 @@ static void *signal_hand(void *arg __attribute__((unused)))
VOID(pthread_cond_signal(&COND_thread_count)); /* Tell main we are ready */
pthread_mutex_unlock(&LOCK_thread_count);
-#ifndef OS2
sigemptyset(&set); /* Catch all signals */
sigaddset(&set,SIGINT);
sigaddset(&set,SIGQUIT);
@@ -941,7 +799,6 @@ static void *signal_hand(void *arg __attribute__((unused)))
#else
puts("Starting signal handling thread");
#endif
-#endif /* OS2 */
printf("server alarm: %d thread alarm: %d\n",
THR_SERVER_ALARM, thr_client_alarm);
DBUG_PRINT("info",("Starting signal and alarm handling thread"));
@@ -964,9 +821,7 @@ static void *signal_hand(void *arg __attribute__((unused)))
case SIGINT:
case SIGQUIT:
case SIGTERM:
-#ifndef OS2
case SIGHUP:
-#endif
printf("Aborting nicely\n");
end_thr_alarm(0);
break;
@@ -976,13 +831,11 @@ static void *signal_hand(void *arg __attribute__((unused)))
exit(1);
return 0; /* Keep some compilers happy */
#endif
-#ifndef OS2
#ifdef USE_ONE_SIGNAL_HAND
case THR_SERVER_ALARM:
process_alarm(sig);
break;
#endif
-#endif /* OS2 */
}
}
}
@@ -998,13 +851,13 @@ int main(int argc __attribute__((unused)),char **argv __attribute__((unused)))
MY_INIT(argv[0]);
if (argc > 1 && argv[1][0] == '-' && argv[1][1] == '#')
+ {
DBUG_PUSH(argv[1]+2);
-
+ }
pthread_mutex_init(&LOCK_thread_count,MY_MUTEX_INIT_FAST);
pthread_cond_init(&COND_thread_count,NULL);
/* Start a alarm handling thread */
-#ifndef OS2
sigemptyset(&set);
sigaddset(&set,SIGINT);
sigaddset(&set,SIGQUIT);
@@ -1022,7 +875,6 @@ int main(int argc __attribute__((unused)),char **argv __attribute__((unused)))
sigaddset(&set, thr_client_alarm);
VOID(pthread_sigmask(SIG_UNBLOCK, &set, (sigset_t*) 0));
#endif
-#endif /* OS2 */
pthread_attr_init(&thr_attr);
pthread_attr_setscope(&thr_attr,PTHREAD_SCOPE_PROCESS);
diff --git a/mysys/thr_lock.c b/mysys/thr_lock.c
index 93884921687..94c34eef2fa 100644
--- a/mysys/thr_lock.c
+++ b/mysys/thr_lock.c
@@ -71,7 +71,7 @@ multiple read locks.
*/
#if !defined(MAIN) && !defined(DBUG_OFF) && !defined(EXTRA_DEBUG)
-#define DBUG_OFF
+#define FORCE_DBUG_OFF
#endif
#include "mysys_priv.h"
@@ -343,8 +343,9 @@ void thr_lock_delete(THR_LOCK *lock)
void thr_lock_info_init(THR_LOCK_INFO *info)
{
- info->thread= pthread_self();
- info->thread_id= my_thread_id(); /* for debugging */
+ struct st_my_thread_var *tmp= my_thread_var;
+ info->thread= tmp->pthread_self;
+ info->thread_id= tmp->id;
info->n_cursors= 0;
}
@@ -481,7 +482,7 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner,
data->type=lock_type;
data->owner= owner; /* Must be reset ! */
VOID(pthread_mutex_lock(&lock->mutex));
- DBUG_PRINT("lock",("data: 0x%lx thread: %ld lock: 0x%lx type: %d",
+ DBUG_PRINT("lock",("data: 0x%lx thread: 0x%lx lock: 0x%lx type: %d",
(long) data, data->owner->info->thread_id,
(long) lock, (int) lock_type));
check_locks(lock,(uint) lock_type <= (uint) TL_READ_NO_INSERT ?
@@ -500,7 +501,7 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner,
and the read lock is not TL_READ_NO_INSERT
*/
- DBUG_PRINT("lock",("write locked by thread: %ld",
+ DBUG_PRINT("lock",("write locked by thread: 0x%lx",
lock->write.data->owner->info->thread_id));
if (thr_lock_owner_equal(data->owner, lock->write.data->owner) ||
(lock->write.data->type <= TL_WRITE_DELAYED &&
@@ -622,8 +623,10 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner,
statistic_increment(locks_immediate,&THR_LOCK_lock);
goto end;
}
- DBUG_PRINT("lock",("write locked by thread: %ld",
+ /* purecov: begin inspected */
+ DBUG_PRINT("lock",("write locked by thread: 0x%lx",
lock->write.data->owner->info->thread_id));
+ /* purecov: end */
}
else
{
@@ -658,7 +661,7 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner,
goto end;
}
}
- DBUG_PRINT("lock",("write locked by thread: %ld type: %d",
+ DBUG_PRINT("lock",("write locked by thread: 0x%lx type: %d",
lock->read.data->owner->info->thread_id, data->type));
}
wait_queue= &lock->write_wait;
@@ -720,8 +723,10 @@ static inline void free_all_read_locks(THR_LOCK *lock,
}
lock->read_no_write_count++;
}
- DBUG_PRINT("lock",("giving read lock to thread: %ld",
+ /* purecov: begin inspected */
+ DBUG_PRINT("lock",("giving read lock to thread: 0x%lx",
data->owner->info->thread_id));
+ /* purecov: end */
data->cond=0; /* Mark thread free */
VOID(pthread_cond_signal(cond));
} while ((data=data->next));
@@ -738,7 +743,7 @@ void thr_unlock(THR_LOCK_DATA *data)
THR_LOCK *lock=data->lock;
enum thr_lock_type lock_type=data->type;
DBUG_ENTER("thr_unlock");
- DBUG_PRINT("lock",("data: 0x%lx thread: %ld lock: 0x%lx",
+ DBUG_PRINT("lock",("data: 0x%lx thread: 0x%lx lock: 0x%lx",
(long) data, data->owner->info->thread_id, (long) lock));
pthread_mutex_lock(&lock->mutex);
check_locks(lock,"start of release lock",0);
@@ -806,8 +811,10 @@ void thr_unlock(THR_LOCK_DATA *data)
if (data->type == TL_WRITE_CONCURRENT_INSERT &&
(*lock->check_status)(data->status_param))
data->type=TL_WRITE; /* Upgrade lock */
- DBUG_PRINT("lock",("giving write lock of type %d to thread: %ld",
+ /* purecov: begin inspected */
+ DBUG_PRINT("lock",("giving write lock of type %d to thread: 0x%lx",
data->type, data->owner->info->thread_id));
+ /* purecov: end */
{
pthread_cond_t *cond=data->cond;
data->cond=0; /* Mark thread free */
@@ -1006,7 +1013,7 @@ void thr_multi_unlock(THR_LOCK_DATA **data,uint count)
thr_unlock(*pos);
else
{
- DBUG_PRINT("lock",("Free lock: data: 0x%lx thread: %ld lock: 0x%lx",
+ DBUG_PRINT("lock",("Free lock: data: 0x%lx thread: 0x%lx lock: 0x%lx",
(long) *pos, (*pos)->owner->info->thread_id,
(long) (*pos)->lock));
}
@@ -1019,7 +1026,7 @@ void thr_multi_unlock(THR_LOCK_DATA **data,uint count)
TL_WRITE_ONLY to abort any new accesses to the lock
*/
-void thr_abort_locks(THR_LOCK *lock)
+void thr_abort_locks(THR_LOCK *lock, bool upgrade_lock)
{
THR_LOCK_DATA *data;
DBUG_ENTER("thr_abort_locks");
@@ -1041,7 +1048,7 @@ void thr_abort_locks(THR_LOCK *lock)
lock->read_wait.last= &lock->read_wait.data;
lock->write_wait.last= &lock->write_wait.data;
lock->read_wait.data=lock->write_wait.data=0;
- if (lock->write.data)
+ if (upgrade_lock && lock->write.data)
lock->write.data->type=TL_WRITE_ONLY;
pthread_mutex_unlock(&lock->mutex);
DBUG_VOID_RETURN;
@@ -1054,7 +1061,7 @@ void thr_abort_locks(THR_LOCK *lock)
This is used to abort all locks for a specific thread
*/
-my_bool thr_abort_locks_for_thread(THR_LOCK *lock, pthread_t thread)
+my_bool thr_abort_locks_for_thread(THR_LOCK *lock, my_thread_id thread_id)
{
THR_LOCK_DATA *data;
my_bool found= FALSE;
@@ -1063,7 +1070,7 @@ my_bool thr_abort_locks_for_thread(THR_LOCK *lock, pthread_t thread)
pthread_mutex_lock(&lock->mutex);
for (data= lock->read_wait.data; data ; data= data->next)
{
- if (pthread_equal(thread, data->owner->info->thread))
+ if (data->owner->info->thread_id == thread_id) /* purecov: tested */
{
DBUG_PRINT("info",("Aborting read-wait lock"));
data->type= TL_UNLOCK; /* Mark killed */
@@ -1080,7 +1087,7 @@ my_bool thr_abort_locks_for_thread(THR_LOCK *lock, pthread_t thread)
}
for (data= lock->write_wait.data; data ; data= data->next)
{
- if (pthread_equal(thread, data->owner->info->thread))
+ if (data->owner->info->thread_id == thread_id) /* purecov: tested */
{
DBUG_PRINT("info",("Aborting write-wait lock"));
data->type= TL_UNLOCK;
@@ -1099,6 +1106,218 @@ my_bool thr_abort_locks_for_thread(THR_LOCK *lock, pthread_t thread)
}
+/*
+ Downgrade a WRITE_* to a lower WRITE level
+ SYNOPSIS
+ thr_downgrade_write_lock()
+ in_data Lock data of thread downgrading its lock
+ new_lock_type New write lock type
+ RETURN VALUE
+ NONE
+ DESCRIPTION
+ This can be used to downgrade a lock already owned. When the downgrade
+ occurs also other waiters, both readers and writers can be allowed to
+ start.
+ The previous lock is often TL_WRITE_ONLY but can also be
+ TL_WRITE and TL_WRITE_ALLOW_READ. The normal downgrade variants are
+ TL_WRITE_ONLY => TL_WRITE_ALLOW_READ After a short exclusive lock
+ TL_WRITE_ALLOW_READ => TL_WRITE_ALLOW_WRITE After discovering that the
+ operation didn't need such a high lock.
+ TL_WRITE_ONLY => TL_WRITE after a short exclusive lock while holding a
+ write table lock
+ TL_WRITE_ONLY => TL_WRITE_ALLOW_WRITE After a short exclusive lock after
+ already earlier having dongraded lock to TL_WRITE_ALLOW_WRITE
+ The implementation is conservative and rather don't start rather than
+ go on unknown paths to start, the common cases are handled.
+
+ NOTE:
+ In its current implementation it is only allowed to downgrade from
+ TL_WRITE_ONLY. In this case there are no waiters. Thus no wake up
+ logic is required.
+*/
+
+void thr_downgrade_write_lock(THR_LOCK_DATA *in_data,
+ enum thr_lock_type new_lock_type)
+{
+ THR_LOCK *lock=in_data->lock;
+#ifndef DBUG_OFF
+ enum thr_lock_type old_lock_type= in_data->type;
+#endif
+#ifdef TO_BE_REMOVED
+ THR_LOCK_DATA *data, *next;
+ bool start_writers= FALSE;
+ bool start_readers= FALSE;
+#endif
+ DBUG_ENTER("thr_downgrade_write_only_lock");
+
+ pthread_mutex_lock(&lock->mutex);
+ DBUG_ASSERT(old_lock_type == TL_WRITE_ONLY);
+ DBUG_ASSERT(old_lock_type > new_lock_type);
+ in_data->type= new_lock_type;
+ check_locks(lock,"after downgrading lock",0);
+
+#if TO_BE_REMOVED
+ switch (old_lock_type)
+ {
+ case TL_WRITE_ONLY:
+ case TL_WRITE:
+ case TL_WRITE_LOW_PRIORITY:
+ /*
+ Previous lock was exclusive we are now ready to start up most waiting
+ threads.
+ */
+ switch (new_lock_type)
+ {
+ case TL_WRITE_ALLOW_READ:
+ /* Still cannot start WRITE operations. Can only start readers. */
+ start_readers= TRUE;
+ break;
+ case TL_WRITE:
+ case TL_WRITE_LOW_PRIORITY:
+ /*
+ Still cannot start anything, but new requests are no longer
+ aborted.
+ */
+ break;
+ case TL_WRITE_ALLOW_WRITE:
+ /*
+ We can start both writers and readers.
+ */
+ start_writers= TRUE;
+ start_readers= TRUE;
+ break;
+ case TL_WRITE_CONCURRENT_INSERT:
+ case TL_WRITE_DELAYED:
+ /*
+ This routine is not designed for those. Lock will be downgraded
+ but no start of waiters will occur. This is not the optimal but
+ should be a correct behaviour.
+ */
+ break;
+ default:
+ DBUG_ASSERT(0);
+ }
+ break;
+ case TL_WRITE_DELAYED:
+ case TL_WRITE_CONCURRENT_INSERT:
+ /*
+ This routine is not designed for those. Lock will be downgraded
+ but no start of waiters will occur. This is not the optimal but
+ should be a correct behaviour.
+ */
+ break;
+ case TL_WRITE_ALLOW_READ:
+ DBUG_ASSERT(new_lock_type == TL_WRITE_ALLOW_WRITE);
+ /*
+ Previously writers were not allowed to start, now it is ok to
+ start them again. Readers are already allowed so no reason to
+ handle them.
+ */
+ start_writers= TRUE;
+ break;
+ default:
+ DBUG_ASSERT(0);
+ break;
+ }
+ if (start_writers)
+ {
+ /*
+ At this time the only active writer can be ourselves. Thus we need
+ not worry about that there are other concurrent write operations
+ active on the table. Thus we only need to worry about starting
+ waiting operations.
+ We also only come here with TL_WRITE_ALLOW_WRITE as the new
+ lock type, thus we can start other writers also of the same type.
+ If we find a lock at exclusive level >= TL_WRITE_LOW_PRIORITY we
+ don't start any more operations that would be mean those operations
+ will have to wait for things started afterwards.
+ */
+ DBUG_ASSERT(new_lock_type == TL_WRITE_ALLOW_WRITE);
+ for (data=lock->write_wait.data; data ; data= next)
+ {
+ /*
+ All WRITE requests compatible with new lock type are also
+ started
+ */
+ next= data->next;
+ if (start_writers && data->type == new_lock_type)
+ {
+ pthread_cond_t *cond= data->cond;
+ /*
+ It is ok to start this waiter.
+ Move from being first in wait queue to be last in write queue.
+ */
+ if (((*data->prev)= data->next))
+ data->next->prev= data->prev;
+ else
+ lock->write_wait.last= data->prev;
+ data->prev= lock->write.last;
+ lock->write.last= &data->next;
+ data->next= 0;
+ check_locks(lock, "Started write lock after downgrade",0);
+ data->cond= 0;
+ pthread_cond_signal(cond);
+ }
+ else
+ {
+ /*
+ We found an incompatible lock, we won't start any more write
+ requests to avoid letting writers pass other writers in the
+ queue.
+ */
+ start_writers= FALSE;
+ if (data->type >= TL_WRITE_LOW_PRIORITY)
+ {
+ /*
+ We have an exclusive writer in the queue so we won't start
+ readers either.
+ */
+ start_readers= FALSE;
+ }
+ }
+ }
+ }
+ if (start_readers)
+ {
+ DBUG_ASSERT(new_lock_type == TL_WRITE_ALLOW_WRITE ||
+ new_lock_type == TL_WRITE_ALLOW_READ);
+ /*
+ When we come here we know that the write locks are
+ TL_WRITE_ALLOW_WRITE or TL_WRITE_ALLOW_READ. This means that reads
+ are ok
+ */
+ for (data=lock->read_wait.data; data ; data=next)
+ {
+ next= data->next;
+ /*
+ All reads are ok to start now except TL_READ_NO_INSERT when
+ write lock is TL_WRITE_ALLOW_READ.
+ */
+ if (new_lock_type != TL_WRITE_ALLOW_READ ||
+ data->type != TL_READ_NO_INSERT)
+ {
+ pthread_cond_t *cond= data->cond;
+ if (((*data->prev)= data->next))
+ data->next->prev= data->prev;
+ else
+ lock->read_wait.last= data->prev;
+ data->prev= lock->read.last;
+ lock->read.last= &data->next;
+ data->next= 0;
+
+ if (data->type == TL_READ_NO_INSERT)
+ lock->read_no_write_count++;
+ check_locks(lock, "Started read lock after downgrade",0);
+ data->cond= 0;
+ pthread_cond_signal(cond);
+ }
+ }
+ }
+ check_locks(lock,"after starting waiters after downgrading lock",0);
+#endif
+ pthread_mutex_unlock(&lock->mutex);
+ DBUG_VOID_RETURN;
+}
/* Upgrade a WRITE_DELAY lock to a WRITE_LOCK */
@@ -1318,6 +1537,10 @@ static void test_get_status(void* param __attribute__((unused)),
{
}
+static void test_update_status(void* param __attribute__((unused)))
+{
+}
+
static void test_copy_status(void* to __attribute__((unused)) ,
void *from __attribute__((unused)))
{
@@ -1410,7 +1633,7 @@ int main(int argc __attribute__((unused)),char **argv __attribute__((unused)))
{
thr_lock_init(locks+i);
locks[i].check_status= test_check_status;
- locks[i].update_status=test_get_status;
+ locks[i].update_status=test_update_status;
locks[i].copy_status= test_copy_status;
locks[i].get_status= test_get_status;
}
diff --git a/mysys/thr_mutex.c b/mysys/thr_mutex.c
index 425e5fce459..007bae2accf 100644
--- a/mysys/thr_mutex.c
+++ b/mysys/thr_mutex.c
@@ -212,7 +212,7 @@ int safe_cond_wait(pthread_cond_t *cond, safe_mutex_t *mp, const char *file,
{
fprintf(stderr,
"safe_mutex: Count was %d in thread 0x%lx when locking mutex at %s, line %d\n",
- mp->count-1, my_thread_id(), file, line);
+ mp->count-1, my_thread_dbug_id(), file, line);
fflush(stderr);
abort();
}
@@ -250,7 +250,7 @@ int safe_cond_timedwait(pthread_cond_t *cond, safe_mutex_t *mp,
{
fprintf(stderr,
"safe_mutex: Count was %d in thread 0x%lx when locking mutex at %s, line %d (error: %d (%d))\n",
- mp->count-1, my_thread_id(), file, line, error, error);
+ mp->count-1, my_thread_dbug_id(), file, line, error, error);
fflush(stderr);
abort();
}
@@ -356,3 +356,80 @@ void safe_mutex_end(FILE *file __attribute__((unused)))
}
#endif /* THREAD && SAFE_MUTEX */
+
+#if defined(THREAD) && defined(MY_PTHREAD_FASTMUTEX) && !defined(SAFE_MUTEX)
+
+#include "mysys_priv.h"
+#include "my_static.h"
+#include <m_string.h>
+
+#include <m_ctype.h>
+#include <hash.h>
+#include <myisampack.h>
+#include <mysys_err.h>
+#include <my_sys.h>
+
+#undef pthread_mutex_t
+#undef pthread_mutex_init
+#undef pthread_mutex_lock
+#undef pthread_mutex_trylock
+#undef pthread_mutex_unlock
+#undef pthread_mutex_destroy
+#undef pthread_cond_wait
+#undef pthread_cond_timedwait
+
+ulong mutex_delay(ulong delayloops)
+{
+ ulong i;
+ volatile ulong j;
+
+ j = 0;
+
+ for (i = 0; i < delayloops * 50; i++)
+ j += i;
+
+ return(j);
+}
+
+#define MY_PTHREAD_FASTMUTEX_SPINS 8
+#define MY_PTHREAD_FASTMUTEX_DELAY 4
+
+int my_pthread_fastmutex_init(my_pthread_fastmutex_t *mp,
+ const pthread_mutexattr_t *attr)
+{
+ static int cpu_count= 0;
+#ifdef _SC_NPROCESSORS_CONF
+ if (!cpu_count && (attr == MY_MUTEX_INIT_FAST))
+ cpu_count= sysconf(_SC_NPROCESSORS_CONF);
+#endif
+
+ if ((cpu_count > 1) && (attr == MY_MUTEX_INIT_FAST))
+ mp->spins= MY_PTHREAD_FASTMUTEX_SPINS;
+ else
+ mp->spins= 0;
+ return pthread_mutex_init(&mp->mutex, attr);
+}
+
+int my_pthread_fastmutex_lock(my_pthread_fastmutex_t *mp)
+{
+ int res;
+ uint i;
+ uint maxdelay= MY_PTHREAD_FASTMUTEX_DELAY;
+
+ for (i= 0; i < mp->spins; i++)
+ {
+ res= pthread_mutex_trylock(&mp->mutex);
+
+ if (res == 0)
+ return 0;
+
+ if (res != EBUSY)
+ return res;
+
+ mutex_delay(maxdelay);
+ maxdelay += ((double) random() / (double) RAND_MAX) *
+ MY_PTHREAD_FASTMUTEX_DELAY + 1;
+ }
+ return pthread_mutex_lock(&mp->mutex);
+}
+#endif /* defined(THREAD) && defined(MY_PTHREAD_FASTMUTEX) && !defined(SAFE_MUTEX) */
diff --git a/mysys/trie.c b/mysys/trie.c
new file mode 100644
index 00000000000..8daa6f3e661
--- /dev/null
+++ b/mysys/trie.c
@@ -0,0 +1,236 @@
+/* Copyright (C) 2005 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 */
+
+/*
+ Implementation of trie and Aho-Corasick automaton.
+ Supports only charsets that can be compared byte-wise.
+
+ TODO:
+ Add character frequencies. Can increase lookup speed
+ up to 30%.
+ Implement character-wise comparision.
+*/
+
+
+#include "mysys_priv.h"
+#include <m_string.h>
+#include <my_trie.h>
+#include <my_base.h>
+
+
+/*
+ SYNOPSIS
+ TRIE *trie_init (TRIE *trie, CHARSET_INFO *charset);
+
+ DESCRIPTION
+ Allocates or initializes a `TRIE' object. If `trie' is a `NULL'
+ pointer, the function allocates, initializes, and returns a new
+ object. Otherwise, the object is initialized and the address of
+ the object is returned. If `trie_init()' allocates a new object,
+ it will be freed when `trie_free()' is called.
+
+ RETURN VALUE
+ An initialized `TRIE*' object. `NULL' if there was insufficient
+ memory to allocate a new object.
+*/
+
+TRIE *trie_init (TRIE *trie, CHARSET_INFO *charset)
+{
+ MEM_ROOT mem_root;
+ DBUG_ENTER("trie_init");
+ DBUG_ASSERT(charset);
+ init_alloc_root(&mem_root,
+ (sizeof(TRIE_NODE) * 128) + ALLOC_ROOT_MIN_BLOCK_SIZE,
+ sizeof(TRIE_NODE) * 128);
+ if (! trie)
+ {
+ if (! (trie= (TRIE *)alloc_root(&mem_root, sizeof(TRIE))))
+ {
+ free_root(&mem_root, MYF(0));
+ DBUG_RETURN(NULL);
+ }
+ }
+
+ memcpy(&trie->mem_root, &mem_root, sizeof(MEM_ROOT));
+ trie->root.leaf= 0;
+ trie->root.c= 0;
+ trie->root.next= NULL;
+ trie->root.links= NULL;
+ trie->root.fail= NULL;
+ trie->charset= charset;
+ trie->nnodes= 0;
+ trie->nwords= 0;
+ DBUG_RETURN(trie);
+}
+
+
+/*
+ SYNOPSIS
+ void trie_free (TRIE *trie);
+ trie - valid pointer to `TRIE'
+
+ DESCRIPTION
+ Frees the memory allocated for a `trie'.
+
+ RETURN VALUE
+ None.
+*/
+
+void trie_free (TRIE *trie)
+{
+ MEM_ROOT mem_root;
+ DBUG_ENTER("trie_free");
+ DBUG_ASSERT(trie);
+ memcpy(&mem_root, &trie->mem_root, sizeof(MEM_ROOT));
+ free_root(&mem_root, MYF(0));
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ SYNOPSIS
+ my_bool trie_insert (TRIE *trie, const byte *key, uint keylen);
+ trie - valid pointer to `TRIE'
+ key - valid pointer to key to insert
+ keylen - non-0 key length
+
+ DESCRIPTION
+ Inserts new key into trie.
+
+ RETURN VALUE
+ Upon successful completion, `trie_insert' returns `FALSE'. Otherwise
+ `TRUE' is returned.
+
+ NOTES
+ If this function fails you must assume `trie' is broken.
+ However it can be freed with trie_free().
+*/
+
+my_bool trie_insert (TRIE *trie, const byte *key, uint keylen)
+{
+ TRIE_NODE *node;
+ TRIE_NODE *next;
+ byte p;
+ uint k;
+ DBUG_ENTER("trie_insert");
+ DBUG_ASSERT(trie && key && keylen);
+ node= &trie->root;
+ trie->root.fail= NULL;
+ for (k= 0; k < keylen; k++)
+ {
+ p= key[k];
+ for (next= node->links; next; next= next->next)
+ if (next->c == p)
+ break;
+
+ if (! next)
+ {
+ TRIE_NODE *tmp= (TRIE_NODE *)alloc_root(&trie->mem_root,
+ sizeof(TRIE_NODE));
+ if (! tmp)
+ DBUG_RETURN(TRUE);
+ tmp->leaf= 0;
+ tmp->c= p;
+ tmp->links= tmp->fail= tmp->next= NULL;
+ trie->nnodes++;
+ if (! node->links)
+ {
+ node->links= tmp;
+ }
+ else
+ {
+ for (next= node->links; next->next; next= next->next) /* no-op */;
+ next->next= tmp;
+ }
+ node= tmp;
+ }
+ else
+ {
+ node= next;
+ }
+ }
+ node->leaf= keylen;
+ trie->nwords++;
+ DBUG_RETURN(FALSE);
+}
+
+
+/*
+ SYNOPSIS
+ my_bool trie_prepare (TRIE *trie);
+ trie - valid pointer to `TRIE'
+
+ DESCRIPTION
+ Constructs Aho-Corasick automaton.
+
+ RETURN VALUE
+ Upon successful completion, `trie_prepare' returns `FALSE'. Otherwise
+ `TRUE' is returned.
+*/
+
+my_bool ac_trie_prepare (TRIE *trie)
+{
+ TRIE_NODE **tmp_nodes;
+ TRIE_NODE *node;
+ uint32 fnode= 0;
+ uint32 lnode= 0;
+ DBUG_ENTER("trie_prepare");
+ DBUG_ASSERT(trie);
+
+ tmp_nodes= (TRIE_NODE **)my_malloc(trie->nnodes * sizeof(TRIE_NODE *), MYF(0));
+ if (! tmp_nodes)
+ DBUG_RETURN(TRUE);
+
+ trie->root.fail= &trie->root;
+ for (node= trie->root.links; node; node= node->next)
+ {
+ node->fail= &trie->root;
+ tmp_nodes[lnode++]= node;
+ }
+
+ while (fnode < lnode)
+ {
+ TRIE_NODE *current= (TRIE_NODE *)tmp_nodes[fnode++];
+ for (node= current->links; node; node= node->next)
+ {
+ TRIE_NODE *fail= current->fail;
+ tmp_nodes[lnode++]= node;
+ while (! (node->fail= trie_goto(&trie->root, fail, node->c)))
+ fail= fail->fail;
+ }
+ }
+ my_free((gptr)tmp_nodes, MYF(0));
+ DBUG_RETURN(FALSE);
+}
+
+
+/*
+ SYNOPSIS
+ void ac_trie_init (TRIE *trie, AC_TRIE_STATE *state);
+ trie - valid pointer to `TRIE'
+ state - value pointer to `AC_TRIE_STATE'
+
+ DESCRIPTION
+ Initializes `AC_TRIE_STATE' object.
+*/
+
+void ac_trie_init (TRIE *trie, AC_TRIE_STATE *state)
+{
+ DBUG_ENTER("ac_trie_init");
+ DBUG_ASSERT(trie && state);
+ state->trie= trie;
+ state->node= &trie->root;
+ DBUG_VOID_RETURN;
+}
diff --git a/mysys/typelib.c b/mysys/typelib.c
index 4fab6f20493..47fe6967da5 100644
--- a/mysys/typelib.c
+++ b/mysys/typelib.c
@@ -20,6 +20,30 @@
#include <m_ctype.h>
+static const char field_separator=',';
+
+int find_type_or_exit(const char *x, TYPELIB *typelib, const char *option)
+{
+ int res;
+ const char **ptr;
+
+ if ((res= find_type((my_string) x, typelib, 2)) <= 0)
+ {
+ ptr= typelib->type_names;
+ if (!*x)
+ fprintf(stderr, "No option given to %s\n", option);
+ else
+ fprintf(stderr, "Unknown option to %s: %s\n", option, x);
+ fprintf(stderr, "Alternatives are: '%s'", *ptr);
+ while (*++ptr)
+ fprintf(stderr, ",'%s'", *ptr);
+ fprintf(stderr, "\n");
+ exit(1);
+ }
+ return res;
+}
+
+
/*
Search after a string in a list of strings. Endspace in x is not compared.
@@ -31,6 +55,7 @@
If & 1 accept only whole names
If & 2 don't expand if half field
If & 4 allow #number# as type
+ If & 8 use ',' as string terminator
NOTES
If part, uniq field is found and full_name == 0 then x is expanded
@@ -42,7 +67,7 @@
>0 Offset+1 in typelib for matched string
*/
-int find_type(my_string x, TYPELIB *typelib, uint full_name)
+int find_type(char *x, const TYPELIB *typelib, uint full_name)
{
int find,pos,findpos;
reg1 my_string i;
@@ -60,16 +85,18 @@ int find_type(my_string x, TYPELIB *typelib, uint full_name)
for (pos=0 ; (j=typelib->type_names[pos]) ; pos++)
{
for (i=x ;
- *i && my_toupper(&my_charset_latin1,*i) ==
+ *i && (!(full_name & 8) || *i != field_separator) &&
+ my_toupper(&my_charset_latin1,*i) ==
my_toupper(&my_charset_latin1,*j) ; i++, j++) ;
if (! *j)
{
while (*i == ' ')
i++; /* skip_end_space */
- if (! *i)
+ if (! *i || ((full_name & 8) && *i == field_separator))
DBUG_RETURN(pos+1);
}
- if (! *i && (!*j || !(full_name & 1)))
+ if ((!*i && (!(full_name & 8) || *i != field_separator)) &&
+ (!*j || !(full_name & 1)))
{
find++;
findpos=pos;
@@ -121,6 +148,50 @@ const char *get_type(TYPELIB *typelib, uint nr)
/*
+ Create an integer value to represent the supplied comma-seperated
+ string where each string in the TYPELIB denotes a bit position.
+
+ SYNOPSIS
+ find_typeset()
+ x string to decompose
+ lib TYPELIB (struct of pointer to values + count)
+ err index (not char position) of string element which was not
+ found or 0 if there was no error
+
+ RETURN
+ a integer representation of the supplied string
+*/
+
+my_ulonglong find_typeset(my_string x, TYPELIB *lib, int *err)
+{
+ my_ulonglong result;
+ int find;
+ my_string i;
+ DBUG_ENTER("find_set");
+ DBUG_PRINT("enter",("x: '%s' lib: 0x%lx", x, (long) lib));
+
+ if (!lib->count)
+ {
+ DBUG_PRINT("exit",("no count"));
+ DBUG_RETURN(0);
+ }
+ result= 0;
+ *err= 0;
+ while (*x)
+ {
+ (*err)++;
+ i= x;
+ while (*x && *x != field_separator) x++;
+ if ((find= find_type(i, lib, 2 | 8) - 1) < 0)
+ DBUG_RETURN(0);
+ result|= (ULL(1) << find);
+ }
+ *err= 0;
+ DBUG_RETURN(result);
+} /* find_set */
+
+
+/*
Create a copy of a specified TYPELIB structure.
SYNOPSIS