diff options
Diffstat (limited to 'mysys')
-rw-r--r-- | mysys/CMakeLists.txt | 12 | ||||
-rw-r--r-- | mysys/array.c | 41 | ||||
-rw-r--r-- | mysys/charset-def.c | 10 | ||||
-rw-r--r-- | mysys/checksum.c | 4 | ||||
-rw-r--r-- | mysys/hash.c | 7 | ||||
-rw-r--r-- | mysys/lf_alloc-pin.c | 42 | ||||
-rw-r--r-- | mysys/lf_dynarray.c | 12 | ||||
-rw-r--r-- | mysys/lf_hash.c | 239 | ||||
-rw-r--r-- | mysys/ma_dyncol.c | 7 | ||||
-rw-r--r-- | mysys/mf_cache.c | 50 | ||||
-rw-r--r-- | mysys/mf_iocache.c | 427 | ||||
-rw-r--r-- | mysys/mf_iocache2.c | 60 | ||||
-rw-r--r-- | mysys/mf_keycache.c | 2 | ||||
-rw-r--r-- | mysys/my_access.c | 9 | ||||
-rw-r--r-- | mysys/my_addr_resolve.c | 93 | ||||
-rw-r--r-- | mysys/my_alloc.c | 20 | ||||
-rw-r--r-- | mysys/my_bitmap.c | 14 | ||||
-rw-r--r-- | mysys/my_compress.c | 125 | ||||
-rw-r--r-- | mysys/my_create.c | 2 | ||||
-rw-r--r-- | mysys/my_default.c | 11 | ||||
-rw-r--r-- | mysys/my_fopen.c | 11 | ||||
-rw-r--r-- | mysys/my_getopt.c | 175 | ||||
-rw-r--r-- | mysys/my_init.c | 18 | ||||
-rw-r--r-- | mysys/my_open.c | 4 | ||||
-rw-r--r-- | mysys/my_rdtsc.c | 16 | ||||
-rw-r--r-- | mysys/mysys_priv.h | 26 | ||||
-rw-r--r-- | mysys/ptr_cmp.c | 20 | ||||
-rw-r--r-- | mysys/stacktrace.c | 3 | ||||
-rw-r--r-- | mysys/thr_lock.c | 131 | ||||
-rw-r--r-- | mysys/thr_timer.c | 570 | ||||
-rw-r--r-- | mysys/waiting_threads.c | 64 |
31 files changed, 1551 insertions, 674 deletions
diff --git a/mysys/CMakeLists.txt b/mysys/CMakeLists.txt index 4756bbccf2f..0d404586569 100644 --- a/mysys/CMakeLists.txt +++ b/mysys/CMakeLists.txt @@ -33,7 +33,9 @@ SET(MYSYS_SOURCES array.c charset-def.c charset.c checksum.c my_default.c my_basename.c my_write.c ptr_cmp.c queues.c stacktrace.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 thr_timer.c + tree.c typelib.c base64.c my_memmem.c + my_getpagesize.c lf_alloc-pin.c lf_dynarray.c lf_hash.c safemalloc.c my_new.cc my_atomic.c my_getncpus.c my_safehash.c my_chmod.c my_rnd.c @@ -68,8 +70,8 @@ IF(HAVE_MLOCK) ENDIF() ADD_CONVENIENCE_LIBRARY(mysys ${MYSYS_SOURCES}) -TARGET_LINK_LIBRARIES(mysys dbug strings ${ZLIB_LIBRARY} - ${LIBNSL} ${LIBM} ${LIBRT} ${LIBSOCKET} ${LIBEXECINFO}) +TARGET_LINK_LIBRARIES(mysys dbug strings mysys_ssl ${ZLIB_LIBRARY} + ${LIBNSL} ${LIBM} ${LIBRT} ${LIBDL} ${LIBSOCKET} ${LIBEXECINFO}) DTRACE_INSTRUMENT(mysys) IF(HAVE_BFD_H) @@ -89,6 +91,10 @@ ADD_EXECUTABLE(thr_lock thr_lock.c) TARGET_LINK_LIBRARIES(thr_lock mysys) SET_TARGET_PROPERTIES(thr_lock PROPERTIES COMPILE_FLAGS "-DMAIN") +ADD_EXECUTABLE(thr_timer thr_timer.c) +TARGET_LINK_LIBRARIES(thr_timer mysys) +SET_TARGET_PROPERTIES(thr_timer PROPERTIES COMPILE_FLAGS "-DMAIN") + IF(MSVC) INSTALL_DEBUG_TARGET(mysys DESTINATION ${INSTALL_LIBDIR}/debug) ENDIF() diff --git a/mysys/array.c b/mysys/array.c index a8c5d181638..35a41f2222c 100644 --- a/mysys/array.c +++ b/mysys/array.c @@ -35,7 +35,6 @@ init_alloc eilements. Array is usable even if space allocation failed, hence, the function never returns TRUE. - Static buffers must begin immediately after the array structure. RETURN VALUE FALSE Ok @@ -57,8 +56,12 @@ my_bool init_dynamic_array2(DYNAMIC_ARRAY *array, uint element_size, array->alloc_increment=alloc_increment; array->size_of_element=element_size; array->malloc_flags= my_flags; + DBUG_ASSERT((my_flags & MY_INIT_BUFFER_USED) == 0); if ((array->buffer= init_buffer)) + { + array->malloc_flags|= MY_INIT_BUFFER_USED; DBUG_RETURN(FALSE); + } /* Since the dynamic array is usable even if allocation fails here malloc should not throw an error @@ -124,10 +127,10 @@ void *alloc_dynamic(DYNAMIC_ARRAY *array) if (array->elements == array->max_element) { char *new_ptr; - if (array->buffer == (uchar *)(array + 1)) + if (array->malloc_flags & MY_INIT_BUFFER_USED) { /* - In this senerio, the buffer is statically preallocated, + In this scenario, 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+ @@ -137,6 +140,7 @@ void *alloc_dynamic(DYNAMIC_ARRAY *array) DBUG_RETURN(0); memcpy(new_ptr, array->buffer, array->elements * array->size_of_element); + array->malloc_flags&= ~MY_INIT_BUFFER_USED; } else if (!(new_ptr=(char*) my_realloc(array->buffer,(array->max_element+ @@ -231,7 +235,7 @@ my_bool allocate_dynamic(DYNAMIC_ARRAY *array, uint max_elements) uchar *new_ptr; size= (max_elements + array->alloc_increment)/array->alloc_increment; size*= array->alloc_increment; - if (array->buffer == (uchar *)(array + 1)) + if (array->malloc_flags & MY_INIT_BUFFER_USED) { /* In this senerio, the buffer is statically preallocated, @@ -243,7 +247,8 @@ my_bool allocate_dynamic(DYNAMIC_ARRAY *array, uint max_elements) DBUG_RETURN(0); memcpy(new_ptr, array->buffer, array->elements * array->size_of_element); - } + array->malloc_flags&= ~MY_INIT_BUFFER_USED; + } else if (!(new_ptr= (uchar*) my_realloc(array->buffer,size* array->size_of_element, MYF(MY_WME | MY_ALLOW_ZERO_PTR | @@ -293,15 +298,11 @@ void delete_dynamic(DYNAMIC_ARRAY *array) /* Just mark as empty if we are using a static buffer */ - if (array->buffer == (uchar *)(array + 1)) - array->elements= 0; - else - if (array->buffer) - { + if (!(array->malloc_flags & MY_INIT_BUFFER_USED) && array->buffer) my_free(array->buffer); - array->buffer=0; - array->elements=array->max_element=0; - } + + array->buffer= 0; + array->elements= array->max_element= 0; } /* @@ -350,24 +351,25 @@ void delete_dynamic_with_callback(DYNAMIC_ARRAY *array, FREE_FUNC f) { void freeze_size(DYNAMIC_ARRAY *array) { - uint elements=MY_MAX(array->elements,1); + uint elements; /* Do nothing if we are using a static buffer */ - if (array->buffer == (uchar *)(array + 1)) + if (array->malloc_flags & MY_INIT_BUFFER_USED) return; - if (array->buffer && array->max_element != elements) + elements= MY_MAX(array->elements, 1); + if (array->buffer && array->max_element > elements) { array->buffer=(uchar*) my_realloc(array->buffer, - elements*array->size_of_element, + elements*array->size_of_element, MYF(MY_WME | array->malloc_flags)); - array->max_element=elements; + array->max_element= elements; } } - +#ifdef NOT_USED /* Get the index of a dynamic element @@ -391,3 +393,4 @@ int get_index_dynamic(DYNAMIC_ARRAY *array, void* element) return ret; } +#endif diff --git a/mysys/charset-def.c b/mysys/charset-def.c index af1e9bae2ac..defb5c0062d 100644 --- a/mysys/charset-def.c +++ b/mysys/charset-def.c @@ -49,6 +49,7 @@ extern struct charset_info_st my_charset_ucs2_unicode_520_ci; extern struct charset_info_st my_charset_ucs2_vietnamese_ci; extern struct charset_info_st my_charset_ucs2_croatian_uca_ci; extern struct charset_info_st my_charset_ucs2_myanmar_uca_ci; +extern struct charset_info_st my_charset_ucs2_thai_520_w2; #endif @@ -78,6 +79,7 @@ extern struct charset_info_st my_charset_utf32_unicode_520_ci; extern struct charset_info_st my_charset_utf32_vietnamese_ci; extern struct charset_info_st my_charset_utf32_croatian_uca_ci; extern struct charset_info_st my_charset_utf32_myanmar_uca_ci; +extern struct charset_info_st my_charset_utf32_thai_520_w2; #endif /* HAVE_CHARSET_utf32 */ @@ -107,6 +109,7 @@ extern struct charset_info_st my_charset_utf16_unicode_520_ci; extern struct charset_info_st my_charset_utf16_vietnamese_ci; extern struct charset_info_st my_charset_utf16_croatian_uca_ci; extern struct charset_info_st my_charset_utf16_myanmar_uca_ci; +extern struct charset_info_st my_charset_utf16_thai_520_w2; #endif /* HAVE_CHARSET_utf16 */ @@ -136,6 +139,7 @@ extern struct charset_info_st my_charset_utf8_unicode_520_ci; extern struct charset_info_st my_charset_utf8_vietnamese_ci; extern struct charset_info_st my_charset_utf8_croatian_uca_ci; extern struct charset_info_st my_charset_utf8_myanmar_uca_ci; +extern struct charset_info_st my_charset_utf8_thai_520_w2; #ifdef HAVE_UTF8_GENERAL_CS extern struct charset_info_st my_charset_utf8_general_cs; #endif @@ -167,6 +171,7 @@ extern struct charset_info_st my_charset_utf8mb4_unicode_520_ci; extern struct charset_info_st my_charset_utf8mb4_vietnamese_ci; extern struct charset_info_st my_charset_utf8mb4_croatian_uca_ci; extern struct charset_info_st my_charset_utf8mb4_myanmar_uca_ci; +extern struct charset_info_st my_charset_utf8mb4_thai_520_w2; #endif /* HAVE_CHARSET_utf8mb4 */ #endif /* HAVE_UCA_COLLATIONS */ @@ -261,6 +266,7 @@ my_bool init_compiled_charsets(myf flags __attribute__((unused))) add_compiled_collation(&my_charset_ucs2_vietnamese_ci); add_compiled_collation(&my_charset_ucs2_croatian_uca_ci); add_compiled_collation(&my_charset_ucs2_myanmar_uca_ci); + add_compiled_collation(&my_charset_ucs2_thai_520_w2); #endif #endif @@ -303,6 +309,7 @@ my_bool init_compiled_charsets(myf flags __attribute__((unused))) add_compiled_collation(&my_charset_utf8_vietnamese_ci); add_compiled_collation(&my_charset_utf8_croatian_uca_ci); add_compiled_collation(&my_charset_utf8_myanmar_uca_ci); + add_compiled_collation(&my_charset_utf8_thai_520_w2); #endif #endif /* HAVE_CHARSET_utf8 */ @@ -337,6 +344,7 @@ my_bool init_compiled_charsets(myf flags __attribute__((unused))) add_compiled_collation(&my_charset_utf8mb4_vietnamese_ci); add_compiled_collation(&my_charset_utf8mb4_croatian_uca_ci); add_compiled_collation(&my_charset_utf8mb4_myanmar_uca_ci); + add_compiled_collation(&my_charset_utf8mb4_thai_520_w2); #endif /* HAVE_UCA_COLLATIONS */ #endif /* HAVE_CHARSET_utf8mb4 */ @@ -373,6 +381,7 @@ my_bool init_compiled_charsets(myf flags __attribute__((unused))) add_compiled_collation(&my_charset_utf16_vietnamese_ci); add_compiled_collation(&my_charset_utf16_croatian_uca_ci); add_compiled_collation(&my_charset_utf16_myanmar_uca_ci); + add_compiled_collation(&my_charset_utf16_thai_520_w2); #endif /* HAVE_UCA_COLLATIONS */ #endif /* HAVE_CHARSET_utf16 */ @@ -407,6 +416,7 @@ my_bool init_compiled_charsets(myf flags __attribute__((unused))) add_compiled_collation(&my_charset_utf32_vietnamese_ci); add_compiled_collation(&my_charset_utf32_croatian_uca_ci); add_compiled_collation(&my_charset_utf32_myanmar_uca_ci); + add_compiled_collation(&my_charset_utf32_thai_520_w2); #endif /* HAVE_UCA_COLLATIONS */ #endif /* HAVE_CHARSET_utf32 */ diff --git a/mysys/checksum.c b/mysys/checksum.c index 7bc52c6a178..a948785d935 100644 --- a/mysys/checksum.c +++ b/mysys/checksum.c @@ -18,8 +18,6 @@ #include <my_sys.h> #include <zlib.h> -ulong my_crc_dbug_check= ~0; /* Cannot happen */ - /* Calculate a long checksum for a memoryblock. @@ -34,7 +32,5 @@ ha_checksum my_checksum(ha_checksum crc, const uchar *pos, size_t length) { crc= (ha_checksum) crc32((uint)crc, pos, (uint) length); DBUG_PRINT("info", ("crc: %lu", (ulong) crc)); - if ((ulong)crc == my_crc_dbug_check) - my_debug_put_break_here(); return crc; } diff --git a/mysys/hash.c b/mysys/hash.c index 344b698a433..dc03ea9a4dc 100644 --- a/mysys/hash.c +++ b/mysys/hash.c @@ -94,10 +94,9 @@ my_hash_init2(HASH *hash, uint growth_size, CHARSET_INFO *charset, hash->free=free_element; hash->flags=flags; hash->charset=charset; - res= my_init_dynamic_array2(&hash->array, - sizeof(HASH_LINK), NULL, size, growth_size, - MYF((flags & HASH_THREAD_SPECIFIC ? - MY_THREAD_SPECIFIC : 0))); + res= init_dynamic_array2(&hash->array, sizeof(HASH_LINK), NULL, size, + growth_size, MYF((flags & HASH_THREAD_SPECIFIC ? + MY_THREAD_SPECIFIC : 0))); DBUG_RETURN(res); } diff --git a/mysys/lf_alloc-pin.c b/mysys/lf_alloc-pin.c index 282433ea48d..b349ef88ed1 100644 --- a/mysys/lf_alloc-pin.c +++ b/mysys/lf_alloc-pin.c @@ -111,7 +111,7 @@ #define LF_PINBOX_MAX_PINS 65536 -static void _lf_pinbox_real_free(LF_PINS *pins); +static void lf_pinbox_real_free(LF_PINS *pins); /* Initialize a pinbox. Normally called from lf_alloc_init. @@ -150,7 +150,7 @@ void lf_pinbox_destroy(LF_PINBOX *pinbox) It is assumed that pins belong to a thread and are not transferable between threads. */ -LF_PINS *_lf_pinbox_get_pins(LF_PINBOX *pinbox) +LF_PINS *lf_pinbox_get_pins(LF_PINBOX *pinbox) { struct st_my_thread_var *var; uint32 pins, next, top_ver; @@ -177,12 +177,12 @@ LF_PINS *_lf_pinbox_get_pins(LF_PINBOX *pinbox) note that the first allocated element has index 1 (pins==1). index 0 is reserved to mean "NULL pointer" */ - el= (LF_PINS *)_lf_dynarray_lvalue(&pinbox->pinarray, pins); + el= (LF_PINS *)lf_dynarray_lvalue(&pinbox->pinarray, pins); if (unlikely(!el)) return 0; break; } - el= (LF_PINS *)_lf_dynarray_value(&pinbox->pinarray, pins); + el= (LF_PINS *)lf_dynarray_value(&pinbox->pinarray, pins); next= el->link; } while (!my_atomic_cas32((int32 volatile*) &pinbox->pinstack_top_ver, (int32*) &top_ver, @@ -212,7 +212,7 @@ LF_PINS *_lf_pinbox_get_pins(LF_PINBOX *pinbox) empty the purgatory (XXX deadlock warning below!), push LF_PINS structure to a stack */ -void _lf_pinbox_put_pins(LF_PINS *pins) +void lf_pinbox_put_pins(LF_PINS *pins) { LF_PINBOX *pinbox= pins->pinbox; uint32 top_ver, nr; @@ -229,19 +229,15 @@ void _lf_pinbox_put_pins(LF_PINS *pins) /* XXX this will deadlock if other threads will wait for - the caller to do something after _lf_pinbox_put_pins(), + the caller to do something after lf_pinbox_put_pins(), and they would have pinned addresses that the caller wants to free. Thus: only free pins when all work is done and nobody can wait for you!!! */ while (pins->purgatory_count) { - _lf_pinbox_real_free(pins); + lf_pinbox_real_free(pins); if (pins->purgatory_count) - { - my_atomic_rwlock_wrunlock(&pins->pinbox->pinarray.lock); pthread_yield(); - my_atomic_rwlock_wrlock(&pins->pinbox->pinarray.lock); - } } top_ver= pinbox->pinstack_top_ver; do @@ -271,14 +267,14 @@ static int ptr_cmp(void **a, void **b) Free an object allocated via pinbox allocator DESCRIPTION - add an object to purgatory. if necessary, call _lf_pinbox_real_free() + add an object to purgatory. if necessary, calllf_pinbox_real_free() to actually free something. */ -void _lf_pinbox_free(LF_PINS *pins, void *addr) +void lf_pinbox_free(LF_PINS *pins, void *addr) { add_to_purgatory(pins, addr); if (pins->purgatory_count % LF_PURGATORY_SIZE == 0) - _lf_pinbox_real_free(pins); + lf_pinbox_real_free(pins); } struct st_harvester { @@ -287,7 +283,7 @@ struct st_harvester { }; /* - callback for _lf_dynarray_iterate: + callback forlf_dynarray_iterate: scan all pins of all threads and accumulate all pins */ static int harvest_pins(LF_PINS *el, struct st_harvester *hv) @@ -314,7 +310,7 @@ static int harvest_pins(LF_PINS *el, struct st_harvester *hv) } /* - callback for _lf_dynarray_iterate: + callback forlf_dynarray_iterate: scan all pins of all threads and see if addr is present there */ static int match_pins(LF_PINS *el, void *addr) @@ -340,7 +336,7 @@ static int match_pins(LF_PINS *el, void *addr) /* Scan the purgatory and free everything that can be freed */ -static void _lf_pinbox_real_free(LF_PINS *pins) +static void lf_pinbox_real_free(LF_PINS *pins) { int npins; void *list; @@ -363,7 +359,7 @@ static void _lf_pinbox_real_free(LF_PINS *pins) hv.granary= addr; hv.npins= npins; /* scan the dynarray and accumulate all pinned addresses */ - _lf_dynarray_iterate(&pinbox->pinarray, + lf_dynarray_iterate(&pinbox->pinarray, (lf_dynarray_func)harvest_pins, &hv); npins= hv.granary-addr; @@ -398,7 +394,7 @@ static void _lf_pinbox_real_free(LF_PINS *pins) } else /* no alloca - no cookie. linear search here */ { - if (_lf_dynarray_iterate(&pinbox->pinarray, + if (lf_dynarray_iterate(&pinbox->pinarray, (lf_dynarray_func)match_pins, cur)) goto found; } @@ -420,7 +416,7 @@ found: /* lock-free memory allocator for fixed-size objects */ /* - callback for _lf_pinbox_real_free to free a list of unpinned objects - + callback forlf_pinbox_real_free to free a list of unpinned objects - add it back to the allocator stack DESCRIPTION @@ -502,7 +498,7 @@ void lf_alloc_destroy(LF_ALLOCATOR *allocator) Pop an unused object from the stack or malloc it is the stack is empty. pin[0] is used, it's removed on return. */ -void *_lf_alloc_new(LF_PINS *pins) +void *lf_alloc_new(LF_PINS *pins) { LF_ALLOCATOR *allocator= (LF_ALLOCATOR *)(pins->pinbox->free_func_arg); uchar *node; @@ -511,7 +507,7 @@ void *_lf_alloc_new(LF_PINS *pins) do { node= allocator->top; - _lf_pin(pins, 0, node); + lf_pin(pins, 0, node); } while (node != allocator->top && LF_BACKOFF); if (!node) { @@ -528,7 +524,7 @@ void *_lf_alloc_new(LF_PINS *pins) (void *)&node, anext_node(node))) break; } - _lf_unpin(pins, 0); + lf_unpin(pins, 0); return node; } diff --git a/mysys/lf_dynarray.c b/mysys/lf_dynarray.c index 16a77c0fa1a..bb6cbcefc49 100644 --- a/mysys/lf_dynarray.c +++ b/mysys/lf_dynarray.c @@ -44,7 +44,6 @@ void lf_dynarray_init(LF_DYNARRAY *array, uint element_size) { bzero(array, sizeof(*array)); array->size_of_element= element_size; - my_atomic_rwlock_init(&array->lock); } static void recursive_free(void **alloc, int level) @@ -68,7 +67,6 @@ void lf_dynarray_destroy(LF_DYNARRAY *array) int i; for (i= 0; i < LF_DYNARRAY_LEVELS; i++) recursive_free(array->level[i], i); - my_atomic_rwlock_destroy(&array->lock); } static const ulong dynarray_idxes_in_prev_levels[LF_DYNARRAY_LEVELS]= @@ -95,7 +93,7 @@ static const ulong dynarray_idxes_in_prev_level[LF_DYNARRAY_LEVELS]= Returns a valid lvalue pointer to the element number 'idx'. Allocates memory if necessary. */ -void *_lf_dynarray_lvalue(LF_DYNARRAY *array, uint idx) +void *lf_dynarray_lvalue(LF_DYNARRAY *array, uint idx) { void * ptr, * volatile * ptr_ptr= 0; int i; @@ -148,7 +146,7 @@ void *_lf_dynarray_lvalue(LF_DYNARRAY *array, uint idx) Returns a pointer to the element number 'idx' or NULL if an element does not exists */ -void *_lf_dynarray_value(LF_DYNARRAY *array, uint idx) +void *lf_dynarray_value(LF_DYNARRAY *array, uint idx) { void * ptr, * volatile * ptr_ptr= 0; int i; @@ -189,14 +187,14 @@ static int recursive_iterate(LF_DYNARRAY *array, void *ptr, int level, DESCRIPTION lf_dynarray consists of a set of arrays, LF_DYNARRAY_LEVEL_LENGTH elements - each. _lf_dynarray_iterate() calls user-supplied function on every array + each. lf_dynarray_iterate() calls user-supplied function on every array from the set. It is the fastest way to scan the array, faster than - for (i=0; i < N; i++) { func(_lf_dynarray_value(dynarray, i)); } + for (i=0; i < N; i++) { func(lf_dynarray_value(dynarray, i)); } NOTE if func() returns non-zero, the scan is aborted */ -int _lf_dynarray_iterate(LF_DYNARRAY *array, lf_dynarray_func func, void *arg) +int lf_dynarray_iterate(LF_DYNARRAY *array, lf_dynarray_func func, void *arg) { int i, res; for (i= 0; i < LF_DYNARRAY_LEVELS; i++) diff --git a/mysys/lf_hash.c b/mysys/lf_hash.c index 0699f5d49fe..6be11edbfcf 100644 --- a/mysys/lf_hash.c +++ b/mysys/lf_hash.c @@ -25,12 +25,13 @@ #include <my_global.h> #include <m_string.h> #include <my_sys.h> +#include <mysys_err.h> #include <my_bit.h> #include <lf.h> /* An element of the list */ typedef struct { - intptr volatile link; /* a pointer to the next element in a listand a flag */ + intptr volatile link; /* a pointer to the next element in a list and a flag */ uint32 hashnr; /* reversed hash number, for sorting */ const uchar *key; size_t keylen; @@ -58,63 +59,84 @@ typedef struct { #define PTR(V) (LF_SLIST *)((V) & (~(intptr)1)) #define DELETED(V) ((V) & 1) -/* - DESCRIPTION +/** walk the list, searching for an element or invoking a callback + Search for hashnr/key/keylen in the list starting from 'head' and position the cursor. The list is ORDER BY hashnr, key - RETURN - 0 - not found - 1 - found + @param head start walking the list from this node + @param cs charset for comparing keys, NULL if callback is used + @param hashnr hash number to search for + @param key key to search for OR data for the callback + @param keylen length of the key to compare, 0 if callback is used + @param cursor for returning the found element + @param pins see lf_alloc-pin.c + @param callback callback action, invoked for every element - NOTE + @note cursor is positioned in either case pins[0..2] are used, they are NOT removed on return + callback might see some elements twice (because of retries) + + @return + if find: 0 - not found + 1 - found + if callback: + 0 - ok + 1 - error (callbck returned 1) */ static int l_find(LF_SLIST * volatile *head, CHARSET_INFO *cs, uint32 hashnr, - const uchar *key, uint keylen, CURSOR *cursor, LF_PINS *pins) + const uchar *key, uint keylen, CURSOR *cursor, LF_PINS *pins, + my_hash_walk_action callback) { uint32 cur_hashnr; const uchar *cur_key; uint cur_keylen; intptr link; + DBUG_ASSERT(!cs || !callback); /* should not be set both */ + DBUG_ASSERT(!keylen || !callback); /* should not be set both */ + retry: cursor->prev= (intptr *)head; do { /* PTR() isn't necessary below, head is a dummy node */ cursor->curr= (LF_SLIST *)(*cursor->prev); - _lf_pin(pins, 1, cursor->curr); + lf_pin(pins, 1, cursor->curr); } while (*cursor->prev != (intptr)cursor->curr && LF_BACKOFF); + for (;;) { if (unlikely(!cursor->curr)) return 0; /* end of the list */ + + cur_hashnr= cursor->curr->hashnr; + cur_keylen= cursor->curr->keylen; + cur_key= cursor->curr->key; + do { - /* QQ: XXX or goto retry ? */ link= cursor->curr->link; cursor->next= PTR(link); - _lf_pin(pins, 0, cursor->next); + lf_pin(pins, 0, cursor->next); } while (link != cursor->curr->link && LF_BACKOFF); - cur_hashnr= cursor->curr->hashnr; - cur_key= cursor->curr->key; - cur_keylen= cursor->curr->keylen; - if (*cursor->prev != (intptr)cursor->curr) - { - (void)LF_BACKOFF; - goto retry; - } + if (!DELETED(link)) { - if (cur_hashnr >= hashnr) + if (unlikely(callback)) + { + if (cur_hashnr & 1 && callback(cursor->curr + 1, (void*)key)) + return 1; + } + else if (cur_hashnr >= hashnr) { int r= 1; if (cur_hashnr > hashnr || - (r= my_strnncoll(cs, (uchar*) cur_key, cur_keylen, (uchar*) key, - keylen)) >= 0) + (r= my_strnncoll(cs, cur_key, cur_keylen, key, keylen)) >= 0) return !r; } cursor->prev= &(cursor->curr->link); - _lf_pin(pins, 2, cursor->curr); + if (!(cur_hashnr & 1)) /* dummy node */ + head= (LF_SLIST **)cursor->prev; + lf_pin(pins, 2, cursor->curr); } else { @@ -123,16 +145,13 @@ retry: and remove this deleted node */ if (my_atomic_casptr((void **) cursor->prev, - (void **)(char*) &cursor->curr, cursor->next)) - _lf_alloc_free(pins, cursor->curr); + (void **) &cursor->curr, cursor->next) && LF_BACKOFF) + lf_alloc_free(pins, cursor->curr); else - { - (void)LF_BACKOFF; goto retry; - } } cursor->curr= cursor->next; - _lf_pin(pins, 1, cursor->curr); + lf_pin(pins, 1, cursor->curr); } } @@ -158,7 +177,7 @@ static LF_SLIST *l_insert(LF_SLIST * volatile *head, CHARSET_INFO *cs, for (;;) { if (l_find(head, cs, node->hashnr, node->key, node->keylen, - &cursor, pins) && + &cursor, pins, 0) && (flags & LF_HASH_UNIQUE)) { res= 0; /* duplicate found */ @@ -177,9 +196,9 @@ static LF_SLIST *l_insert(LF_SLIST * volatile *head, CHARSET_INFO *cs, } } } - _lf_unpin(pins, 0); - _lf_unpin(pins, 1); - _lf_unpin(pins, 2); + lf_unpin(pins, 0); + lf_unpin(pins, 1); + lf_unpin(pins, 2); /* Note that cursor.curr is not pinned here and the pointer is unreliable, the object may dissapear anytime. But if it points to a dummy node, the @@ -209,7 +228,7 @@ static int l_delete(LF_SLIST * volatile *head, CHARSET_INFO *cs, uint32 hashnr, for (;;) { - if (!l_find(head, cs, hashnr, key, keylen, &cursor, pins)) + if (!l_find(head, cs, hashnr, key, keylen, &cursor, pins, 0)) { res= 1; /* not found */ break; @@ -224,7 +243,7 @@ static int l_delete(LF_SLIST * volatile *head, CHARSET_INFO *cs, uint32 hashnr, /* and remove it from the list */ if (my_atomic_casptr((void **)cursor.prev, (void **)(char*)&cursor.curr, cursor.next)) - _lf_alloc_free(pins, cursor.curr); + lf_alloc_free(pins, cursor.curr); else { /* @@ -233,16 +252,16 @@ static int l_delete(LF_SLIST * volatile *head, CHARSET_INFO *cs, uint32 hashnr, (to ensure the number of "set DELETED flag" actions is equal to the number of "remove from the list" actions) */ - l_find(head, cs, hashnr, key, keylen, &cursor, pins); + l_find(head, cs, hashnr, key, keylen, &cursor, pins, 0); } res= 0; break; } } } - _lf_unpin(pins, 0); - _lf_unpin(pins, 1); - _lf_unpin(pins, 2); + lf_unpin(pins, 0); + lf_unpin(pins, 1); + lf_unpin(pins, 2); return res; } @@ -264,13 +283,13 @@ static LF_SLIST *l_search(LF_SLIST * volatile *head, CHARSET_INFO *cs, LF_PINS *pins) { CURSOR cursor; - int res= l_find(head, cs, hashnr, key, keylen, &cursor, pins); + int res= l_find(head, cs, hashnr, key, keylen, &cursor, pins, 0); if (res) - _lf_pin(pins, 2, cursor.curr); + lf_pin(pins, 2, cursor.curr); else - _lf_unpin(pins, 2); - _lf_unpin(pins, 1); - _lf_unpin(pins, 0); + lf_unpin(pins, 2); + lf_unpin(pins, 1); + lf_unpin(pins, 0); return res ? cursor.curr : 0; } @@ -289,18 +308,24 @@ static inline const uchar* hash_key(const LF_HASH *hash, @note, that the hash value is limited to 2^31, because we need one bit to distinguish between normal and dummy nodes. */ -static inline uint calc_hash(LF_HASH *hash, const uchar *key, uint keylen) +static inline my_hash_value_type calc_hash(const CHARSET_INFO *cs, + const uchar *key, + size_t keylen) { ulong nr1= 1, nr2= 4; - hash->charset->coll->hash_sort(hash->charset, (uchar*) key, keylen, - &nr1, &nr2); - return nr1 & INT_MAX32; + cs->coll->hash_sort(cs, (uchar*) key, keylen, &nr1, &nr2); + return nr1; } #define MAX_LOAD 1.0 /* average number of elements in a bucket */ static int initialize_bucket(LF_HASH *, LF_SLIST * volatile*, uint, LF_PINS *); +static void default_initializer(LF_HASH *hash, void *dst, const void *src) +{ + memcpy(dst, src, hash->element_size); +} + /* Initializes lf_hash, the arguments are compatible with hash_init @@ -312,7 +337,9 @@ static int initialize_bucket(LF_HASH *, LF_SLIST * volatile*, uint, LF_PINS *); is expensive to initialize - for example if there is a mutex or DYNAMIC_ARRAY. In this case they should be initialize in the LF_ALLOCATOR::constructor, and lf_hash_insert should not overwrite them. - See wt_init() for example. + + The above works well with PODS. For more complex cases (e.g. C++ classes + with private members) use initializer function. */ void lf_hash_init(LF_HASH *hash, uint element_size, uint flags, uint key_offset, uint key_length, my_hash_get_key get_key, @@ -329,12 +356,14 @@ void lf_hash_init(LF_HASH *hash, uint element_size, uint flags, hash->key_offset= key_offset; hash->key_length= key_length; hash->get_key= get_key; + hash->initializer= default_initializer; + hash->hash_function= calc_hash; DBUG_ASSERT(get_key ? !key_offset && !key_length : key_length); } void lf_hash_destroy(LF_HASH *hash) { - LF_SLIST *el, **head= (LF_SLIST **)_lf_dynarray_value(&hash->array, 0); + LF_SLIST *el, **head= (LF_SLIST **)lf_dynarray_value(&hash->array, 0); if (head) { @@ -371,15 +400,14 @@ int lf_hash_insert(LF_HASH *hash, LF_PINS *pins, const void *data) int csize, bucket, hashnr; LF_SLIST *node, * volatile *el; - lf_rwlock_by_pins(pins); - node= (LF_SLIST *)_lf_alloc_new(pins); + node= (LF_SLIST *)lf_alloc_new(pins); if (unlikely(!node)) return -1; - memcpy(node+1, data, hash->element_size); + hash->initializer(hash, node + 1, data); node->key= hash_key(hash, (uchar *)(node+1), &node->keylen); - hashnr= calc_hash(hash, node->key, node->keylen); + hashnr= hash->hash_function(hash->charset, node->key, node->keylen) & INT_MAX32; bucket= hashnr % hash->size; - el= _lf_dynarray_lvalue(&hash->array, bucket); + el= lf_dynarray_lvalue(&hash->array, bucket); if (unlikely(!el)) return -1; if (*el == NULL && unlikely(initialize_bucket(hash, el, bucket, pins))) @@ -387,14 +415,12 @@ int lf_hash_insert(LF_HASH *hash, LF_PINS *pins, const void *data) node->hashnr= my_reverse_bits(hashnr) | 1; /* normal node */ if (l_insert(el, hash->charset, node, pins, hash->flags)) { - _lf_alloc_free(pins, node); - lf_rwunlock_by_pins(pins); + lf_alloc_free(pins, node); return 1; } csize= hash->size; if ((my_atomic_add32(&hash->count, 1)+1.0) / csize > MAX_LOAD) my_atomic_cas32(&hash->size, &csize, csize*2); - lf_rwunlock_by_pins(pins); return 0; } @@ -406,36 +432,31 @@ int lf_hash_insert(LF_HASH *hash, LF_PINS *pins, const void *data) RETURN 0 - deleted 1 - didn't (not found) - -1 - out of memory NOTE see l_delete() for pin usage notes */ int lf_hash_delete(LF_HASH *hash, LF_PINS *pins, const void *key, uint keylen) { LF_SLIST * volatile *el; - uint bucket, hashnr= calc_hash(hash, (uchar *)key, keylen); + uint bucket, hashnr; - bucket= hashnr % hash->size; - lf_rwlock_by_pins(pins); - el= _lf_dynarray_lvalue(&hash->array, bucket); - if (unlikely(!el)) - return -1; - /* - note that we still need to initialize_bucket here, - we cannot return "node not found", because an old bucket of that - node may've been split and the node was assigned to a new bucket - that was never accessed before and thus is not initialized. - */ - if (*el == NULL && unlikely(initialize_bucket(hash, el, bucket, pins))) - return -1; + hashnr= hash->hash_function(hash->charset, (uchar *)key, keylen) & INT_MAX32; + + /* hide OOM errors - if we cannot initalize a bucket, try the previous one */ + for (bucket= hashnr % hash->size; ;bucket= my_clear_highest_bit(bucket)) + { + el= lf_dynarray_lvalue(&hash->array, bucket); + if (el && (*el || initialize_bucket(hash, el, bucket, pins) == 0)) + break; + if (unlikely(bucket == 0)) + return 1; /* if there's no bucket==0, the hash is empty */ + } if (l_delete(el, hash->charset, my_reverse_bits(hashnr) | 1, (uchar *)key, keylen, pins)) { - lf_rwunlock_by_pins(pins); return 1; } my_atomic_add32(&hash->count, -1); - lf_rwunlock_by_pins(pins); return 0; } @@ -444,29 +465,73 @@ int lf_hash_delete(LF_HASH *hash, LF_PINS *pins, const void *key, uint keylen) a pointer to an element with the given key (if a hash is not unique and there're many elements with this key - the "first" matching element) NULL if nothing is found - MY_ERRPTR if OOM NOTE see l_search() for pin usage notes */ -void *lf_hash_search(LF_HASH *hash, LF_PINS *pins, const void *key, uint keylen) +void *lf_hash_search_using_hash_value(LF_HASH *hash, LF_PINS *pins, + my_hash_value_type hashnr, + const void *key, uint keylen) { LF_SLIST * volatile *el, *found; - uint bucket, hashnr= calc_hash(hash, (uchar *)key, keylen); + uint bucket; - bucket= hashnr % hash->size; - lf_rwlock_by_pins(pins); - el= _lf_dynarray_lvalue(&hash->array, bucket); - if (unlikely(!el)) - return MY_ERRPTR; - if (*el == NULL && unlikely(initialize_bucket(hash, el, bucket, pins))) - return MY_ERRPTR; + /* hide OOM errors - if we cannot initalize a bucket, try the previous one */ + for (bucket= hashnr % hash->size; ;bucket= my_clear_highest_bit(bucket)) + { + el= lf_dynarray_lvalue(&hash->array, bucket); + if (el && (*el || initialize_bucket(hash, el, bucket, pins) == 0)) + break; + if (unlikely(bucket == 0)) + return 0; /* if there's no bucket==0, the hash is empty */ + } found= l_search(el, hash->charset, my_reverse_bits(hashnr) | 1, (uchar *)key, keylen, pins); - lf_rwunlock_by_pins(pins); return found ? found+1 : 0; } + +/** + Iterate over all elements in hash and call function with the element + + @note + If one of 'action' invocations returns 1 the iteration aborts. + 'action' might see some elements twice! + + @retval 0 ok + @retval 1 error (action returned 1) +*/ +int lf_hash_iterate(LF_HASH *hash, LF_PINS *pins, + my_hash_walk_action action, void *argument) +{ + CURSOR cursor; + uint bucket= 0; + int res; + LF_SLIST * volatile *el; + + el= lf_dynarray_lvalue(&hash->array, bucket); + if (unlikely(!el)) + return 0; /* if there's no bucket==0, the hash is empty */ + if (*el == NULL && unlikely(initialize_bucket(hash, el, bucket, pins))) + return 0; /* if there's no bucket==0, the hash is empty */ + + res= l_find(el, 0, 0, (uchar*)argument, 0, &cursor, pins, action); + + lf_unpin(pins, 2); + lf_unpin(pins, 1); + lf_unpin(pins, 0); + return res; +} + +void *lf_hash_search(LF_HASH *hash, LF_PINS *pins, const void *key, uint keylen) +{ + return lf_hash_search_using_hash_value(hash, pins, + hash->hash_function(hash->charset, + (uchar*) key, + keylen) & INT_MAX32, + key, keylen); +} + static const uchar *dummy_key= (uchar*)""; /* @@ -480,7 +545,7 @@ static int initialize_bucket(LF_HASH *hash, LF_SLIST * volatile *node, uint parent= my_clear_highest_bit(bucket); LF_SLIST *dummy= (LF_SLIST *)my_malloc(sizeof(LF_SLIST), MYF(MY_WME)); LF_SLIST **tmp= 0, *cur; - LF_SLIST * volatile *el= _lf_dynarray_lvalue(&hash->array, parent); + LF_SLIST * volatile *el= lf_dynarray_lvalue(&hash->array, parent); if (unlikely(!el || !dummy)) return -1; if (*el == NULL && bucket && diff --git a/mysys/ma_dyncol.c b/mysys/ma_dyncol.c index 7cd0c2b02df..92418e38c2e 100644 --- a/mysys/ma_dyncol.c +++ b/mysys/ma_dyncol.c @@ -2578,7 +2578,6 @@ find_place(DYN_HEADER *hdr, void *key, my_bool string_keys) mid= 1; while (start != end) { - uint val; mid= (start + end) / 2; hdr->entry= hdr->header + mid * hdr->entry_size; if (!string_keys) @@ -3895,11 +3894,11 @@ mariadb_dyncol_val_str(DYNAMIC_STRING *str, DYNAMIC_COLUMN_VALUE *val, } case DYN_COL_DECIMAL: { - int len= sizeof(buff); - decimal2string(&val->x.decimal.value, buff, &len, + int tmp_len= sizeof(buff); + decimal2string(&val->x.decimal.value, buff, &tmp_len, 0, val->x.decimal.value.frac, '0'); - if (dynstr_append_mem(str, buff, len)) + if (dynstr_append_mem(str, buff, tmp_len)) return ER_DYNCOL_RESOURCE; break; } diff --git a/mysys/mf_cache.c b/mysys/mf_cache.c index 545084ea97f..a3426889a82 100644 --- a/mysys/mf_cache.c +++ b/mysys/mf_cache.c @@ -20,11 +20,12 @@ #include "my_static.h" #include "mysys_err.h" - /* - Remove an open tempfile so that it doesn't survive - if we crash; If the operating system doesn't support - this, just remember the file name for later removal - */ +/** + Remove an open tempfile so that it doesn't survive if we crash + + If the operating system doesn't support this, just remember + the file name for later removal +*/ static my_bool cache_remove_open_tmp(IO_CACHE *cache __attribute__((unused)), const char *name) @@ -49,41 +50,46 @@ static my_bool cache_remove_open_tmp(IO_CACHE *cache __attribute__((unused)), return 0; } - /* - ** Open tempfile cached by IO_CACHE - ** Should be used when no seeks are done (only reinit_io_buff) - ** Return 0 if cache is inited ok - ** The actual file is created when the IO_CACHE buffer gets filled - ** If dir is not given, use TMPDIR. - */ +/** + Open tempfile cached by IO_CACHE + Should be used when no seeks are done (only reinit_io_buff) + Return 0 if cache is inited ok + The actual file is created when the IO_CACHE buffer gets filled + If dir is not given, use TMPDIR. +*/ my_bool open_cached_file(IO_CACHE *cache, const char* dir, const char *prefix, size_t cache_size, myf cache_myflags) { DBUG_ENTER("open_cached_file"); - cache->dir= dir ? my_strdup(dir,MYF(cache_myflags & MY_WME)) : (char*) 0; - cache->prefix= (prefix ? my_strdup(prefix,MYF(cache_myflags & MY_WME)) : - (char*) 0); + cache->dir= dir; + if (prefix) + { + DBUG_ASSERT(strlen(prefix) == 2); + memcpy(cache->prefix, prefix, 3); + } + else + cache->prefix[0]= 0; cache->file_name=0; cache->buffer=0; /* Mark that not open */ - if (!init_io_cache(cache,-1,cache_size,WRITE_CACHE,0L,0, + if (!init_io_cache(cache, -1, cache_size, WRITE_CACHE, 0L, 0, MYF(cache_myflags | MY_NABP))) { DBUG_RETURN(0); } - my_free(cache->dir); - my_free(cache->prefix); DBUG_RETURN(1); } - /* Create the temporary file */ - +/** + Create the temporary file +*/ my_bool real_open_cached_file(IO_CACHE *cache) { char name_buff[FN_REFLEN]; int error=1; DBUG_ENTER("real_open_cached_file"); - if ((cache->file=create_temp_file(name_buff, cache->dir, cache->prefix, + if ((cache->file=create_temp_file(name_buff, cache->dir, + cache->prefix[0] ? cache->prefix : 0, (O_RDWR | O_BINARY | O_TRUNC | O_TEMPORARY | O_SHORT_LIVED), MYF(MY_WME))) >= 0) @@ -114,8 +120,6 @@ void close_cached_file(IO_CACHE *cache) } #endif } - my_free(cache->dir); - my_free(cache->prefix); } DBUG_VOID_RETURN; } diff --git a/mysys/mf_iocache.c b/mysys/mf_iocache.c index f891a22b17d..4afe74da077 100644 --- a/mysys/mf_iocache.c +++ b/mysys/mf_iocache.c @@ -1,5 +1,6 @@ /* Copyright (c) 2000, 2011, Oracle and/or its affiliates + Copyright (c) 2010, 2015, MariaDB 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 @@ -22,7 +23,6 @@ Possibly use of asyncronic io. macros for read and writes for faster io. Used instead of FILE when reading or writing whole files. - This code makes mf_rec_cache obsolete (currently only used by ISAM) One can change info->pos_in_file to a higher value to skip bytes in file if also info->read_pos is set to info->read_end. If called through open_cached_file(), then the temporary file will @@ -64,6 +64,16 @@ static void my_aiowait(my_aio_result *result); #define IO_ROUND_UP(X) (((X)+IO_SIZE-1) & ~(IO_SIZE-1)) #define IO_ROUND_DN(X) ( (X) & ~(IO_SIZE-1)) +static int _my_b_cache_read(IO_CACHE *info, uchar *Buffer, size_t Count); +static int _my_b_cache_read_r(IO_CACHE *info, uchar *Buffer, size_t Count); +static int _my_b_seq_read(IO_CACHE *info, uchar *Buffer, size_t Count); +static int _my_b_cache_write(IO_CACHE *info, const uchar *Buffer, size_t Count); +static int _my_b_cache_write_r(IO_CACHE *info, const uchar *Buffer, size_t Count); + +int (*_my_b_encr_read)(IO_CACHE *info,uchar *Buffer,size_t Count)= 0; +int (*_my_b_encr_write)(IO_CACHE *info,const uchar *Buffer,size_t Count)= 0; + + /* Setup internal pointers inside IO_CACHE @@ -97,6 +107,8 @@ static void init_functions(IO_CACHE* info) { enum cache_type type= info->type; + info->read_function = 0; /* Force a core if used */ + info->write_function = 0; /* Force a core if used */ switch (type) { case READ_NET: /* @@ -106,14 +118,35 @@ init_functions(IO_CACHE* info) programs that link against mysys but know nothing about THD, such as myisamchk */ + DBUG_ASSERT(!(info->myflags & MY_ENCRYPT)); break; case SEQ_READ_APPEND: info->read_function = _my_b_seq_read; - info->write_function = 0; /* Force a core if used */ + DBUG_ASSERT(!(info->myflags & MY_ENCRYPT)); + break; + case READ_CACHE: + if (info->myflags & MY_ENCRYPT) + { + DBUG_ASSERT(info->share == 0); + info->read_function = _my_b_encr_read; + break; + } + /* fall through */ + case WRITE_CACHE: + if (info->myflags & MY_ENCRYPT) + { + info->write_function = _my_b_encr_write; + break; + } + /* fall through */ + case READ_FIFO: + DBUG_ASSERT(!(info->myflags & MY_ENCRYPT)); + info->read_function = info->share ? _my_b_cache_read_r : _my_b_cache_read; + info->write_function = info->share ? _my_b_cache_write_r : _my_b_cache_write; + break; + case TYPE_NOT_SET: + DBUG_ASSERT(0); break; - default: - info->read_function = info->share ? _my_b_read_r : _my_b_read; - info->write_function = _my_b_write; } setup_io_cache(info); @@ -134,7 +167,7 @@ init_functions(IO_CACHE* info) type Type of cache seek_offset Where cache should start reading/writing use_async_io Set to 1 of we should use async_io (if avaiable) - cache_myflags Bitmap of differnt flags + cache_myflags Bitmap of different flags MY_WME | MY_FAE | MY_NABP | MY_FNABP | MY_DONT_CHECK_FILESIZE @@ -145,7 +178,7 @@ init_functions(IO_CACHE* info) int init_io_cache(IO_CACHE *info, File file, size_t cachesize, enum cache_type type, my_off_t seek_offset, - pbool use_async_io, myf cache_myflags) + my_bool use_async_io, myf cache_myflags) { size_t min_cache; my_off_t pos; @@ -157,14 +190,13 @@ int init_io_cache(IO_CACHE *info, File file, size_t cachesize, info->file= file; info->type= TYPE_NOT_SET; /* Don't set it until mutex are created */ info->pos_in_file= seek_offset; - info->pre_close = info->pre_read = info->post_read = 0; - info->arg = 0; info->alloced_buffer = 0; info->buffer=0; info->seek_not_done= 0; if (file >= 0) { + DBUG_ASSERT(!(cache_myflags & MY_ENCRYPT)); pos= mysql_file_tell(file, MYF(0)); if ((pos == (my_off_t) -1) && (my_errno == ESPIPE)) { @@ -181,6 +213,12 @@ int init_io_cache(IO_CACHE *info, File file, size_t cachesize, else info->seek_not_done= MY_TEST(seek_offset != pos); } + else + if (type == WRITE_CACHE && _my_b_encr_read) + { + cache_myflags|= MY_ENCRYPT; + DBUG_ASSERT(seek_offset == 0); + } info->disk_writes= 0; info->share=0; @@ -190,6 +228,7 @@ int init_io_cache(IO_CACHE *info, File file, size_t cachesize, min_cache=use_async_io ? IO_SIZE*4 : IO_SIZE*2; if (type == READ_CACHE || type == SEQ_READ_APPEND) { /* Assume file isn't growing */ + DBUG_ASSERT(!(cache_myflags & MY_ENCRYPT)); if (!(cache_myflags & MY_DONT_CHECK_FILESIZE)) { /* Calculate end of file to avoid allocating oversized buffers */ @@ -207,7 +246,7 @@ int init_io_cache(IO_CACHE *info, File file, size_t cachesize, } } cache_myflags &= ~MY_DONT_CHECK_FILESIZE; - if (type != READ_NET && type != WRITE_NET) + if (type != READ_NET) { /* Retry allocating memory in smaller blocks until we get one */ cachesize= ((cachesize + min_cache-1) & ~(min_cache-1)); @@ -225,15 +264,18 @@ int init_io_cache(IO_CACHE *info, File file, size_t cachesize, buffer_block= cachesize; if (type == SEQ_READ_APPEND) buffer_block *= 2; + else if (cache_myflags & MY_ENCRYPT) + buffer_block= 2*(buffer_block + MY_AES_BLOCK_SIZE) + sizeof(IO_CACHE_CRYPT); if (cachesize == min_cache) flags|= (myf) MY_WME; if ((info->buffer= (uchar*) my_malloc(buffer_block, flags)) != 0) { - info->write_buffer=info->buffer; if (type == SEQ_READ_APPEND) - info->write_buffer = info->buffer + cachesize; - info->alloced_buffer=1; + info->write_buffer= info->buffer + cachesize; + else + info->write_buffer= info->buffer; + info->alloced_buffer= 1; break; /* Enough memory found */ } if (cachesize == min_cache) @@ -277,6 +319,7 @@ int init_io_cache(IO_CACHE *info, File file, size_t cachesize, if (use_async_io && ! my_disable_async_io) { DBUG_PRINT("info",("Using async io")); + DBUG_ASSERT(!(cache_myflags & MY_ENCRYPT)); info->read_length/=2; info->read_function=_my_b_async_read; } @@ -322,18 +365,16 @@ static void my_aiowait(my_aio_result *result) my_bool reinit_io_cache(IO_CACHE *info, enum cache_type type, my_off_t seek_offset, - pbool use_async_io __attribute__((unused)), - pbool clear_cache) + my_bool use_async_io __attribute__((unused)), + my_bool clear_cache) { DBUG_ENTER("reinit_io_cache"); DBUG_PRINT("enter",("cache: 0x%lx type: %d seek_offset: %lu clear_cache: %d", (ulong) info, type, (ulong) seek_offset, (int) clear_cache)); - /* One can't do reinit with the following types */ - DBUG_ASSERT(type != READ_NET && info->type != READ_NET && - type != WRITE_NET && info->type != WRITE_NET && - type != SEQ_READ_APPEND && info->type != SEQ_READ_APPEND); + DBUG_ASSERT(type == READ_CACHE || type == WRITE_CACHE); + DBUG_ASSERT(info->type == READ_CACHE || info->type == WRITE_CACHE); /* If the whole file is in memory, avoid flushing to disk */ if (! clear_cache && @@ -391,8 +432,22 @@ my_bool reinit_io_cache(IO_CACHE *info, enum cache_type type, } else { - info->write_end=(info->buffer + info->buffer_length - - (seek_offset & (IO_SIZE-1))); + if (info->myflags & MY_ENCRYPT) + { + info->write_end = info->write_buffer + info->buffer_length; + if (seek_offset && info->file != -1) + { + info->read_end= info->buffer; + _my_b_encr_read(info, 0, 0); /* prefill the buffer */ + info->write_pos= info->read_pos; + info->pos_in_file+= info->buffer_length; + } + } + else + { + info->write_end=(info->buffer + info->buffer_length - + (seek_offset & (IO_SIZE-1))); + } info->end_of_file= ~(my_off_t) 0; } } @@ -405,6 +460,7 @@ my_bool reinit_io_cache(IO_CACHE *info, enum cache_type type, ((ulong) info->buffer_length < (ulong) (info->end_of_file - seek_offset))) { + DBUG_ASSERT(!(cache_myflags & MY_ENCRYPT)); info->read_length=info->buffer_length/2; info->read_function=_my_b_async_read; } @@ -414,12 +470,72 @@ my_bool reinit_io_cache(IO_CACHE *info, enum cache_type type, } /* reinit_io_cache */ +int _my_b_read(IO_CACHE *info, uchar *Buffer, size_t Count) +{ + size_t left_length; + int res; + + /* If the buffer is not empty yet, copy what is available. */ + if ((left_length= (size_t) (info->read_end - info->read_pos))) + { + DBUG_ASSERT(Count > left_length); + memcpy(Buffer, info->read_pos, left_length); + Buffer+=left_length; + Count-=left_length; + } + res= info->read_function(info, Buffer, Count); + if (res && info->error >= 0) + info->error+= left_length; /* update number or read bytes */ + return res; +} + +int _my_b_write(IO_CACHE *info, const uchar *Buffer, size_t Count) +{ + size_t rest_length; + int res; + + /* Always use my_b_flush_io_cache() to flush write_buffer! */ + DBUG_ASSERT(Buffer != info->write_buffer); + + if (info->pos_in_file + info->buffer_length > info->end_of_file) + { + my_errno=errno=EFBIG; + return info->error = -1; + } + + rest_length= (size_t) (info->write_end - info->write_pos); + DBUG_ASSERT(Count >= rest_length); + memcpy(info->write_pos, Buffer, (size_t) rest_length); + Buffer+=rest_length; + Count-=rest_length; + info->write_pos+=rest_length; + + if (my_b_flush_io_cache(info, 1)) + return 1; + + if (Count) + { + my_off_t old_pos_in_file= info->pos_in_file; + res= info->write_function(info, Buffer, Count); + Count-= info->pos_in_file - old_pos_in_file; + Buffer+= info->pos_in_file - old_pos_in_file; + } + else + res= 0; + + if (!res && Count) + { + memcpy(info->write_pos, Buffer, Count); + info->write_pos+= Count; + } + return res; +} /* Read buffered. SYNOPSIS - _my_b_read() + _my_b_cache_read() info IO_CACHE pointer Buffer Buffer to retrieve count bytes from file Count Number of bytes to read into Buffer @@ -435,7 +551,7 @@ my_bool reinit_io_cache(IO_CACHE *info, enum cache_type type, types than my_off_t unless you can be sure that their value fits. Same applies to differences of file offsets. - When changing this function, check _my_b_read_r(). It might need the + When changing this function, check _my_b_cache_read_r(). It might need the same change. RETURN @@ -445,20 +561,11 @@ my_bool reinit_io_cache(IO_CACHE *info, enum cache_type type, Otherwise info->error contains the number of bytes in Buffer. */ -int _my_b_read(register IO_CACHE *info, uchar *Buffer, size_t Count) +int _my_b_cache_read(IO_CACHE *info, uchar *Buffer, size_t Count) { - size_t length,diff_length,left_length, max_length; + size_t length, diff_length, left_length= 0, max_length; my_off_t pos_in_file; - DBUG_ENTER("_my_b_read"); - - /* If the buffer is not empty yet, copy what is available. */ - if ((left_length= (size_t) (info->read_end-info->read_pos))) - { - DBUG_ASSERT(Count >= left_length); /* User is not using my_b_read() */ - memcpy(Buffer,info->read_pos, left_length); - Buffer+=left_length; - Count-=left_length; - } + DBUG_ENTER("_my_b_cache_read"); /* pos_in_file always point on where info->buffer was read */ pos_in_file=info->pos_in_file+ (size_t) (info->read_end - info->buffer); @@ -515,7 +622,7 @@ int _my_b_read(register IO_CACHE *info, uchar *Buffer, size_t Count) what we did already read from a block. That way, the read will end aligned with a block. */ - length=(Count & (size_t) ~(IO_SIZE-1))-diff_length; + length= IO_ROUND_DN(Count) - diff_length; if ((read_length= mysql_file_read(info->file,Buffer, length, info->myflags)) != length) { @@ -558,7 +665,11 @@ int _my_b_read(register IO_CACHE *info, uchar *Buffer, size_t Count) info->error= (int) left_length; DBUG_RETURN(1); } - length=0; /* Didn't read any chars */ + else + { + info->error= 0; + DBUG_RETURN(0); /* EOF */ + } } else if ((length= mysql_file_read(info->file,info->buffer, max_length, info->myflags)) < Count || @@ -685,12 +796,15 @@ void init_io_cache_share(IO_CACHE *read_cache, IO_CACHE_SHARE *cshare, cshare->source_cache= write_cache; /* Can be NULL. */ read_cache->share= cshare; - read_cache->read_function= _my_b_read_r; + read_cache->read_function= _my_b_cache_read_r; read_cache->current_pos= NULL; read_cache->current_end= NULL; if (write_cache) + { write_cache->share= cshare; + write_cache->write_function= _my_b_cache_write_r; + } DBUG_VOID_RETURN; } @@ -958,7 +1072,7 @@ static void unlock_io_cache(IO_CACHE *cache) Read from IO_CACHE when it is shared between several threads. SYNOPSIS - _my_b_read_r() + _my_b_cache_read_r() cache IO_CACHE pointer Buffer Buffer to retrieve count bytes from file Count Number of bytes to read into Buffer @@ -983,7 +1097,7 @@ static void unlock_io_cache(IO_CACHE *cache) types than my_off_t unless you can be sure that their value fits. Same applies to differences of file offsets. (Bug #11527) - When changing this function, check _my_b_read(). It might need the + When changing this function, check _my_b_cache_read(). It might need the same change. RETURN @@ -991,20 +1105,14 @@ static void unlock_io_cache(IO_CACHE *cache) 1 Error: can't read requested characters */ -int _my_b_read_r(register IO_CACHE *cache, uchar *Buffer, size_t Count) +static int _my_b_cache_read_r(IO_CACHE *cache, uchar *Buffer, size_t Count) { my_off_t pos_in_file; - size_t length, diff_length, left_length; + size_t length, diff_length, left_length= 0; IO_CACHE_SHARE *cshare= cache->share; - DBUG_ENTER("_my_b_read_r"); + DBUG_ENTER("_my_b_cache_read_r"); + DBUG_ASSERT(!(cache->myflags & MY_ENCRYPT)); - if ((left_length= (size_t) (cache->read_end - cache->read_pos))) - { - DBUG_ASSERT(Count >= left_length); /* User is not using my_b_read() */ - memcpy(Buffer, cache->read_pos, left_length); - Buffer+= left_length; - Count-= left_length; - } while (Count) { size_t cnt, len; @@ -1119,21 +1227,22 @@ int _my_b_read_r(register IO_CACHE *cache, uchar *Buffer, size_t Count) */ static void copy_to_read_buffer(IO_CACHE *write_cache, - const uchar *write_buffer, size_t write_length) + const uchar *write_buffer, my_off_t pos_in_file) { + size_t write_length= write_cache->pos_in_file - pos_in_file; IO_CACHE_SHARE *cshare= write_cache->share; DBUG_ASSERT(cshare->source_cache == write_cache); /* write_length is usually less or equal to buffer_length. - It can be bigger if _my_b_write() is called with a big length. + It can be bigger if _my_b_cache_write_r() is called with a big length. */ while (write_length) { size_t copy_length= MY_MIN(write_length, write_cache->buffer_length); int __attribute__((unused)) rc; - rc= lock_io_cache(write_cache, write_cache->pos_in_file); + rc= lock_io_cache(write_cache, pos_in_file); /* The writing thread does always have the lock when it awakes. */ DBUG_ASSERT(rc); @@ -1141,7 +1250,7 @@ static void copy_to_read_buffer(IO_CACHE *write_cache, cshare->error= 0; cshare->read_end= cshare->buffer + copy_length; - cshare->pos_in_file= write_cache->pos_in_file; + cshare->pos_in_file= pos_in_file; /* Mark all threads as running and wake them. */ unlock_io_cache(write_cache); @@ -1165,20 +1274,12 @@ static void copy_to_read_buffer(IO_CACHE *write_cache, 1 Failed to read */ -int _my_b_seq_read(register IO_CACHE *info, uchar *Buffer, size_t Count) +static int _my_b_seq_read(IO_CACHE *info, uchar *Buffer, size_t Count) { - size_t length, diff_length, left_length, save_count, max_length; + size_t length, diff_length, left_length= 0, save_count, max_length; my_off_t pos_in_file; save_count=Count; - /* first, read the regular buffer */ - if ((left_length=(size_t) (info->read_end-info->read_pos))) - { - DBUG_ASSERT(Count > left_length); /* User is not using my_b_read() */ - memcpy(Buffer,info->read_pos, left_length); - Buffer+=left_length; - Count-=left_length; - } lock_append_buffer(info); /* pos_in_file always point on where info->buffer was read */ @@ -1206,7 +1307,7 @@ int _my_b_seq_read(register IO_CACHE *info, uchar *Buffer, size_t Count) /* Fill first intern buffer */ size_t read_length; - length=(Count & (size_t) ~(IO_SIZE-1))-diff_length; + length= IO_ROUND_DN(Count) - diff_length; if ((read_length= mysql_file_read(info->file,Buffer, length, info->myflags)) == (size_t) -1) { @@ -1323,18 +1424,14 @@ read_append_buffer: 1 An error has occurred; IO_CACHE to error state. */ -int _my_b_async_read(register IO_CACHE *info, uchar *Buffer, size_t Count) +int _my_b_async_read(IO_CACHE *info, uchar *Buffer, size_t Count) { - size_t length,read_length,diff_length,left_length,use_length,org_Count; + size_t length, read_length, diff_length, left_length=0, use_length, org_Count; size_t max_length; my_off_t next_pos_in_file; uchar *read_buffer; - memcpy(Buffer,info->read_pos, - (left_length= (size_t) (info->read_end-info->read_pos))); - Buffer+=left_length; org_Count=Count; - Count-=left_length; if (info->inited) { /* wait for read block */ @@ -1488,7 +1585,7 @@ int _my_b_async_read(register IO_CACHE *info, uchar *Buffer, size_t Count) info->read_end-=info->read_length; } info->read_length=info->buffer_length; /* Use hole buffer */ - info->read_function=_my_b_read; /* Use normal IO_READ next */ + info->read_function=_my_b_cache_read; /* Use normal IO_READ next */ } else info->inited=info->aio_result.pending=1; @@ -1503,13 +1600,8 @@ int _my_b_async_read(register IO_CACHE *info, uchar *Buffer, size_t Count) int _my_b_get(IO_CACHE *info) { uchar buff; - IO_CACHE_CALLBACK pre_read,post_read; - if ((pre_read = info->pre_read)) - (*pre_read)(info); if ((*(info)->read_function)(info,&buff,1)) return my_b_EOF; - if ((post_read = info->post_read)) - (*post_read)(info); return (int) (uchar) buff; } @@ -1523,70 +1615,61 @@ int _my_b_get(IO_CACHE *info) -1 On error; my_errno contains error code. */ -int _my_b_write(register IO_CACHE *info, const uchar *Buffer, size_t Count) +int _my_b_cache_write(IO_CACHE *info, const uchar *Buffer, size_t Count) { - size_t rest_length,length; - my_off_t pos_in_file= info->pos_in_file; - - DBUG_EXECUTE_IF("simulate_huge_load_data_file", - { - pos_in_file=(my_off_t)(5000000000ULL); - }); - if (pos_in_file+info->buffer_length > info->end_of_file) + if (Buffer != info->write_buffer) { - my_errno=errno=EFBIG; - return info->error = -1; + Count= IO_ROUND_DN(Count); + if (!Count) + return 0; } - rest_length= (size_t) (info->write_end - info->write_pos); - memcpy(info->write_pos,Buffer,(size_t) rest_length); - Buffer+=rest_length; - Count-=rest_length; - info->write_pos+=rest_length; - - if (my_b_flush_io_cache(info,1)) - return 1; - if (Count >= IO_SIZE) - { /* Fill first intern buffer */ - length=Count & (size_t) ~(IO_SIZE-1); - if (info->seek_not_done) + if (info->seek_not_done) + { + /* + Whenever a function which operates on IO_CACHE flushes/writes + some part of the IO_CACHE to disk it will set the property + "seek_not_done" to indicate this to other functions operating + on the IO_CACHE. + */ + if (mysql_file_seek(info->file, info->pos_in_file, MY_SEEK_SET, + MYF(info->myflags & MY_WME)) == MY_FILEPOS_ERROR) { - /* - Whenever a function which operates on IO_CACHE flushes/writes - some part of the IO_CACHE to disk it will set the property - "seek_not_done" to indicate this to other functions operating - on the IO_CACHE. - */ - if (mysql_file_seek(info->file, info->pos_in_file, MY_SEEK_SET, MYF(0))) - { - info->error= -1; - return (1); - } - info->seek_not_done=0; + info->error= -1; + return 1; } - if (mysql_file_write(info->file, Buffer, length, info->myflags | MY_NABP)) - return info->error= -1; + info->seek_not_done=0; + } + if (mysql_file_write(info->file, Buffer, Count, info->myflags | MY_NABP)) + return info->error= -1; - /* - In case of a shared I/O cache with a writer we normally do direct - write cache to read cache copy. Simulate this here by direct - caller buffer to read cache copy. Do it after the write so that - the cache readers actions on the flushed part can go in parallel - with the write of the extra stuff. copy_to_read_buffer() - synchronizes writer and readers so that after this call the - readers can act on the extra stuff while the writer can go ahead - and prepare the next output. copy_to_read_buffer() relies on - info->pos_in_file. - */ - if (info->share) - copy_to_read_buffer(info, Buffer, length); + info->pos_in_file+= Count; + return 0; +} + + +/* + In case of a shared I/O cache with a writer we normally do direct + write cache to read cache copy. Simulate this here by direct + caller buffer to read cache copy. Do it after the write so that + the cache readers actions on the flushed part can go in parallel + with the write of the extra stuff. copy_to_read_buffer() + synchronizes writer and readers so that after this call the + readers can act on the extra stuff while the writer can go ahead + and prepare the next output. copy_to_read_buffer() relies on + info->pos_in_file. +*/ +static int _my_b_cache_write_r(IO_CACHE *info, const uchar *Buffer, size_t Count) +{ + my_off_t old_pos_in_file= info->pos_in_file; + int res= _my_b_cache_write(info, Buffer, Count); + if (res) + return res; + + DBUG_ASSERT(!(info->myflags & MY_ENCRYPT)); + DBUG_ASSERT(info->share); + copy_to_read_buffer(info, Buffer, old_pos_in_file); - Count-=length; - Buffer+=length; - info->pos_in_file+=length; - } - memcpy(info->write_pos,Buffer,(size_t) Count); - info->write_pos+=Count; return 0; } @@ -1597,7 +1680,7 @@ int _my_b_write(register IO_CACHE *info, const uchar *Buffer, size_t Count) the write buffer before we are ready with it. */ -int my_b_append(register IO_CACHE *info, const uchar *Buffer, size_t Count) +int my_b_append(IO_CACHE *info, const uchar *Buffer, size_t Count) { size_t rest_length,length; @@ -1606,6 +1689,7 @@ int my_b_append(register IO_CACHE *info, const uchar *Buffer, size_t Count) day, we might need to add a call to copy_to_read_buffer(). */ DBUG_ASSERT(!info->share); + DBUG_ASSERT(!(info->myflags & MY_ENCRYPT)); lock_append_buffer(info); rest_length= (size_t) (info->write_end - info->write_pos); @@ -1622,7 +1706,7 @@ int my_b_append(register IO_CACHE *info, const uchar *Buffer, size_t Count) } if (Count >= IO_SIZE) { /* Fill first intern buffer */ - length=Count & (size_t) ~(IO_SIZE-1); + length= IO_ROUND_DN(Count); if (mysql_file_write(info->file,Buffer, length, info->myflags | MY_NABP)) { unlock_append_buffer(info); @@ -1661,7 +1745,7 @@ int my_b_safe_write(IO_CACHE *info, const uchar *Buffer, size_t Count) we will never get a seek over the end of the buffer */ -int my_block_write(register IO_CACHE *info, const uchar *Buffer, size_t Count, +int my_block_write(IO_CACHE *info, const uchar *Buffer, size_t Count, my_off_t pos) { size_t length; @@ -1672,6 +1756,7 @@ int my_block_write(register IO_CACHE *info, const uchar *Buffer, size_t Count, day, we might need to add a call to copy_to_read_buffer(). */ DBUG_ASSERT(!info->share); + DBUG_ASSERT(!(info->myflags & MY_ENCRYPT)); if (pos < info->pos_in_file) { @@ -1719,11 +1804,9 @@ int my_block_write(register IO_CACHE *info, const uchar *Buffer, size_t Count, #define UNLOCK_APPEND_BUFFER if (need_append_buffer_lock) \ unlock_append_buffer(info); -int my_b_flush_io_cache(IO_CACHE *info, - int need_append_buffer_lock __attribute__((unused))) +int my_b_flush_io_cache(IO_CACHE *info, int need_append_buffer_lock) { size_t length; - my_off_t pos_in_file; my_bool append_cache= (info->type == SEQ_READ_APPEND); DBUG_ENTER("my_b_flush_io_cache"); DBUG_PRINT("enter", ("cache: 0x%lx", (long) info)); @@ -1742,52 +1825,30 @@ int my_b_flush_io_cache(IO_CACHE *info, if ((length=(size_t) (info->write_pos - info->write_buffer))) { - /* - In case of a shared I/O cache with a writer we do direct write - cache to read cache copy. Do it before the write here so that - the readers can work in parallel with the write. - copy_to_read_buffer() relies on info->pos_in_file. - */ - if (info->share) - copy_to_read_buffer(info, info->write_buffer, length); - - pos_in_file=info->pos_in_file; - /* - If we have append cache, we always open the file with - O_APPEND which moves the pos to EOF automatically on every write - */ - if (!append_cache && info->seek_not_done) - { /* File touched, do seek */ - if (mysql_file_seek(info->file, pos_in_file, MY_SEEK_SET, MYF(info->myflags & MY_WME)) == - MY_FILEPOS_ERROR) - { - UNLOCK_APPEND_BUFFER; - DBUG_RETURN((info->error= -1)); - } - if (!append_cache) - info->seek_not_done=0; - } - if (!append_cache) - info->pos_in_file+=length; - info->write_end= (info->write_buffer+info->buffer_length- - ((pos_in_file+length) & (IO_SIZE-1))); - - if (mysql_file_write(info->file,info->write_buffer,length, - info->myflags | MY_NABP)) - info->error= -1; - else - info->error= 0; - if (!append_cache) + if (append_cache) { - set_if_bigger(info->end_of_file,(pos_in_file+length)); + + if (mysql_file_write(info->file, info->write_buffer, length, + info->myflags | MY_NABP)) + info->error= -1; + else + info->error= 0; + + info->end_of_file+= info->write_pos - info->append_read_pos; + info->append_read_pos= info->write_buffer; + DBUG_ASSERT(info->end_of_file == mysql_file_tell(info->file, MYF(0))); } else { - info->end_of_file+=(info->write_pos-info->append_read_pos); - DBUG_ASSERT(info->end_of_file == mysql_file_tell(info->file, MYF(0))); - } + int res= info->write_function(info, info->write_buffer, length); + if (res) + DBUG_RETURN(res); - info->append_read_pos=info->write_pos=info->write_buffer; + set_if_bigger(info->end_of_file, info->pos_in_file); + } + info->write_end= (info->write_buffer + info->buffer_length - + ((info->pos_in_file + length) & (IO_SIZE - 1))); + info->write_pos= info->write_buffer; ++info->disk_writes; UNLOCK_APPEND_BUFFER; DBUG_RETURN(info->error); @@ -1824,7 +1885,6 @@ int my_b_flush_io_cache(IO_CACHE *info, int end_io_cache(IO_CACHE *info) { int error=0; - IO_CACHE_CALLBACK pre_close; DBUG_ENTER("end_io_cache"); DBUG_PRINT("enter",("cache: 0x%lx", (ulong) info)); @@ -1834,11 +1894,6 @@ int end_io_cache(IO_CACHE *info) */ DBUG_ASSERT(!info->share || !info->share->total_threads); - if ((pre_close=info->pre_close)) - { - (*pre_close)(info); - info->pre_close= 0; - } if (info->alloced_buffer) { info->alloced_buffer=0; diff --git a/mysys/mf_iocache2.c b/mysys/mf_iocache2.c index 9e693209445..2499094037d 100644 --- a/mysys/mf_iocache2.c +++ b/mysys/mf_iocache2.c @@ -61,7 +61,6 @@ my_b_copy_to_file(IO_CACHE *cache, FILE *file) if (my_fwrite(file, cache->read_pos, bytes_in_cache, MYF(MY_WME | MY_NABP)) == (size_t) -1) DBUG_RETURN(1); - cache->read_pos= cache->read_end; } while ((bytes_in_cache= my_b_fill(cache))); if(cache->error == -1) DBUG_RETURN(1); @@ -181,60 +180,19 @@ void my_b_seek(IO_CACHE *info,my_off_t pos) DBUG_VOID_RETURN; } - -/* - Fill buffer of the cache. - - NOTES - This assumes that you have already used all characters in the CACHE, - independent of the read_pos value! - - RETURN - 0 On error or EOF (info->error = -1 on error) - # Number of characters -*/ - - -size_t my_b_fill(IO_CACHE *info) +int my_b_pread(IO_CACHE *info, uchar *Buffer, size_t Count, my_off_t pos) { - my_off_t pos_in_file=(info->pos_in_file+ - (size_t) (info->read_end - info->buffer)); - size_t diff_length, length, max_length; - - if (info->seek_not_done) - { /* File touched, do seek */ - if (mysql_file_seek(info->file, pos_in_file, MY_SEEK_SET, MYF(0)) == - MY_FILEPOS_ERROR) - { - info->error= 0; - return 0; - } - info->seek_not_done=0; - } - diff_length=(size_t) (pos_in_file & (IO_SIZE-1)); - max_length=(info->read_length-diff_length); - if (max_length >= (info->end_of_file - pos_in_file)) - max_length= (size_t) (info->end_of_file - pos_in_file); - - if (!max_length) - { - info->error= 0; - return 0; /* EOF */ - } - DBUG_EXECUTE_IF ("simulate_my_b_fill_error", - {DBUG_SET("+d,simulate_file_read_error");}); - if ((length= mysql_file_read(info->file, info->buffer, max_length, - info->myflags)) == (size_t) -1) + if (info->myflags & MY_ENCRYPT) { - info->error= -1; - return 0; + my_b_seek(info, pos); + return my_b_read(info, Buffer, Count); } - info->read_pos=info->buffer; - info->read_end=info->buffer+length; - info->pos_in_file=pos_in_file; - return length; -} + /* backward compatibility behavior. XXX remove it? */ + if (mysql_file_pread(info->file, Buffer, Count, pos, info->myflags | MY_NABP)) + return info->error= -1; + return 0; +} /* Read a string ended by '\n' into a buffer of 'max_length' size. diff --git a/mysys/mf_keycache.c b/mysys/mf_keycache.c index 0e7c43cc4c4..3263459bd72 100644 --- a/mysys/mf_keycache.c +++ b/mysys/mf_keycache.c @@ -4166,10 +4166,10 @@ restart: if (! (type == FLUSH_KEEP || type == FLUSH_FORCE_WRITE)) { - BLOCK_LINK *last_for_update= NULL; BLOCK_LINK *last_in_switch= NULL; uint total_found= 0; uint found; + last_for_update= NULL; /* Finally free all clean blocks for this file. diff --git a/mysys/my_access.c b/mysys/my_access.c index 68cd01d33e6..0da5e7f0bf0 100644 --- a/mysys/my_access.c +++ b/mysys/my_access.c @@ -173,6 +173,11 @@ static my_bool does_drive_exists(char drive_letter) file names with a colon (:) are not allowed because such file names store data in Alternate Data Streams which can be used to hide the data. + Apart from colon, other characters that are not allowed in filenames + on Windows are greater/less sign, double quotes, forward slash, backslash, + pipe and star characters. + + See MSDN documentation on filename restrictions. @param name contains the file name with or without path @param length contains the length of file name @@ -181,6 +186,8 @@ static my_bool does_drive_exists(char drive_letter) @return TRUE if the file name is allowed, FALSE otherwise. */ +#define ILLEGAL_FILENAME_CHARS "<>:\"/\|?*" + my_bool is_filename_allowed(const char *name __attribute__((unused)), size_t length __attribute__((unused)), my_bool allow_current_dir __attribute__((unused))) @@ -205,6 +212,8 @@ my_bool is_filename_allowed(const char *name __attribute__((unused)), return (allow_current_dir && (ch - name == 1) && does_drive_exists(*name)); } + else if (strchr(ILLEGAL_FILENAME_CHARS, *ch)) + return FALSE; } return TRUE; } /* is_filename_allowed */ diff --git a/mysys/my_addr_resolve.c b/mysys/my_addr_resolve.c index 90e6f43f390..72b04119855 100644 --- a/mysys/my_addr_resolve.c +++ b/mysys/my_addr_resolve.c @@ -126,41 +126,94 @@ err: */ #elif defined(MY_ADDR_RESOLVE_FORK) /* - yet another - just execute addr2line or eu-addr2line, whatever available, - pipe the addresses to it, and parse the output + yet another - just execute addr2line pipe the addresses to it, and parse the + output */ #include <m_string.h> #include <ctype.h> + +#if defined(HAVE_LINK_H) && defined(HAVE_DLOPEN) +#include <link.h> +static ptrdiff_t offset= 0; +#else +#define offset 0 +#endif + static int in[2], out[2]; static int initialized= 0; static char output[1024]; int my_addr_resolve(void *ptr, my_addr_loc *loc) { - char input[32], *s; + char input[32]; size_t len; - len= my_snprintf(input, sizeof(input), "%p\n", ptr); + ssize_t total_bytes_read = 0; + ssize_t extra_bytes_read = 0; + + fd_set set; + struct timeval timeout; + + int filename_start = -1; + int line_number_start = -1; + ssize_t i; + + FD_ZERO(&set); + FD_SET(out[0], &set); + + len= my_snprintf(input, sizeof(input), "%p\n", ptr - offset); if (write(in[1], input, len) <= 0) return 1; - if (read(out[0], output, sizeof(output)) <= 0) + + /* 10 ms should be plenty of time for addr2line to issue a response. */ + timeout.tv_sec = 0; + timeout.tv_usec = 10000; + /* Read in a loop till all the output from addr2line is complete. */ + while (select(out[0] + 1, &set, NULL, NULL, &timeout) > 0) + { + extra_bytes_read= read(out[0], output + total_bytes_read, + sizeof(output) - total_bytes_read); + if (extra_bytes_read < 0) + return 1; + /* Timeout or max bytes read. */ + if (extra_bytes_read == 0) + break; + + total_bytes_read += extra_bytes_read; + } + + /* Failed starting addr2line. */ + if (total_bytes_read == 0) return 1; - loc->func= s= output; - while (*s != '\n') - s++; - *s++= 0; - loc->file= s; - while (*s != ':') - s++; - *s++= 0; + /* Go through the addr2line response and get the required data. + The response is structured in 2 lines. The first line contains the function + name, while the second one contains <filename>:<line number> */ + for (i = 0; i < total_bytes_read; i++) { + if (output[i] == '\n') { + filename_start = i + 1; + output[i] = '\0'; + } + if (filename_start != -1 && output[i] == ':') { + line_number_start = i + 1; + output[i] = '\0'; + } + if (line_number_start != -1) { + loc->line= atoi(output + line_number_start); + break; + } + } + /* Response is malformed. */ + if (filename_start == -1 || line_number_start == -1) + return 1; + + loc->func= output; + loc->file= output + filename_start; + + /* Addr2line was unable to extract any meaningful information. */ if (strcmp(loc->file, "??") == 0) return 1; - loc->line= 0; - while (isdigit(*s)) - loc->line = loc->line * 10 + (*s++ - '0'); - *s = 0; loc->file= strip_path(loc->file); return 0; @@ -172,6 +225,12 @@ const char *my_addr_resolve_init() { pid_t pid; +#if defined(HAVE_LINK_H) && defined(HAVE_DLOPEN) + struct link_map *lm = (struct link_map*) dlopen(0, RTLD_NOW); + if (lm) + offset= lm->l_addr; +#endif + if (pipe(in) < 0) return "pipe(in)"; if (pipe(out) < 0) diff --git a/mysys/my_alloc.c b/mysys/my_alloc.c index fc30185eb5a..664a7380ca4 100644 --- a/mysys/my_alloc.c +++ b/mysys/my_alloc.c @@ -56,7 +56,8 @@ void init_alloc_root(MEM_ROOT *mem_root, size_t block_size, myf my_flags) { DBUG_ENTER("init_alloc_root"); - DBUG_PRINT("enter",("root: 0x%lx", (long) mem_root)); + DBUG_PRINT("enter",("root: %p prealloc: %zu", mem_root, + pre_alloc_size)); mem_root->free= mem_root->used= mem_root->pre_alloc= 0; mem_root->min_malloc= 32; @@ -104,6 +105,7 @@ void init_alloc_root(MEM_ROOT *mem_root, size_t block_size, void reset_root_defaults(MEM_ROOT *mem_root, size_t block_size, size_t pre_alloc_size __attribute__((unused))) { + DBUG_ENTER("reset_root_defaults"); DBUG_ASSERT(alloc_root_inited(mem_root)); mem_root->block_size= (((block_size - ALLOC_ROOT_MIN_BLOCK_SIZE) & ~1) | @@ -126,7 +128,7 @@ void reset_root_defaults(MEM_ROOT *mem_root, size_t block_size, { /* We found a suitable block, no need to do anything else */ mem_root->pre_alloc= mem; - return; + DBUG_VOID_RETURN; } if (mem->left + ALIGN_SIZE(sizeof(USED_MEM)) == mem->size) { @@ -156,6 +158,8 @@ void reset_root_defaults(MEM_ROOT *mem_root, size_t block_size, else #endif mem_root->pre_alloc= 0; + + DBUG_VOID_RETURN; } @@ -164,7 +168,7 @@ void *alloc_root(MEM_ROOT *mem_root, size_t length) #if defined(HAVE_valgrind) && defined(EXTRA_DEBUG) reg1 USED_MEM *next; DBUG_ENTER("alloc_root"); - DBUG_PRINT("enter",("root: 0x%lx", (long) mem_root)); + DBUG_PRINT("enter",("root: %p", mem_root)); DBUG_ASSERT(alloc_root_inited(mem_root)); @@ -188,8 +192,8 @@ void *alloc_root(MEM_ROOT *mem_root, size_t length) next->next= mem_root->used; next->size= length; mem_root->used= next; - DBUG_PRINT("exit",("ptr: 0x%lx", (long) (((char*) next)+ - ALIGN_SIZE(sizeof(USED_MEM))))); + DBUG_PRINT("exit",("ptr: %p", (((char*) next)+ + ALIGN_SIZE(sizeof(USED_MEM))))); DBUG_RETURN((uchar*) (((char*) next)+ALIGN_SIZE(sizeof(USED_MEM)))); #else size_t get_size, block_size; @@ -197,7 +201,7 @@ void *alloc_root(MEM_ROOT *mem_root, size_t length) reg1 USED_MEM *next= 0; reg2 USED_MEM **prev; DBUG_ENTER("alloc_root"); - DBUG_PRINT("enter",("root: 0x%lx", (long) mem_root)); + DBUG_PRINT("enter",("root: %p", mem_root)); DBUG_ASSERT(alloc_root_inited(mem_root)); DBUG_EXECUTE_IF("simulate_out_of_memory", @@ -256,7 +260,7 @@ void *alloc_root(MEM_ROOT *mem_root, size_t length) mem_root->first_block_usage= 0; } TRASH_ALLOC(point, length); - DBUG_PRINT("exit",("ptr: 0x%lx", (ulong) point)); + DBUG_PRINT("exit",("ptr: %p", point)); DBUG_RETURN((void*) point); #endif } @@ -368,7 +372,7 @@ void free_root(MEM_ROOT *root, myf MyFlags) { reg1 USED_MEM *next,*old; DBUG_ENTER("free_root"); - DBUG_PRINT("enter",("root: 0x%lx flags: %u", (long) root, (uint) MyFlags)); + DBUG_PRINT("enter",("root: %p flags: %u", root, (uint) MyFlags)); if (MyFlags & MY_MARK_BLOCKS_FREE) { diff --git a/mysys/my_bitmap.c b/mysys/my_bitmap.c index 67c478659b5..0eaf1a88aa1 100644 --- a/mysys/my_bitmap.c +++ b/mysys/my_bitmap.c @@ -306,6 +306,12 @@ uint bitmap_set_next(MY_BITMAP *map) } +/** + Set the specified number of bits in the bitmap buffer. + + @param map [IN] Bitmap + @param prefix_size [IN] Number of bits to be set +*/ void bitmap_set_prefix(MY_BITMAP *map, uint prefix_size) { uint prefix_bytes, prefix_bits, d; @@ -319,11 +325,12 @@ void bitmap_set_prefix(MY_BITMAP *map, uint prefix_size) m+= prefix_bytes; if ((prefix_bits= prefix_size & 7)) { - *m++= (1 << prefix_bits)-1; - prefix_bytes++; + *(m++)= (1 << prefix_bits)-1; + // As the prefix bits are set, lets count this byte too as a prefix byte. + prefix_bytes ++; } if ((d= no_bytes_in_map(map)-prefix_bytes)) - bzero(m, d); + memset(m, 0, d); } @@ -373,6 +380,7 @@ my_bool bitmap_is_clear_all(const MY_BITMAP *map) my_bitmap_map *data_ptr= map->bitmap; my_bitmap_map *end= map->last_word_ptr; + DBUG_ASSERT(map->n_bits > 0); for (; data_ptr < end; data_ptr++) if (*data_ptr) return FALSE; diff --git a/mysys/my_compress.c b/mysys/my_compress.c index 4cd43596031..78d09bb5f36 100644 --- a/mysys/my_compress.c +++ b/mysys/my_compress.c @@ -203,129 +203,4 @@ my_bool my_uncompress(uchar *packet, size_t len, size_t *complen) DBUG_RETURN(0); } -/* - Internal representation of the frm blob is: - - ver 4 bytes - orglen 4 bytes - complen 4 bytes -*/ - -#define BLOB_HEADER 12 - - -/* - 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 - - NOTES - data is replaced with compressed content - - RETURN VALUES - 0 Success - >0 Failure -*/ - -int packfrm(const uchar *data, size_t len, - uchar **pack_data, size_t *pack_len) -{ - int error; - size_t org_len, comp_len, blob_len; - uchar *blob; - DBUG_ENTER("packfrm"); - DBUG_PRINT("enter", ("data: 0x%lx len: %lu", (long) data, (ulong) len)); - - error= 1; - org_len= len; - if (my_compress((uchar*)data, &org_len, &comp_len)) - goto err; - - DBUG_PRINT("info", ("org_len: %lu comp_len: %lu", (ulong) org_len, - (ulong) comp_len)); - DBUG_DUMP("compressed", data, org_len); - - error= 2; - blob_len= BLOB_HEADER + org_len; - if (!(blob= (uchar*) my_malloc(blob_len,MYF(MY_WME)))) - goto err; - - /* Store compressed blob in machine independent format */ - int4store(blob, 1); - int4store(blob+4, (uint32) len); - int4store(blob+8, (uint32) org_len); /* compressed length */ - - /* Copy frm data into blob, already in machine independent format */ - memcpy(blob+BLOB_HEADER, data, org_len); - - *pack_data= blob; - *pack_len= blob_len; - error= 0; - - DBUG_PRINT("exit", ("pack_data: 0x%lx pack_len: %lu", - (long) *pack_data, (ulong) *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(uchar **unpack_data, size_t *unpack_len, - const uchar *pack_data) -{ - uchar *data; - size_t complen, orglen; - ulong ver; - DBUG_ENTER("unpackfrm"); - DBUG_PRINT("enter", ("pack_data: 0x%lx", (long) pack_data)); - - ver= uint4korr(pack_data); - orglen= uint4korr(pack_data+4); - complen= uint4korr(pack_data+8); - - DBUG_PRINT("blob",("ver: %lu complen: %lu orglen: %lu", - ver, (ulong) complen, (ulong) orglen)); - DBUG_DUMP("blob->data", pack_data + BLOB_HEADER, complen); - - if (ver != 1) - DBUG_RETURN(1); - if (!(data= my_malloc(MY_MAX(orglen, complen), MYF(MY_WME)))) - DBUG_RETURN(2); - memcpy(data, pack_data + BLOB_HEADER, complen); - - if (my_uncompress(data, complen, &orglen)) - { - my_free(data); - DBUG_RETURN(3); - } - - *unpack_data= data; - *unpack_len= orglen; - - DBUG_PRINT("exit", ("frmdata: 0x%lx len: %lu", (long) *unpack_data, - (ulong) *unpack_len)); - DBUG_RETURN(0); -} #endif /* HAVE_COMPRESS */ diff --git a/mysys/my_create.c b/mysys/my_create.c index 51de343d4a1..6a3bcd63557 100644 --- a/mysys/my_create.c +++ b/mysys/my_create.c @@ -43,7 +43,7 @@ File my_create(const char *FileName, int CreateFlags, int access_flags, #if defined(_WIN32) fd= my_win_open(FileName, access_flags | O_CREAT); #else - fd= open((char *) FileName, access_flags | O_CREAT, + fd= open((char *) FileName, access_flags | O_CREAT | O_CLOEXEC, CreateFlags ? CreateFlags : my_umask); #endif diff --git a/mysys/my_default.c b/mysys/my_default.c index 0f9b70ca326..7f41551f779 100644 --- a/mysys/my_default.c +++ b/mysys/my_default.c @@ -102,8 +102,7 @@ static const char *f_extensions[]= { ".cnf", 0 }; #define NEWLINE "\n" #endif -static int handle_default_option(void *in_ctx, const char *group_name, - const char *option); +static int handle_default_option(void *, const char *, const char *); /* This structure defines the context that we pass to callback @@ -410,14 +409,13 @@ int get_defaults_options(int argc, char **argv, char **extra_defaults, char **group_suffix) { - int org_argc= argc, prev_argc= 0; + int org_argc= argc; *defaults= *extra_defaults= *group_suffix= 0; - while (argc >= 2 && argc != prev_argc) + while (argc >= 2) { /* Skip program name or previously handled argument */ argv++; - prev_argc= argc; /* To check if we found */ if (!*defaults && is_prefix(*argv,"--defaults-file=")) { *defaults= *argv + sizeof("--defaults-file=")-1; @@ -436,6 +434,7 @@ int get_defaults_options(int argc, char **argv, argc--; continue; } + break; } return org_argc - argc; } @@ -918,7 +917,7 @@ static int search_default_file_with_ext(Process_option_func opt_handler, end= remove_end_comment(ptr); if ((value= strchr(ptr, '='))) - end= value; /* Option without argument */ + end= value; for ( ; my_isspace(&my_charset_latin1,end[-1]) ; end--) ; if (!value) { diff --git a/mysys/my_fopen.c b/mysys/my_fopen.c index b6027a99c90..2ac033c8264 100644 --- a/mysys/my_fopen.c +++ b/mysys/my_fopen.c @@ -42,7 +42,7 @@ static void make_ftype(char * to,int flag); FILE *my_fopen(const char *filename, int flags, myf MyFlags) { FILE *fd; - char type[5]; + char type[10]; DBUG_ENTER("my_fopen"); DBUG_PRINT("my",("Name: '%s' flags: %d MyFlags: %lu", filename, flags, MyFlags)); @@ -87,8 +87,7 @@ FILE *my_fopen(const char *filename, int flags, myf MyFlags) my_errno=errno; DBUG_PRINT("error",("Got error %d on open",my_errno)); if (MyFlags & (MY_FFNF | MY_FAE | MY_WME)) - my_error((flags & O_RDONLY) || (flags == O_RDONLY ) ? EE_FILENOTFOUND : - EE_CANTCREATEFILE, + my_error((flags & O_RDONLY) ? EE_FILENOTFOUND : EE_CANTCREATEFILE, MYF(ME_BELL+ME_WAITTANG), filename, my_errno); DBUG_RETURN((FILE*) 0); } /* my_fopen */ @@ -351,9 +350,11 @@ static void make_ftype(register char * to, register int flag) else *to++= 'r'; -#if FILE_BINARY /* If we have binary-files */ if (flag & FILE_BINARY) *to++='b'; -#endif + + if (O_CLOEXEC) + *to++= 'e'; + *to='\0'; } /* make_ftype */ diff --git a/mysys/my_getopt.c b/mysys/my_getopt.c index 2a4557118b0..3f75f6553a2 100644 --- a/mysys/my_getopt.c +++ b/mysys/my_getopt.c @@ -47,16 +47,17 @@ static char *check_struct_option(char *cur_arg, char *key_name); order of their arguments must correspond to each other. */ static const char *special_opt_prefix[]= -{"skip", "disable", "enable", "maximum", "loose", 0}; +{"skip", "disable", "enable", "maximum", "loose", "autoset", 0}; static const uint special_opt_prefix_lengths[]= -{ 4, 7, 6, 7, 5, 0}; +{ 4, 7, 6, 7, 5, 7, 0}; enum enum_special_opt -{ OPT_SKIP, OPT_DISABLE, OPT_ENABLE, OPT_MAXIMUM, OPT_LOOSE}; +{ OPT_SKIP, OPT_DISABLE, OPT_ENABLE, OPT_MAXIMUM, OPT_LOOSE, OPT_AUTOSET}; char *disabled_my_option= (char*) "0"; char *enabled_my_option= (char*) "1"; +char *autoset_my_option= (char*) "auto"; -/* +/* This is a flag that can be set in client programs. 0 means that my_getopt will not print error messages, but the client should do it by itself @@ -64,13 +65,21 @@ char *enabled_my_option= (char*) "1"; 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; + +/* + This is a flag that can be set in client programs. 1 means that + my_getopt will reconize command line options by their unambiguous + prefixes. 0 means an option must be always specified in full. +*/ +my_bool my_getopt_prefix_matching= 1; + static void default_reporter(enum loglevel level, const char *format, ...) { @@ -190,7 +199,7 @@ int handle_options(int *argc, char ***argv, { uint UNINIT_VAR(opt_found), argvpos= 0, length; my_bool end_of_options= 0, must_be_var, set_maximum_value, - option_is_loose; + option_is_loose, option_is_autoset; char **pos, **pos_end, *optend, *opt_str, key_name[FN_REFLEN]; const char *UNINIT_VAR(prev_found); const struct my_option *optp; @@ -241,6 +250,7 @@ int handle_options(int *argc, char ***argv, must_be_var= 0; set_maximum_value= 0; option_is_loose= 0; + option_is_autoset= 0; cur_arg++; /* skip '-' */ if (*cur_arg == '-') /* check for long option, */ @@ -289,6 +299,8 @@ int handle_options(int *argc, char ***argv, length-= special_opt_prefix_lengths[i] + 1; if (i == OPT_LOOSE) option_is_loose= 1; + else if (i == OPT_AUTOSET) + option_is_autoset= 1; if ((opt_found= findopt(opt_str, length, &optp, &prev_found))) { if (opt_found > 1) @@ -450,6 +462,36 @@ int handle_options(int *argc, char ***argv, } argument= optend; } + else if (option_is_autoset) + { + if (optend) + { + my_getopt_error_reporter(ERROR_LEVEL, + "%s: automatically set " + "option '--%s' cannot take an argument", + my_progname, optp->name); + + DBUG_RETURN(EXIT_NO_ARGUMENT_ALLOWED); + } + /* + We support automatic setup only via get_one_option and only for + marked options. + */ + if (!get_one_option || + !(optp->var_type & GET_AUTO)) + { + my_getopt_error_reporter(option_is_loose ? + WARNING_LEVEL : ERROR_LEVEL, + "%s: automatic setup request is " + "unsupported by option '--%s'", + my_progname, optp->name); + if (!option_is_loose) + DBUG_RETURN(EXIT_ARGUMENT_INVALID); + continue; + } + else + argument= autoset_my_option; + } else if (optp->arg_type == REQUIRED_ARG && !optend) { /* Check if there are more arguments after this one, @@ -577,7 +619,8 @@ int handle_options(int *argc, char ***argv, (*argc)--; /* option handled (short), decrease argument count */ continue; } - if (((error= setval(optp, value, argument, set_maximum_value))) && + if ((!option_is_autoset) && + ((error= setval(optp, value, argument, set_maximum_value))) && !option_is_loose) DBUG_RETURN(error); if (get_one_option && get_one_option(optp->id, optp, argument)) @@ -851,6 +894,9 @@ static int findopt(char *optpat, uint length, if (!opt->name[length]) /* Exact match */ DBUG_RETURN(1); + if (!my_getopt_prefix_matching) + continue; + if (!count) { /* We only need to know one prev */ @@ -867,6 +913,14 @@ static int findopt(char *optpat, uint length, } } } + + if (count == 1) + my_getopt_error_reporter(INFORMATION_LEVEL, + "Using unique option prefix '%.*s' is error-prone " + "and can break in the future. " + "Please use the full name '%s' instead.", + length, optpat, *ffname); + DBUG_RETURN(count); } @@ -1259,7 +1313,8 @@ void my_cleanup_options(const struct my_option *options) SYNOPSIS init_variables() - options Array of options + options Array of options + func_init_one_value Call this function to init the variable NOTES We will initialize the value that is pointed to by options->value. @@ -1268,7 +1323,7 @@ void my_cleanup_options(const struct my_option *options) */ static void init_variables(const struct my_option *options, - init_func_p init_one_value) + init_func_p func_init_one_value) { DBUG_ENTER("init_variables"); for (; options->name; options++) @@ -1281,11 +1336,11 @@ static void init_variables(const struct my_option *options, set the value to default value. */ if (options->u_max_value) - init_one_value(options, options->u_max_value, options->max_value); + func_init_one_value(options, options->u_max_value, options->max_value); value= (options->var_type & GET_ASK_ADDR ? (*getopt_get_addr)("", 0, options, 0) : options->value); if (value) - init_one_value(options, value, options->def_value); + func_init_one_value(options, value, options->def_value); } DBUG_VOID_RETURN; } @@ -1300,6 +1355,48 @@ static uint print_name(const struct my_option *optp) return s - optp->name; } +/** prints option comment with indentation and wrapping. + + The comment column starts at startpos, and has width of width + Current cursor position is curpos, returns new cursor position + + @note can print one character beyond width! +*/ +static uint print_comment(const char *comment, + int curpos, int startpos, int width) +{ + const char *end= strend(comment); + int endpos= startpos + width; + + for (; curpos < startpos; curpos++) + putchar(' '); + + if (*comment == '.' || *comment == ',') + { + putchar(*comment); + comment++; + curpos++; + } + + while (end - comment > endpos - curpos) + { + const char *line_end; + for (line_end= comment + endpos - curpos; + line_end > comment && *line_end != ' '; + line_end--); + for (; comment < line_end; comment++) + putchar(*comment); + while (*comment == ' ') + comment++; /* skip the space, as a newline will take it's place now */ + putchar('\n'); + for (curpos= 0; curpos < startpos; curpos++) + putchar(' '); + } + printf("%s", comment); + return curpos + (end - comment); +} + + /* function: my_print_options @@ -1309,12 +1406,12 @@ static uint print_name(const struct my_option *optp) void my_print_help(const struct my_option *options) { uint col, name_space= 22, comment_space= 57; - const char *line_end; const struct my_option *optp; DBUG_ENTER("my_print_help"); for (optp= options; optp->name; optp++) { + const char *typelib_help= 0; if (!optp->comment) continue; if (optp->id && optp->id < 256) @@ -1353,29 +1450,51 @@ void my_print_help(const struct my_option *options) optp->arg_type == OPT_ARG ? "]" : ""); col+= (optp->arg_type == OPT_ARG) ? 5 : 3; } - if (col > name_space && optp->comment && *optp->comment) - { - putchar('\n'); - col= 0; - } } - for (; col < name_space; col++) - putchar(' '); if (optp->comment && *optp->comment) { - const char *comment= optp->comment, *end= strend(comment); + uint count; - while ((uint) (end - comment) > comment_space) + if (col > name_space) { - for (line_end= comment + comment_space; *line_end != ' '; line_end--); - for (; comment != line_end; comment++) - putchar(*comment); - comment++; /* skip the space, as a newline will take it's place now */ putchar('\n'); - for (col= 0; col < name_space; col++) - putchar(' '); + col= 0; + } + + col= print_comment(optp->comment, col, name_space, comment_space); + if (optp->var_type & GET_AUTO) + { + col= print_comment(" (Automatically configured unless set explicitly)", + col, name_space, comment_space); + } + + switch (optp->var_type & GET_TYPE_MASK) { + case GET_ENUM: + typelib_help= ". One of: "; + count= optp->typelib->count; + break; + case GET_SET: + typelib_help= ". Any combination of: "; + count= optp->typelib->count; + break; + case GET_FLAGSET: + typelib_help= ". Takes a comma-separated list of option=value pairs, " + "where value is on, off, or default, and options are: "; + count= optp->typelib->count - 1; + break; + } + if (typelib_help && + strstr(optp->comment, optp->typelib->type_names[0]) == NULL) + { + uint i; + col= print_comment(typelib_help, col, name_space, comment_space); + col= print_comment(optp->typelib->type_names[0], col, name_space, comment_space); + for (i= 1; i < count; i++) + { + col= print_comment(", ", col, name_space, comment_space); + col= print_comment(optp->typelib->type_names[i], col, name_space, comment_space); + } } - printf("%s", comment); } putchar('\n'); if ((optp->var_type & GET_TYPE_MASK) == GET_BOOL) diff --git a/mysys/my_init.c b/mysys/my_init.c index 2c06425f6fb..32289dbed7a 100644 --- a/mysys/my_init.c +++ b/mysys/my_init.c @@ -454,7 +454,8 @@ PSI_mutex_key key_LOCK_localtime_r; #endif /* !defined(HAVE_LOCALTIME_R) || !defined(HAVE_GMTIME_R) */ PSI_mutex_key key_BITMAP_mutex, key_IO_CACHE_append_buffer_lock, - key_IO_CACHE_SHARE_mutex, key_KEY_CACHE_cache_lock, key_LOCK_alarm, + key_IO_CACHE_SHARE_mutex, key_KEY_CACHE_cache_lock, + key_LOCK_alarm, key_LOCK_timer, key_my_thread_var_mutex, key_THR_LOCK_charset, key_THR_LOCK_heap, key_THR_LOCK_lock, key_THR_LOCK_malloc, key_THR_LOCK_mutex, key_THR_LOCK_myisam, key_THR_LOCK_net, @@ -474,6 +475,7 @@ static PSI_mutex_info all_mysys_mutexes[]= { &key_IO_CACHE_SHARE_mutex, "IO_CACHE::SHARE_mutex", 0}, { &key_KEY_CACHE_cache_lock, "KEY_CACHE::cache_lock", 0}, { &key_LOCK_alarm, "LOCK_alarm", PSI_FLAG_GLOBAL}, + { &key_LOCK_timer, "LOCK_timer", PSI_FLAG_GLOBAL}, { &key_my_thread_var_mutex, "my_thread_var::mutex", 0}, { &key_THR_LOCK_charset, "THR_LOCK_charset", PSI_FLAG_GLOBAL}, { &key_THR_LOCK_heap, "THR_LOCK_heap", PSI_FLAG_GLOBAL}, @@ -489,13 +491,14 @@ static PSI_mutex_info all_mysys_mutexes[]= { &key_LOCK_uuid_generator, "LOCK_uuid_generator", PSI_FLAG_GLOBAL } }; -PSI_cond_key key_COND_alarm, key_IO_CACHE_SHARE_cond, +PSI_cond_key key_COND_alarm, key_COND_timer, key_IO_CACHE_SHARE_cond, key_IO_CACHE_SHARE_cond_writer, key_my_thread_var_suspend, key_THR_COND_threads, key_WT_RESOURCE_cond; static PSI_cond_info all_mysys_conds[]= { { &key_COND_alarm, "COND_alarm", PSI_FLAG_GLOBAL}, + { &key_COND_timer, "COND_timer", PSI_FLAG_GLOBAL}, { &key_IO_CACHE_SHARE_cond, "IO_CACHE_SHARE::cond", 0}, { &key_IO_CACHE_SHARE_cond_writer, "IO_CACHE_SHARE::cond_writer", 0}, { &key_my_thread_var_suspend, "my_thread_var::suspend", 0}, @@ -512,12 +515,17 @@ static PSI_rwlock_info all_mysys_rwlocks[]= #ifdef USE_ALARM_THREAD PSI_thread_key key_thread_alarm; +#endif +PSI_thread_key key_thread_timer; static PSI_thread_info all_mysys_threads[]= { - { &key_thread_alarm, "alarm", PSI_FLAG_GLOBAL} +#ifdef USE_ALARM_THREAD + { &key_thread_alarm, "alarm", PSI_FLAG_GLOBAL}, +#endif + { &key_thread_timer, "statement_timer", PSI_FLAG_GLOBAL} }; -#endif /* USE_ALARM_THREAD */ + #ifdef HUGETLB_USE_PROC_MEMINFO PSI_file_key key_file_proc_meminfo; @@ -552,10 +560,8 @@ void my_init_mysys_psi_keys() count= sizeof(all_mysys_rwlocks)/sizeof(all_mysys_rwlocks[0]); mysql_rwlock_register(category, all_mysys_rwlocks, count); -#ifdef USE_ALARM_THREAD count= sizeof(all_mysys_threads)/sizeof(all_mysys_threads[0]); mysql_thread_register(category, all_mysys_threads, count); -#endif /* USE_ALARM_THREAD */ count= sizeof(all_mysys_files)/sizeof(all_mysys_files[0]); mysql_file_register(category, all_mysys_files, count); diff --git a/mysys/my_open.c b/mysys/my_open.c index 5263ba4b5c8..b6d8f08bfc1 100644 --- a/mysys/my_open.c +++ b/mysys/my_open.c @@ -46,9 +46,9 @@ File my_open(const char *FileName, int Flags, myf MyFlags) #if defined(_WIN32) fd= my_win_open(FileName, Flags); #elif !defined(NO_OPEN_3) - fd = open(FileName, Flags, my_umask); /* Normal unix */ + fd = open(FileName, Flags | O_CLOEXEC, my_umask); /* Normal unix */ #else - fd = open((char *) FileName, Flags); + fd = open((char *) FileName, Flags | O_CLOEXEC); #endif fd= my_register_filename(fd, FileName, FILE_BY_OPEN, diff --git a/mysys/my_rdtsc.c b/mysys/my_rdtsc.c index ad11e8c6a6c..4228973caa8 100644 --- a/mysys/my_rdtsc.c +++ b/mysys/my_rdtsc.c @@ -249,6 +249,13 @@ ulonglong my_timer_cycles(void) clock_gettime(CLOCK_SGI_CYCLE, &tp); return (ulonglong) tp.tv_sec * 1000000000 + (ulonglong) tp.tv_nsec; } +#elif defined(__GNUC__) && defined(__s390__) + /* covers both s390 and s390x */ + { + ulonglong result; + __asm__ __volatile__ ("stck %0" : "=Q" (result) : : "cc"); + return result; + } #elif defined(HAVE_SYS_TIMES_H) && defined(HAVE_GETHRTIME) /* gethrtime may appear as either cycle or nanosecond counter */ return (ulonglong) gethrtime(); @@ -558,6 +565,8 @@ void my_timer_init(MY_TIMER_INFO *mti) mti->cycles.routine= MY_TIMER_ROUTINE_ASM_GCC_SPARC32; #elif defined(__sgi) && defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_SGI_CYCLE) mti->cycles.routine= MY_TIMER_ROUTINE_SGI_CYCLE; +#elif defined(__GNUC__) && defined(__s390__) + mti->cycles.routine= MY_TIMER_ROUTINE_ASM_S390; #elif defined(HAVE_SYS_TIMES_H) && defined(HAVE_GETHRTIME) mti->cycles.routine= MY_TIMER_ROUTINE_GETHRTIME; #else @@ -753,7 +762,6 @@ void my_timer_init(MY_TIMER_INFO *mti) mti->cycles.frequency= mti->microseconds.frequency; else { - ulonglong time1, time2; time1= my_timer_init_frequency(mti); /* Repeat once in case there was an interruption. */ time2= my_timer_init_frequency(mti); @@ -775,8 +783,7 @@ void my_timer_init(MY_TIMER_INFO *mti) && mti->microseconds.routine && mti->cycles.routine) { - int i; - ulonglong time1, time2, time3, time4; + ulonglong time3, time4; time1= my_timer_cycles(); time2= my_timer_milliseconds(); time3= time2; /* Avoids a Microsoft/IBM compiler warning */ @@ -801,8 +808,7 @@ void my_timer_init(MY_TIMER_INFO *mti) && mti->microseconds.routine && mti->cycles.routine) { - int i; - ulonglong time1, time2, time3, time4; + ulonglong time3, time4; time1= my_timer_cycles(); time2= my_timer_ticks(); time3= time2; /* Avoids a Microsoft/IBM compiler warning */ diff --git a/mysys/mysys_priv.h b/mysys/mysys_priv.h index 9c6855bb92f..d080aca7404 100644 --- a/mysys/mysys_priv.h +++ b/mysys/mysys_priv.h @@ -13,8 +13,14 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#ifndef MYSYS_PRIV_INCLUDED +#define MYSYS_PRIV_INCLUDED + #include <my_global.h> #include <my_sys.h> +#include <my_crypt.h> + +C_MODE_START #ifdef USE_SYSTEM_WRAPPERS #include "system_wrappers.h" @@ -42,16 +48,16 @@ extern PSI_mutex_key key_BITMAP_mutex, key_IO_CACHE_append_buffer_lock, key_THR_LOCK_lock, key_THR_LOCK_malloc, key_THR_LOCK_mutex, key_THR_LOCK_myisam, key_THR_LOCK_net, key_THR_LOCK_open, key_THR_LOCK_threads, key_LOCK_uuid_generator, - key_TMPDIR_mutex, key_THR_LOCK_myisam_mmap; + key_TMPDIR_mutex, key_THR_LOCK_myisam_mmap, key_LOCK_timer; -extern PSI_cond_key key_COND_alarm, key_IO_CACHE_SHARE_cond, +extern PSI_cond_key key_COND_alarm, key_COND_timer, key_IO_CACHE_SHARE_cond, key_IO_CACHE_SHARE_cond_writer, key_my_thread_var_suspend, key_THR_COND_threads; #ifdef USE_ALARM_THREAD extern PSI_thread_key key_thread_alarm; #endif /* USE_ALARM_THREAD */ - +extern PSI_thread_key key_thread_timer; extern PSI_rwlock_key key_SAFEHASH_mutex; #endif /* HAVE_PSI_INTERFACE */ @@ -71,6 +77,16 @@ extern PSI_file_key key_file_proc_meminfo; extern PSI_file_key key_file_charset, key_file_cnf; #endif /* HAVE_PSI_INTERFACE */ +typedef struct { + ulonglong counter; + uint block_length, last_block_length; + uchar key[MY_AES_BLOCK_SIZE]; + ulonglong inbuf_counter; +} IO_CACHE_CRYPT; + +extern int (*_my_b_encr_read)(IO_CACHE *info,uchar *Buffer,size_t Count); +extern int (*_my_b_encr_write)(IO_CACHE *info,const uchar *Buffer,size_t Count); + #ifdef SAFEMALLOC void *sf_malloc(size_t size, myf my_flags); void *sf_realloc(void *ptr, size_t size, myf my_flags); @@ -116,3 +132,7 @@ extern File my_win_dup(File fd); extern File my_win_sopen(const char *path, int oflag, int shflag, int perm); extern File my_open_osfhandle(HANDLE handle, int oflag); #endif + +C_MODE_END + +#endif diff --git a/mysys/ptr_cmp.c b/mysys/ptr_cmp.c index 6e373e98972..6d853a8db25 100644 --- a/mysys/ptr_cmp.c +++ b/mysys/ptr_cmp.c @@ -88,8 +88,8 @@ qsort2_cmp get_ptr_compare (size_t size) static int ptr_compare(size_t *compare_length, uchar **a, uchar **b) { - reg3 int length= *compare_length; - reg1 uchar *first,*last; + size_t length= *compare_length; + uchar *first,*last; DBUG_ASSERT(length > 0); first= *a; last= *b; @@ -104,8 +104,8 @@ static int ptr_compare(size_t *compare_length, uchar **a, uchar **b) static int ptr_compare_0(size_t *compare_length,uchar **a, uchar **b) { - reg3 int length= *compare_length; - reg1 uchar *first,*last; + size_t length= *compare_length; + uchar *first,*last; first= *a; last= *b; loop: @@ -125,8 +125,8 @@ static int ptr_compare_0(size_t *compare_length,uchar **a, uchar **b) static int ptr_compare_1(size_t *compare_length,uchar **a, uchar **b) { - reg3 int length= *compare_length-1; - reg1 uchar *first,*last; + size_t length= *compare_length-1; + uchar *first,*last; first= *a+1; last= *b+1; cmp(-1); @@ -146,8 +146,8 @@ static int ptr_compare_1(size_t *compare_length,uchar **a, uchar **b) static int ptr_compare_2(size_t *compare_length,uchar **a, uchar **b) { - reg3 int length= *compare_length-2; - reg1 uchar *first,*last; + size_t length= *compare_length-2; + uchar *first,*last; first= *a +2 ; last= *b +2; cmp(-2); @@ -168,8 +168,8 @@ static int ptr_compare_2(size_t *compare_length,uchar **a, uchar **b) static int ptr_compare_3(size_t *compare_length,uchar **a, uchar **b) { - reg3 int length= *compare_length-3; - reg1 uchar *first,*last; + size_t length= *compare_length-3; + uchar *first,*last; first= *a +3 ; last= *b +3; cmp(-3); diff --git a/mysys/stacktrace.c b/mysys/stacktrace.c index 746b99d6112..395659238b3 100644 --- a/mysys/stacktrace.c +++ b/mysys/stacktrace.c @@ -336,12 +336,11 @@ inline uint32* find_prev_pc(uint32* pc, uchar** fp) void my_print_stacktrace(uchar* stack_bottom, ulong thread_stack) { - uchar** fp; + uchar** UNINIT_VAR(fp); uint frame_count = 0, sigreturn_frame_count; #if defined(__alpha__) && defined(__GNUC__) uint32* pc; #endif - LINT_INIT(fp); #ifdef __i386__ diff --git a/mysys/thr_lock.c b/mysys/thr_lock.c index 8dce58dd58a..8990cbd5a14 100644 --- a/mysys/thr_lock.c +++ b/mysys/thr_lock.c @@ -95,6 +95,24 @@ my_bool thr_lock_inited=0; ulong locks_immediate = 0L, locks_waited = 0L; enum thr_lock_type thr_upgraded_concurrent_insert_lock = TL_WRITE; +#ifdef WITH_WSREP +static wsrep_thd_is_brute_force_fun wsrep_thd_is_brute_force= NULL; +static wsrep_abort_thd_fun wsrep_abort_thd= NULL; +static my_bool wsrep_debug; +static my_bool wsrep_convert_LOCK_to_trx; +static wsrep_on_fun wsrep_on = NULL; + +void wsrep_thr_lock_init( + wsrep_thd_is_brute_force_fun bf_fun, wsrep_abort_thd_fun abort_fun, + my_bool debug, my_bool convert_LOCK_to_trx, wsrep_on_fun on_fun +) { + wsrep_thd_is_brute_force = bf_fun; + wsrep_abort_thd = abort_fun; + wsrep_debug = debug; + wsrep_convert_LOCK_to_trx= convert_LOCK_to_trx; + wsrep_on = on_fun; +} +#endif /* The following constants are only for debug output */ #define MAX_THREADS 1000 #define MAX_LOCKS 1000 @@ -327,15 +345,14 @@ static void check_locks(THR_LOCK *lock, const char *where, "Warning at '%s': Write lock %d waiting while no exclusive read locks\n",where,(int) lock->write_wait.data->type); DBUG_PRINT("warning", ("Warning at '%s': Write lock %d waiting while no exclusive read locks",where,(int) lock->write_wait.data->type)); } - } + } } else { /* We have at least one write lock */ if (lock->write.data->type == TL_WRITE_CONCURRENT_INSERT) { - THR_LOCK_DATA *data; - uint count= 0; + count= 0; for (data=lock->write.data->next; data && count < MAX_LOCKS; data=data->next) @@ -368,7 +385,6 @@ static void check_locks(THR_LOCK *lock, const char *where, } if (lock->read.data) { - THR_LOCK_DATA *data; for (data=lock->read.data ; data ; data=data->next) { if (!thr_lock_owner_equal(lock->write.data->owner, @@ -646,6 +662,92 @@ wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data, DBUG_RETURN(result); } +#ifdef WITH_WSREP +/* + * If brute force applier would need to wait for a thr lock, + * it needs to make sure that it will get the lock without (too much) + * delay. + * We identify here the owners of blocking locks and ask them to + * abort. We then put our lock request in the first place in the + * wait queue. When lock holders abort (one by one) the lock release + * algorithm should grant the lock to us. We rely on this and proceed + * to wait_for_locks(). + * wsrep_break_locks() should be called in all the cases, where lock + * wait would happen. + * + * TODO: current implementation might not cover all possible lock wait + * situations. This needs an review still. + * TODO: lock release, might favor some other lock (instead our bf). + * This needs an condition to check for bf locks first. + * TODO: we still have a debug fprintf, this should be removed + */ +static my_bool +wsrep_break_lock( + THR_LOCK_DATA *data, struct st_lock_list *lock_queue1, + struct st_lock_list *wait_queue) +{ + if (wsrep_on && wsrep_on(data->owner->mysql_thd) && + wsrep_thd_is_brute_force && + wsrep_thd_is_brute_force(data->owner->mysql_thd, TRUE)) + { + THR_LOCK_DATA *holder; + + /* if locking session conversion to transaction has been enabled, + we know that this conflicting lock must be read lock and furthermore, + lock holder is read-only. It is safe to wait for him. + */ +#ifdef TODO_WHEN_LOCK_TABLES_IS_A_TRANSACTION + if (wsrep_convert_LOCK_to_trx && + (THD*)(data->owner->mysql_thd)->in_lock_tables) + { + if (wsrep_debug) + fprintf(stderr,"WSREP wsrep_break_lock read lock untouched\n"); + return FALSE; + } +#endif + if (wsrep_debug) + fprintf(stderr,"WSREP wsrep_break_lock aborting locks\n"); + + /* aborting lock holder(s) here */ + for (holder=(lock_queue1) ? lock_queue1->data : NULL; + holder; + holder=holder->next) + { + if (!wsrep_thd_is_brute_force(holder->owner->mysql_thd, TRUE)) + { + wsrep_abort_thd(data->owner->mysql_thd, + holder->owner->mysql_thd, FALSE); + } + else + { + if (wsrep_debug) + fprintf(stderr,"WSREP wsrep_break_lock skipping BF lock conflict\n"); + return FALSE; + } + } + + /* Add our lock to the head of the wait queue */ + if (*(wait_queue->last)==wait_queue->data) + { + wait_queue->last=&data->next; + assert(wait_queue->data==0); + } + else + { + assert(wait_queue->data!=0); + wait_queue->data->prev=&data->next; + } + data->next=wait_queue->data; + data->prev=&wait_queue->data; + wait_queue->data=data; + data->cond=get_cond(); + + statistic_increment(locks_immediate,&THR_LOCK_lock); + return TRUE; + } + return FALSE; +} +#endif static enum enum_thr_lock_result thr_lock(THR_LOCK_DATA *data, THR_LOCK_INFO *owner, ulong lock_wait_timeout) @@ -654,6 +756,9 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_INFO *owner, ulong lock_wait_timeout) enum enum_thr_lock_result result= THR_LOCK_SUCCESS; struct st_lock_list *wait_queue; enum thr_lock_type lock_type= data->type; +#ifdef WITH_WSREP + my_bool wsrep_lock_inserted= FALSE; +#endif MYSQL_TABLE_WAIT_VARIABLES(locker, state) /* no ';' */ DBUG_ENTER("thr_lock"); @@ -750,6 +855,13 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_INFO *owner, ulong lock_wait_timeout) lock but a high priority write waiting in the write_wait queue. In the latter case we should yield the lock to the writer. */ +#ifdef WITH_WSREP + if (wsrep_break_lock(data, &lock->write, &lock->read_wait)) + { + wsrep_lock_inserted= TRUE; + } +#endif + wait_queue= &lock->read_wait; } else /* Request for WRITE lock */ @@ -894,9 +1006,20 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_INFO *owner, ulong lock_wait_timeout) DBUG_PRINT("lock",("write locked 3 by thread: 0x%lx type: %d", lock->read.data->owner->thread_id, data->type)); } +#ifdef WITH_WSREP + if (wsrep_break_lock(data, &lock->write, &lock->write_wait)) + { + wsrep_lock_inserted= TRUE; + } +#endif + wait_queue= &lock->write_wait; } /* Can't get lock yet; Wait for it */ +#ifdef WITH_WSREP + if (wsrep_lock_inserted && wsrep_on(data->owner->mysql_thd)) + DBUG_RETURN(wait_for_lock(wait_queue, data, 1, lock_wait_timeout)); +#endif result= wait_for_lock(wait_queue, data, 0, lock_wait_timeout); MYSQL_END_TABLE_LOCK_WAIT(locker); DBUG_RETURN(result); diff --git a/mysys/thr_timer.c b/mysys/thr_timer.c new file mode 100644 index 00000000000..b8726617f44 --- /dev/null +++ b/mysys/thr_timer.c @@ -0,0 +1,570 @@ +/* + Copyright (c) 2012 Monty Program 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 or later 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +/* + Implementation if OS independent timers. + This is done based on pthread primitives, especially pthread_cond_timedwait() +*/ + +#include "mysys_priv.h" +#include "thr_timer.h" +#include <m_string.h> +#include <queues.h> +#ifdef HAVE_TIMER_CREATE +#include <sys/syscall.h> +#endif + +struct timespec next_timer_expire_time; + +static my_bool thr_timer_inited= 0; +static mysql_mutex_t LOCK_timer; +static mysql_cond_t COND_timer; +static QUEUE timer_queue; +pthread_t timer_thread; + +#define set_max_time(abs_time) \ + { (abs_time)->MY_tv_sec= INT_MAX32; (abs_time)->MY_tv_nsec= 0; } + + +static void *timer_handler(void *arg __attribute__((unused))); + +/* + Compare two timespecs +*/ + +static int compare_timespec(void *not_used __attribute__((unused)), + uchar *a_ptr, uchar *b_ptr) +{ + return cmp_timespec((*(struct timespec*) a_ptr), + (*(struct timespec*) b_ptr)); +} + + +/** + Initialize timer variables and create timer thread + + @param alloc_timers Init allocation of timers. Will be autoextended + if needed + @return 0 ok + @return 1 error; Can't create thread +*/ + +static thr_timer_t max_timer_data; + +my_bool init_thr_timer(uint alloc_timers) +{ + pthread_attr_t thr_attr; + my_bool res= 0; + DBUG_ENTER("init_thr_timer"); + + init_queue(&timer_queue, alloc_timers+2, offsetof(thr_timer_t,expire_time), + 0, compare_timespec, NullS, + offsetof(thr_timer_t, index_in_queue)+1, 1); + mysql_mutex_init(key_LOCK_timer, &LOCK_timer, MY_MUTEX_INIT_FAST); + mysql_cond_init(key_COND_timer, &COND_timer, NULL); + + /* Set dummy element with max time into the queue to simplify usage */ + bzero(&max_timer_data, sizeof(max_timer_data)); + set_max_time(&max_timer_data.expire_time); + queue_insert(&timer_queue, (uchar*) &max_timer_data); + next_timer_expire_time= max_timer_data.expire_time; + + /* Create a thread to handle timers */ + pthread_attr_init(&thr_attr); + pthread_attr_setscope(&thr_attr,PTHREAD_SCOPE_PROCESS); + pthread_attr_setstacksize(&thr_attr,8196); + thr_timer_inited= 1; + if (mysql_thread_create(key_thread_timer, &timer_thread, &thr_attr, + timer_handler, NULL)) + { + thr_timer_inited= 0; + res= 1; + mysql_mutex_destroy(&LOCK_timer); + mysql_cond_destroy(&COND_timer); + delete_queue(&timer_queue); + } + pthread_attr_destroy(&thr_attr); + + DBUG_RETURN(res); +} + + +void end_thr_timer(void) +{ + DBUG_ENTER("end_thr_timer"); + + if (!thr_timer_inited) + DBUG_VOID_RETURN; + + mysql_mutex_lock(&LOCK_timer); + thr_timer_inited= 0; /* Signal abort */ + mysql_cond_signal(&COND_timer); + mysql_mutex_unlock(&LOCK_timer); + pthread_join(timer_thread, NULL); + + mysql_mutex_destroy(&LOCK_timer); + mysql_cond_destroy(&COND_timer); + delete_queue(&timer_queue); + DBUG_VOID_RETURN; +} + + +/* + Initialize a timer object + + @param timer_data Timer structure + @param function Function to be called when getting timeout + @param argument Argument for function +*/ + +void thr_timer_init(thr_timer_t *timer_data, void(*function)(void*), + void *arg) +{ + DBUG_ENTER("thr_timer_init"); + bzero(timer_data, sizeof(*timer_data)); + timer_data->func= function; + timer_data->func_arg= arg; + timer_data->expired= 1; /* Not active */ + DBUG_VOID_RETURN; +} + + +/* + Request timer after X milliseconds + + SYNOPSIS + thr_timer() + timer_data Pointer to timer structure + micro_seconds; Number of microseconds until timer + + RETURN VALUES + 0 ok + 1 If no more timers are allowed (aborted by process) + + Stores in first argument a pointer to a non-zero int which is set to 0 + when the timer has been given +*/ + +my_bool thr_timer_settime(thr_timer_t *timer_data, ulonglong micro_seconds) +{ + int reschedule; + DBUG_ENTER("thr_timer_settime"); + DBUG_PRINT("enter",("thread: %s micro_seconds: %llu",my_thread_name(), + micro_seconds)); + + DBUG_ASSERT(timer_data->expired == 1); + + set_timespec_nsec(timer_data->expire_time, micro_seconds*1000); + timer_data->expired= 0; + + mysql_mutex_lock(&LOCK_timer); /* Lock from threads & timers */ + if (queue_insert_safe(&timer_queue,(uchar*) timer_data)) + { + DBUG_PRINT("info", ("timer queue full")); + fprintf(stderr,"Warning: thr_timer queue is full\n"); + timer_data->expired= 1; + mysql_mutex_unlock(&LOCK_timer); + DBUG_RETURN(1); + } + + /* Reschedule timer if the current one has more time left than new one */ + reschedule= cmp_timespec(next_timer_expire_time, timer_data->expire_time); + mysql_mutex_unlock(&LOCK_timer); + if (reschedule > 0) + { +#if defined(MAIN) + printf("reschedule\n"); fflush(stdout); +#endif + DBUG_PRINT("info", ("reschedule")); + mysql_cond_signal(&COND_timer); + } + + DBUG_RETURN(0); +} + + +/* + Remove timer from list of timers + + notes: Timer will be marked as expired +*/ + +void thr_timer_end(thr_timer_t *timer_data) +{ + DBUG_ENTER("thr_timer_end"); + + mysql_mutex_lock(&LOCK_timer); + if (!timer_data->expired) + { + DBUG_ASSERT(timer_data->index_in_queue != 0); + DBUG_ASSERT(queue_element(&timer_queue, timer_data->index_in_queue) == + (uchar*) timer_data); + queue_remove(&timer_queue, timer_data->index_in_queue); + /* Mark as expired for asserts to work */ + timer_data->expired= 1; + } + mysql_mutex_unlock(&LOCK_timer); + DBUG_VOID_RETURN; +} + + +/* + Come here when some timer in queue is due. +*/ + +static sig_handler process_timers(struct timespec *now) +{ + thr_timer_t *timer_data; + DBUG_ENTER("process_timers"); + DBUG_PRINT("info",("active timers: %d", timer_queue.elements - 1)); + +#if defined(MAIN) + printf("process_timer\n"); fflush(stdout); +#endif + + /* We can safely remove the first one as it has already expired */ + for (;;) + { + void (*function)(void*); + void *func_arg; + + timer_data= (thr_timer_t*) queue_top(&timer_queue); + function= timer_data->func; + func_arg= timer_data->func_arg; + timer_data->expired= 1; /* Mark expired */ + /* + We remove timer before calling timer function to allow thread to + delete it's timer data any time. + */ + queue_remove_top(&timer_queue); /* Remove timer */ + (*function)(func_arg); /* Inform thread of timeout */ + + /* Check if next one has also expired */ + timer_data= (thr_timer_t*) queue_top(&timer_queue); + if (cmp_timespec(timer_data->expire_time, (*now)) > 0) + break; /* All data processed */ + } + DBUG_VOID_RETURN; +} + + +/* + set up a timer thread to handle timeouts + This will be killed when thr_timer_inited is set to false. +*/ + +static void *timer_handler(void *arg __attribute__((unused))) +{ + my_thread_init(); + + mysql_mutex_lock(&LOCK_timer); + while (likely(thr_timer_inited)) + { + int error; + struct timespec *top_time; + struct timespec now, abstime; + + set_timespec(now, 0); + + top_time= &(((thr_timer_t*) queue_top(&timer_queue))->expire_time); + + if (cmp_timespec((*top_time), now) <= 0) + { + process_timers(&now); + top_time= &(((thr_timer_t*) queue_top(&timer_queue))->expire_time); + } + + abstime= *top_time; + next_timer_expire_time= *top_time; + if ((error= mysql_cond_timedwait(&COND_timer, &LOCK_timer, &abstime)) && + error != ETIME && error != ETIMEDOUT) + { +#ifdef MAIN + printf("Got error: %d from ptread_cond_timedwait (errno: %d)\n", + error,errno); +#endif + } + } + mysql_mutex_unlock(&LOCK_timer); + my_thread_end(); + pthread_exit(0); + return 0; /* Impossible */ +} + + +/**************************************************************************** + Testing of thr_timer (when compiled with -DMAIN) +***************************************************************************/ + +#ifdef MAIN + +static mysql_cond_t COND_thread_count; +static mysql_mutex_t LOCK_thread_count; +static uint thread_count, benchmark_runs, test_to_run= 1; + +static void send_signal(void *arg) +{ + struct st_my_thread_var *current_my_thread_var= arg; +#if defined(MAIN) + printf("sending signal\n"); fflush(stdout); +#endif + mysql_mutex_lock(¤t_my_thread_var->mutex); + mysql_cond_signal(¤t_my_thread_var->suspend); + mysql_mutex_unlock(¤t_my_thread_var->mutex); +} + + +static void run_thread_test(int param) +{ + int i,wait_time,retry; + my_hrtime_t start_time; + thr_timer_t timer_data; + struct st_my_thread_var *current_my_thread_var; + DBUG_ENTER("run_thread_test"); + + current_my_thread_var= my_thread_var; + thr_timer_init(&timer_data, send_signal, current_my_thread_var); + + for (i=1 ; i <= 10 ; i++) + { + wait_time=param ? 11-i : i; + start_time= my_hrtime(); + + mysql_mutex_lock(¤t_my_thread_var->mutex); + if (thr_timer_settime(&timer_data, wait_time * 1000000)) + { + printf("Thread: %s timers aborted\n",my_thread_name()); + break; + } + if (wait_time == 3) + { + printf("Thread: %s Simulation of no timer needed\n",my_thread_name()); + fflush(stdout); + } + else + { + for (retry=0 ; !timer_data.expired && retry < 10 ; retry++) + { + printf("Thread: %s Waiting %d sec\n",my_thread_name(),wait_time); + mysql_cond_wait(¤t_my_thread_var->suspend, + ¤t_my_thread_var->mutex); + + } + if (!timer_data.expired) + { + printf("Thread: %s didn't get an timer. Aborting!\n", + my_thread_name()); + break; + } + } + mysql_mutex_unlock(¤t_my_thread_var->mutex); + printf("Thread: %s Slept for %g (%d) sec\n",my_thread_name(), + (int) (my_hrtime().val-start_time.val)/1000000.0, wait_time); + fflush(stdout); + thr_timer_end(&timer_data); + fflush(stdout); + } + DBUG_VOID_RETURN; +} + + +static void run_thread_benchmark(int param) +{ + int i; + struct st_my_thread_var *current_my_thread_var; + thr_timer_t timer_data; + DBUG_ENTER("run_thread_benchmark"); + + current_my_thread_var= my_thread_var; + thr_timer_init(&timer_data, send_signal, current_my_thread_var); + + for (i=1 ; i <= param ; i++) + { + if (thr_timer_settime(&timer_data, 1000000)) + { + printf("Thread: %s timers aborted\n",my_thread_name()); + break; + } + thr_timer_end(&timer_data); + } + DBUG_VOID_RETURN; +} + + +#ifdef HAVE_TIMER_CREATE + +/* Test for benchmarking posix timers against thr_timer */ + +#ifndef sigev_notify_thread_id +#define sigev_notify_thread_id _sigev_un._tid +#endif + +static void run_timer_benchmark(int param) +{ + int i; + timer_t timerid; + struct sigevent sigev; + pid_t thread_id= (pid_t) syscall(SYS_gettid); + DBUG_ENTER("run_timer_benchmark"); + + /* Setup a signal that will never be signaled */ + sigev.sigev_value.sival_ptr= 0; + sigev.sigev_signo= SIGRTMIN; /* First free signal */ + sigev.sigev_notify= SIGEV_SIGNAL | SIGEV_THREAD_ID; + sigev.sigev_notify_thread_id= thread_id; + + if (timer_create(CLOCK_MONOTONIC, &sigev, &timerid)) + { + printf("Could not create timer\n"); + exit(1); + } + + for (i=1 ; i <= param ; i++) + { + struct itimerspec abstime; + abstime.it_interval.tv_sec= 0; + abstime.it_interval.tv_nsec= 0; + abstime.it_value.tv_sec= 1; + abstime.it_value.tv_nsec= 0; + + if (timer_settime(timerid, 0, &abstime, NULL)) + { + printf("Thread: %s timers aborted\n",my_thread_name()); + break; + } + abstime.it_interval.tv_sec= 0; + abstime.it_interval.tv_nsec= 0; + abstime.it_value.tv_sec= 0; + abstime.it_value.tv_nsec= 0; + timer_settime(timerid, 0, &abstime, NULL); + } + timer_delete(timerid); + DBUG_VOID_RETURN; +} +#endif /* HAVE_TIMER_CREATE */ + + +static void *start_thread(void *arg) +{ + my_thread_init(); + printf("Thread %d (%s) started\n",*((int*) arg),my_thread_name()); + fflush(stdout); + + switch (test_to_run) { + case 1: + run_thread_test(*((int*) arg)); + break; + case 2: + run_thread_benchmark(benchmark_runs); + break; + case 3: +#ifdef HAVE_TIMER_CREATE + run_timer_benchmark(benchmark_runs); +#endif + break; + } + free((uchar*) arg); + mysql_mutex_lock(&LOCK_thread_count); + thread_count--; + mysql_cond_signal(&COND_thread_count); /* Tell main we are ready */ + mysql_mutex_unlock(&LOCK_thread_count); + my_thread_end(); + return 0; +} + + +/* Start a lot of threads that will run with timers */ + +static void run_test() +{ + pthread_t tid; + pthread_attr_t thr_attr; + int i,*param,error; + DBUG_ENTER("run_test"); + + if (init_thr_timer(5)) + { + printf("Can't initialize timers\n"); + exit(1); + } + + mysql_mutex_init(0, &LOCK_thread_count, MY_MUTEX_INIT_FAST); + mysql_cond_init(0, &COND_thread_count, NULL); + + thr_setconcurrency(3); + pthread_attr_init(&thr_attr); + pthread_attr_setscope(&thr_attr,PTHREAD_SCOPE_PROCESS); + printf("Main thread: %s\n",my_thread_name()); + for (i=0 ; i < 2 ; i++) + { + param=(int*) malloc(sizeof(int)); + *param= i; + mysql_mutex_lock(&LOCK_thread_count); + if ((error= mysql_thread_create(0, + &tid, &thr_attr, start_thread, + (void*) param))) + { + printf("Can't create thread %d, error: %d\n",i,error); + exit(1); + } + thread_count++; + mysql_mutex_unlock(&LOCK_thread_count); + } + + pthread_attr_destroy(&thr_attr); + mysql_mutex_lock(&LOCK_thread_count); + while (thread_count) + { + mysql_cond_wait(&COND_thread_count, &LOCK_thread_count); + } + mysql_mutex_unlock(&LOCK_thread_count); + DBUG_ASSERT(timer_queue.elements == 1); + end_thr_timer(); + printf("Test succeeded\n"); + DBUG_VOID_RETURN; +} + + +int main(int argc __attribute__((unused)),char **argv __attribute__((unused))) +{ + MY_INIT(argv[0]); + + if (argc > 1 && argv[1][0] == '-') + { + switch (argv[1][1]) { + case '#': + test_to_run= 1; + DBUG_PUSH(argv[1]+2); + break; + case 'b': + test_to_run= 2; + benchmark_runs= atoi(argv[1]+2); + break; + case 't': + test_to_run= 3; + benchmark_runs= atoi(argv[1]+2); + break; + } + } + if (!benchmark_runs) + benchmark_runs= 1000000; + + run_test(); + my_end(1); + return 0; +} + +#endif /* MAIN */ diff --git a/mysys/waiting_threads.c b/mysys/waiting_threads.c index 1fe6a0f9a1c..ae0ffe7f7eb 100644 --- a/mysys/waiting_threads.c +++ b/mysys/waiting_threads.c @@ -192,19 +192,12 @@ uint32 wt_wait_stats[WT_WAIT_STATS+1]; uint32 wt_cycle_stats[2][WT_CYCLE_STATS+1]; uint32 wt_success_stats; -static my_atomic_rwlock_t cycle_stats_lock, wait_stats_lock, success_stats_lock; - #ifdef HAVE_PSI_INTERFACE extern PSI_cond_key key_WT_RESOURCE_cond; #endif #ifdef SAFE_STATISTICS -#define incr(VAR, LOCK) \ - do { \ - my_atomic_rwlock_wrlock(&(LOCK)); \ - my_atomic_add32(&(VAR), 1); \ - my_atomic_rwlock_wrunlock(&(LOCK)); \ - } while(0) +#define incr(VAR, LOCK) do { my_atomic_add32(&(VAR), 1); } while(0) #else #define incr(VAR,LOCK) do { (VAR)++; } while(0) #endif @@ -260,11 +253,7 @@ struct st_wt_resource { #ifndef DBUG_OFF mysql_mutex_t *cond_mutex; /* a mutex for the 'cond' below */ #endif - /* - before the 'lock' all elements are mutable, after (and including) - - immutable in the sense that lf_hash_insert() won't memcpy() over them. - See wt_init(). - */ + #ifdef WT_RWLOCKS_USE_MUTEXES /* we need a special rwlock-like 'lock' to allow readers bypass @@ -396,10 +385,10 @@ static LF_HASH reshash; It's called from lf_hash and takes a pointer to an LF_SLIST instance. WT_RESOURCE is located at arg+sizeof(LF_SLIST) */ -static void wt_resource_init(uchar *arg) +static void wt_resource_create(uchar *arg) { WT_RESOURCE *rc= (WT_RESOURCE*)(arg+LF_HASH_OVERHEAD); - DBUG_ENTER("wt_resource_init"); + DBUG_ENTER("wt_resource_create"); bzero(rc, sizeof(*rc)); rc_rwlock_init(rc); @@ -426,25 +415,37 @@ static void wt_resource_destroy(uchar *arg) DBUG_VOID_RETURN; } +/** + WT_RESOURCE initializer + + It's called from lf_hash when an element is inserted. +*/ +static void wt_resource_init(LF_HASH *hash __attribute__((unused)), + WT_RESOURCE *rc, WT_RESOURCE_ID *id) +{ + DBUG_ENTER("wt_resource_init"); + rc->id= *id; + rc->waiter_count= 0; + rc->state= ACTIVE; +#ifndef DBUG_OFF + rc->cond_mutex= 0; +#endif + DBUG_VOID_RETURN; +} + static int wt_init_done; void wt_init() { DBUG_ENTER("wt_init"); - DBUG_ASSERT(reshash.alloc.constructor != wt_resource_init); + DBUG_ASSERT(reshash.alloc.constructor != wt_resource_create); lf_hash_init(&reshash, sizeof(WT_RESOURCE), LF_HASH_UNIQUE, 0, sizeof_WT_RESOURCE_ID, 0, 0); - reshash.alloc.constructor= wt_resource_init; + reshash.alloc.constructor= wt_resource_create; reshash.alloc.destructor= wt_resource_destroy; - /* - Note a trick: we initialize the hash with the real element size, - but fix it later to a shortened element size. This way - the allocator will allocate elements correctly, but - lf_hash_insert() will only overwrite part of the element with memcpy(). - lock, condition, and dynamic array will be intact. - */ - reshash.element_size= offsetof(WT_RESOURCE, lock); + reshash.initializer= (lf_hash_initializer) wt_resource_init; + bzero(wt_wait_stats, sizeof(wt_wait_stats)); bzero(wt_cycle_stats, sizeof(wt_cycle_stats)); wt_success_stats= 0; @@ -458,9 +459,6 @@ void wt_init() DBUG_ASSERT(i == 0 || wt_wait_table[i-1] != wt_wait_table[i]); } } - my_atomic_rwlock_init(&cycle_stats_lock); - my_atomic_rwlock_init(&success_stats_lock); - my_atomic_rwlock_init(&wait_stats_lock); wt_init_done= 1; DBUG_VOID_RETURN; } @@ -473,9 +471,6 @@ void wt_end() DBUG_ASSERT(reshash.count == 0); lf_hash_destroy(&reshash); - my_atomic_rwlock_destroy(&cycle_stats_lock); - my_atomic_rwlock_destroy(&success_stats_lock); - my_atomic_rwlock_destroy(&wait_stats_lock); reshash.alloc.constructor= NULL; wt_init_done= 0; DBUG_VOID_RETURN; @@ -943,14 +938,9 @@ int wt_thd_will_wait_for(WT_THD *thd, WT_THD *blocker, retry: while ((rc= lf_hash_search(&reshash, thd->pins, key, keylen)) == 0) { - WT_RESOURCE tmp; - DBUG_PRINT("wt", ("failed to find rc in hash, inserting")); - bzero(&tmp, sizeof(tmp)); - tmp.id= *resid; - tmp.state= ACTIVE; - if (lf_hash_insert(&reshash, thd->pins, &tmp) == -1) /* if OOM */ + if (lf_hash_insert(&reshash, thd->pins, resid) == -1) /* if OOM */ DBUG_RETURN(WT_DEADLOCK); /* Two cases: either lf_hash_insert() failed - because another thread |