diff options
Diffstat (limited to 'mysys')
58 files changed, 1209 insertions, 719 deletions
diff --git a/mysys/CMakeLists.txt b/mysys/CMakeLists.txt index 22d758d45a5..09d3f726ffc 100644 --- a/mysys/CMakeLists.txt +++ b/mysys/CMakeLists.txt @@ -37,17 +37,18 @@ SET(MYSYS_SOURCES array.c charset-def.c charset.c checksum.c my_default.c string.c thr_alarm.c thr_lock.c thr_mutex.c thr_rwlock.c thr_timer.c tree.c typelib.c base64.c my_memmem.c - my_getpagesize.c + my_getpagesize.c + guess_malloc_library.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 + my_getncpus.c my_safehash.c my_chmod.c my_rnd.c my_uuid.c wqueue.c waiting_threads.c ma_dyncol.c ../sql-common/my_time.c my_rdtsc.c my_context.c psi_noop.c - my_atomic_writes.c + my_atomic_writes.c my_cpu.c my_likely.c file_logger.c my_dlerror.c) IF (WIN32) - SET (MYSYS_SOURCES ${MYSYS_SOURCES} my_winthread.c my_wincond.c my_winerr.c my_winfile.c my_windac.c my_conio.c) + SET (MYSYS_SOURCES ${MYSYS_SOURCES} my_winthread.c my_wincond.c my_winerr.c my_winfile.c my_windac.c my_conio.c my_minidump.cc) ENDIF() IF(UNIX) @@ -73,16 +74,20 @@ IF(HAVE_MLOCK) ENDIF() ADD_CONVENIENCE_LIBRARY(mysys ${MYSYS_SOURCES}) -TARGET_LINK_LIBRARIES(mysys dbug strings - ${ZLIB_LIBRARY} ${LIBNSL} ${LIBM} ${LIBRT} ${LIBDL} ${LIBSOCKET} ${LIBEXECINFO} ${CRC32_VPMSUM_LIBRARY}) +TARGET_LINK_LIBRARIES(mysys dbug strings ${ZLIB_LIBRARY} + ${LIBNSL} ${LIBM} ${LIBRT} ${CMAKE_DL_LIBS} ${LIBSOCKET} ${LIBEXECINFO} ${CRC32_LIBRARY}) DTRACE_INSTRUMENT(mysys) +IF (HAVE_GCC_C11_ATOMICS_WITH_LIBATOMIC) + TARGET_LINK_LIBRARIES(mysys atomic) +ENDIF() + IF(HAVE_BFD_H) TARGET_LINK_LIBRARIES(mysys bfd) ENDIF(HAVE_BFD_H) IF (WIN32) - TARGET_LINK_LIBRARIES(mysys IPHLPAPI) + TARGET_LINK_LIBRARIES(mysys iphlpapi dbghelp) ENDIF(WIN32) # Need explicit pthread for gcc -fsanitize=address @@ -101,7 +106,3 @@ SET_TARGET_PROPERTIES(thr_timer PROPERTIES COMPILE_FLAGS "-DMAIN") ADD_EXECUTABLE(test_hash hash.c) TARGET_LINK_LIBRARIES(test_hash mysys) SET_TARGET_PROPERTIES(test_hash PROPERTIES COMPILE_FLAGS "-DMAIN") - -IF(MSVC) - INSTALL_DEBUG_TARGET(mysys DESTINATION ${INSTALL_LIBDIR}/debug) -ENDIF() diff --git a/mysys/file_logger.c b/mysys/file_logger.c index a4cfe94a9a2..476ed44089e 100644 --- a/mysys/file_logger.c +++ b/mysys/file_logger.c @@ -150,23 +150,34 @@ exit: } +/* + Return 1 if we should rotate the log +*/ + +my_bool logger_time_to_rotate(LOGGER_HANDLE *log) +{ + my_off_t filesize; + if (log->rotations > 0 && + (filesize= my_tell(log->file, MYF(0))) != (my_off_t) -1 && + ((ulonglong) filesize >= log->size_limit)) + return 1; + return 0; +} + + int logger_vprintf(LOGGER_HANDLE *log, const char* fmt, va_list ap) { int result; - my_off_t filesize; char cvtbuf[1024]; size_t n_bytes; flogger_mutex_lock(&log->lock); - if (log->rotations > 0) - if ((filesize= my_tell(log->file, MYF(0))) == (my_off_t) -1 || - ((unsigned long long)filesize >= log->size_limit && - do_rotate(log))) - { - result= -1; - errno= my_errno; - goto exit; /* Log rotation needed but failed */ - } + if (logger_time_to_rotate(log) && do_rotate(log)) + { + result= -1; + errno= my_errno; + goto exit; /* Log rotation needed but failed */ + } n_bytes= my_vsnprintf(cvtbuf, sizeof(cvtbuf), fmt, ap); if (n_bytes >= sizeof(cvtbuf)) @@ -180,21 +191,18 @@ exit: } -int logger_write(LOGGER_HANDLE *log, const char *buffer, size_t size) +static int logger_write_r(LOGGER_HANDLE *log, my_bool allow_rotations, + const char *buffer, size_t size) { int result; - my_off_t filesize; flogger_mutex_lock(&log->lock); - if (log->rotations > 0) - if ((filesize= my_tell(log->file, MYF(0))) == (my_off_t) -1 || - ((unsigned long long)filesize >= log->size_limit && - do_rotate(log))) - { - result= -1; - errno= my_errno; - goto exit; /* Log rotation needed but failed */ - } + if (allow_rotations && logger_time_to_rotate(log) && do_rotate(log)) + { + result= -1; + errno= my_errno; + goto exit; /* Log rotation needed but failed */ + } result= (int)my_write(log->file, (uchar *) buffer, size, MYF(0)); @@ -204,6 +212,11 @@ exit: } +int logger_write(LOGGER_HANDLE *log, const char *buffer, size_t size) +{ + return logger_write_r(log, TRUE, buffer, size); +} + int logger_rotate(LOGGER_HANDLE *log) { int result; @@ -227,7 +240,7 @@ int logger_printf(LOGGER_HANDLE *log, const char *fmt, ...) void logger_init_mutexes() { #ifdef HAVE_PSI_INTERFACE - if (PSI_server) + if (unlikely(PSI_server)) PSI_server->register_mutex("sql_logger", mutex_list, 1); #endif } diff --git a/mysys/get_password.c b/mysys/get_password.c index 04c5537fc35..e20800f4213 100644 --- a/mysys/get_password.c +++ b/mysys/get_password.c @@ -64,7 +64,6 @@ char *get_tty_password(const char *opt_message) { char to[80]; char *pos=to,*end=to+sizeof(to)-1; - int i=0; DBUG_ENTER("get_tty_password"); _cputs(opt_message ? opt_message : "Enter password: "); for (;;) diff --git a/mysys/guess_malloc_library.c b/mysys/guess_malloc_library.c new file mode 100644 index 00000000000..ed86ae0679c --- /dev/null +++ b/mysys/guess_malloc_library.c @@ -0,0 +1,65 @@ +/* Copyright (c) 2002, 2015, Oracle and/or its affiliates. + Copyright (c) 2012, 2017, MariaDB Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +/* guess_malloc_library() deduces, to the best of its ability, + the currently used malloc library and its version */ + +#include <stddef.h> +#include "my_global.h" +#include <m_string.h> + +typedef const char* (*tc_version_type)(int*, int*, const char**); +typedef int (*mallctl_type)(const char*, void*, size_t*, void*, size_t); + +char *guess_malloc_library() +{ + tc_version_type tc_version_func; + mallctl_type mallctl_func; +#ifndef HAVE_DLOPEN + return (char*) MALLOC_LIBRARY; +#else + static char buf[128]; + + if (strcmp(MALLOC_LIBRARY, "system") != 0) + { + return (char*) MALLOC_LIBRARY; + } + + /* tcmalloc */ + tc_version_func= (tc_version_type) dlsym(RTLD_DEFAULT, "tc_version"); + if (tc_version_func) + { + int major, minor; + const char* ver_str = tc_version_func(&major, &minor, NULL); + strxnmov(buf, sizeof(buf)-1, "tcmalloc ", ver_str, NULL); + return buf; + } + + /* jemalloc */ + mallctl_func= (mallctl_type) dlsym(RTLD_DEFAULT, "mallctl"); + if (mallctl_func) + { + char *ver; + size_t len = sizeof(ver); + mallctl_func("version", &ver, &len, NULL, 0); + strxnmov(buf, sizeof(buf)-1, "jemalloc ", ver, NULL); + return buf; + } + + return (char*) MALLOC_LIBRARY; +#endif +} + diff --git a/mysys/hash.c b/mysys/hash.c index d5903df40bd..d29297a5233 100644 --- a/mysys/hash.c +++ b/mysys/hash.c @@ -386,7 +386,7 @@ static int hashcmp(const HASH *hash, HASH_LINK *pos, const uchar *key, my_bool my_hash_insert(HASH *info, const uchar *record) { int flag; - uint idx, halfbuff, first_index; + size_t idx, halfbuff, first_index; size_t length; my_hash_value_type current_hash_nr, UNINIT_VAR(rec_hash_nr), UNINIT_VAR(rec2_hash_nr); diff --git a/mysys/lf_alloc-pin.c b/mysys/lf_alloc-pin.c index 880ef44e9ca..280cf74e657 100644 --- a/mysys/lf_alloc-pin.c +++ b/mysys/lf_alloc-pin.c @@ -151,7 +151,6 @@ void lf_pinbox_destroy(LF_PINBOX *pinbox) */ LF_PINS *lf_pinbox_get_pins(LF_PINBOX *pinbox) { - struct st_my_thread_var *var; uint32 pins, next, top_ver; LF_PINS *el; /* @@ -161,7 +160,7 @@ LF_PINS *lf_pinbox_get_pins(LF_PINBOX *pinbox) pinstack_top_ver is 32 bits; 16 low bits are the index in the array, to the first element of the list. 16 high bits are a version (every time the 16 low bits are updated, the 16 high bits are - incremented). Versioniong prevents the ABA problem. + incremented). Versioning prevents the ABA problem. */ top_ver= pinbox->pinstack_top_ver; do @@ -194,12 +193,7 @@ LF_PINS *lf_pinbox_get_pins(LF_PINBOX *pinbox) el->link= pins; el->purgatory_count= 0; el->pinbox= pinbox; - var= my_thread_var; - /* - Threads that do not call my_thread_init() should still be - able to use the LF_HASH. - */ - el->stack_ends_here= (var ? & var->stack_ends_here : NULL); + return el; } @@ -336,16 +330,18 @@ static void lf_pinbox_real_free(LF_PINS *pins) void *list; void **addr= NULL; void *first= NULL, *last= NULL; + struct st_my_thread_var *var= my_thread_var; + void *stack_ends_here= var ? var->stack_ends_here : NULL; LF_PINBOX *pinbox= pins->pinbox; npins= pinbox->pins_in_array+1; #ifdef HAVE_ALLOCA - if (pins->stack_ends_here != NULL) + if (stack_ends_here != NULL) { int alloca_size= sizeof(void *)*LF_PINBOX_PINS*npins; /* create a sorted list of pinned addresses, to speed up searches */ - if (available_stack_size(&pinbox, *pins->stack_ends_here) > + if (available_stack_size(&pinbox, stack_ends_here) > alloca_size + ALLOCA_SAFETY_MARGIN) { struct st_harvester hv; @@ -431,7 +427,7 @@ static void alloc_free(uchar *first, { anext_node(last)= tmp.node; } while (!my_atomic_casptr((void **)(char *)&allocator->top, - (void **)&tmp.ptr, first) && LF_BACKOFF); + (void **)&tmp.ptr, first) && LF_BACKOFF()); } /* @@ -502,7 +498,7 @@ void *lf_alloc_new(LF_PINS *pins) { node= allocator->top; lf_pin(pins, 0, node); - } while (node != allocator->top && LF_BACKOFF); + } while (node != allocator->top && LF_BACKOFF()); if (!node) { node= (void *)my_malloc(allocator->element_size, MYF(MY_WME)); diff --git a/mysys/lf_hash.c b/mysys/lf_hash.c index ca3a377190f..8bff2e3bfa4 100644 --- a/mysys/lf_hash.c +++ b/mysys/lf_hash.c @@ -86,12 +86,12 @@ typedef struct { 1 - error (callbck returned 1) */ static int l_find(LF_SLIST **head, CHARSET_INFO *cs, uint32 hashnr, - const uchar *key, uint keylen, CURSOR *cursor, LF_PINS *pins, + const uchar *key, size_t keylen, CURSOR *cursor, LF_PINS *pins, my_hash_walk_action callback) { uint32 cur_hashnr; const uchar *cur_key; - uint cur_keylen; + size_t cur_keylen; intptr link; DBUG_ASSERT(!cs || !callback); /* should not be set both */ @@ -103,7 +103,7 @@ retry: cursor->curr= (LF_SLIST *)(*cursor->prev); lf_pin(pins, 1, cursor->curr); } while (my_atomic_loadptr((void**)cursor->prev) != cursor->curr && - LF_BACKOFF); + LF_BACKOFF()); for (;;) { if (unlikely(!cursor->curr)) @@ -119,8 +119,8 @@ retry: MY_MEMORY_ORDER_RELAXED); cursor->next= PTR(link); lf_pin(pins, 0, cursor->next); - } while (link != (intptr) my_atomic_loadptr((void *) &cursor->curr->link) - && LF_BACKOFF); + } while (link != (intptr) my_atomic_loadptr((void **) &cursor->curr->link) + && LF_BACKOFF()); if (!DELETED(link)) { @@ -148,7 +148,7 @@ retry: and remove this deleted node */ if (my_atomic_casptr((void **) cursor->prev, - (void **) &cursor->curr, cursor->next) && LF_BACKOFF) + (void **) &cursor->curr, cursor->next) && LF_BACKOFF()) lf_alloc_free(pins, cursor->curr); else goto retry; diff --git a/mysys/ma_dyncol.c b/mysys/ma_dyncol.c index fb163899a76..0085c375aa1 100644 --- a/mysys/ma_dyncol.c +++ b/mysys/ma_dyncol.c @@ -3822,11 +3822,11 @@ static my_bool dynstr_append_json_quoted(DYNAMIC_STRING *str, const char *append, size_t len) { - uint additional= ((str->alloc_increment && str->alloc_increment > 6) ? + size_t additional= ((str->alloc_increment && str->alloc_increment > 6) ? str->alloc_increment : 10); - uint lim= additional; - uint i; + size_t lim= additional; + size_t i; if (dynstr_realloc(str, len + additional + 2)) return TRUE; str->str[str->length++]= '"'; diff --git a/mysys/mf_cache.c b/mysys/mf_cache.c index 0e8a01266c1..dba5f5b831c 100644 --- a/mysys/mf_cache.c +++ b/mysys/mf_cache.c @@ -21,36 +21,6 @@ #include "mysys_err.h" /** - Remove an open tempfile so that it doesn't survive if we crash - - If the operating system doesn't support this, just remember - the file name for later removal -*/ - -static my_bool cache_remove_open_tmp(IO_CACHE *cache __attribute__((unused)), - const char *name) -{ -#if O_TEMPORARY == 0 -#if !defined(CANT_DELETE_OPEN_FILES) - /* The following should always succeed */ - (void) my_delete(name,MYF(MY_WME | ME_NOINPUT)); -#else - int length; - if (!(cache->file_name= - (char*) my_malloc((length=strlen(name)+1),MYF(MY_WME)))) - { - my_close(cache->file,MYF(0)); - cache->file = -1; - errno=my_errno=ENOMEM; - return 1; - } - memcpy(cache->file_name,name,length); -#endif -#endif /* O_TEMPORARY == 0 */ - return 0; -} - -/** Open tempfile cached by IO_CACHE Should be used when no seeks are done (only reinit_io_buff) @@ -88,14 +58,11 @@ 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, + 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) + O_BINARY, MYF(MY_WME | MY_TEMPORARY))) >= 0) { error=0; - cache_remove_open_tmp(cache, name_buff); } DBUG_RETURN(error); } diff --git a/mysys/mf_iocache.c b/mysys/mf_iocache.c index 0b19aaf9015..33930316f08 100644 --- a/mysys/mf_iocache.c +++ b/mysys/mf_iocache.c @@ -566,7 +566,7 @@ int _my_b_read(IO_CACHE *info, uchar *Buffer, size_t Count) } res= info->read_function(info, Buffer, Count); if (res && info->error >= 0) - info->error+= left_length; /* update number or read bytes */ + info->error+= (int)left_length; /* update number or read bytes */ return res; } @@ -1934,13 +1934,12 @@ int my_b_flush_io_cache(IO_CACHE *info, int need_append_buffer_lock) { if (append_cache) { - if (mysql_file_write(info->file, info->write_buffer, length, info->myflags | MY_NABP)) + { info->error= -1; - else - info->error= 0; - + DBUG_RETURN(-1); + } 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))); diff --git a/mysys/mf_iocache2.c b/mysys/mf_iocache2.c index ea426347c18..4622b68646e 100644 --- a/mysys/mf_iocache2.c +++ b/mysys/mf_iocache2.c @@ -255,18 +255,16 @@ my_off_t my_b_filelength(IO_CACHE *info) } -size_t +my_bool my_b_write_backtick_quote(IO_CACHE *info, const char *str, size_t len) { const uchar *start; const uchar *p= (const uchar *)str; const uchar *end= p + len; size_t count; - size_t total= 0; if (my_b_write(info, (uchar *)"`", 1)) - return (size_t)-1; - ++total; + return 1; for (;;) { start= p; @@ -274,35 +272,32 @@ my_b_write_backtick_quote(IO_CACHE *info, const char *str, size_t len) ++p; count= p - start; if (count && my_b_write(info, start, count)) - return (size_t)-1; - total+= count; + return 1; if (p >= end) break; if (my_b_write(info, (uchar *)"``", 2)) - return (size_t)-1; - total+= 2; + return 1; ++p; } - if (my_b_write(info, (uchar *)"`", 1)) - return (size_t)-1; - ++total; - return total; + return (my_b_write(info, (uchar *)"`", 1)); } /* Simple printf version. Supports '%s', '%d', '%u', "%ld" and "%lu" - Used for logging in MySQL - returns number of written character, or (size_t) -1 on error + Used for logging in MariaDB + + @return 0 ok + 1 error */ -size_t my_b_printf(IO_CACHE *info, const char* fmt, ...) +my_bool my_b_printf(IO_CACHE *info, const char* fmt, ...) { size_t result; va_list args; va_start(args,fmt); result=my_b_vprintf(info, fmt, args); va_end(args); - return result; + return result == (size_t) -1; } diff --git a/mysys/mf_keycache.c b/mysys/mf_keycache.c index aab26384be7..8f62869b57e 100644 --- a/mysys/mf_keycache.c +++ b/mysys/mf_keycache.c @@ -421,11 +421,12 @@ static int keycache_pthread_cond_signal(mysql_cond_t *cond); #undef inline #endif #define inline /* disabled inline for easier debugging */ -static int fail_block(BLOCK_LINK *block); static int fail_hlink(HASH_LINK *hlink); static int cache_empty(SIMPLE_KEY_CACHE_CB *keycache); #endif - +#ifdef DBUG_ASSERT_EXISTS +static int fail_block(BLOCK_LINK *block); +#endif static inline uint next_power(uint value) { @@ -713,7 +714,7 @@ int prepare_resize_simple_key_cache(SIMPLE_KEY_CACHE_CB *keycache, res= 1; goto finish; } - DBUG_ASSERT(cache_empty(keycache)); + DBUG_SLOW_ASSERT(cache_empty(keycache)); /* End the flush phase. */ keycache->resize_in_flush= 0; @@ -748,7 +749,6 @@ finish: SYNOPSIS finish_resize_simple_key_cache() keycache pointer to the control block of a simple key cache - acquire_lock <=> acquire the key cache lock at start DESCRIPTION This function performs finalizing actions for the operation of @@ -756,8 +756,6 @@ finish: keycache as a pointer to the control block structure of the type SIMPLE_KEY_CACHE_CB for this key cache. The function sets the flag in_resize in this structure to FALSE. - The parameter acquire_lock says whether the key cache lock must be - acquired at the start of the function. RETURN VALUE none @@ -769,14 +767,10 @@ finish: */ static -void finish_resize_simple_key_cache(SIMPLE_KEY_CACHE_CB *keycache, - my_bool acquire_lock) +void finish_resize_simple_key_cache(SIMPLE_KEY_CACHE_CB *keycache) { DBUG_ENTER("finish_resize_simple_key_cache"); - if (acquire_lock) - keycache_pthread_mutex_lock(&keycache->cache_lock); - mysql_mutex_assert_owner(&keycache->cache_lock); /* @@ -848,8 +842,7 @@ int resize_simple_key_cache(SIMPLE_KEY_CACHE_CB *keycache, int blocks= 0; DBUG_ENTER("resize_simple_key_cache"); - if (!keycache->key_cache_inited) - DBUG_RETURN(blocks); + DBUG_ASSERT(keycache->key_cache_inited); /* Note that the cache_lock mutex and the resize_queue are left untouched. @@ -865,7 +858,7 @@ int resize_simple_key_cache(SIMPLE_KEY_CACHE_CB *keycache, changed_blocks_hash_size); finish: - finish_resize_simple_key_cache(keycache, 0); + finish_resize_simple_key_cache(keycache); DBUG_RETURN(blocks); } @@ -1088,7 +1081,7 @@ static void unlink_from_queue(KEYCACHE_WQUEUE *wqueue, thread->prev); } thread->next= NULL; -#if !defined(DBUG_OFF) +#ifdef DBUG_ASSERT_EXISTS /* This makes it easier to see it's not in a chain during debugging. And some DBUG_ASSERT() rely on it. @@ -1211,7 +1204,7 @@ static inline void unlink_changed(BLOCK_LINK *block) block->next_changed->prev_changed= block->prev_changed; *block->prev_changed= block->next_changed; -#if !defined(DBUG_OFF) +#ifdef DBUG_ASSERT_EXISTS /* This makes it easier to see it's not in a chain during debugging. And some DBUG_ASSERT() rely on it. @@ -1497,7 +1490,7 @@ static void unlink_block(SIMPLE_KEY_CACHE_CB *keycache, BLOCK_LINK *block) keycache->used_ins=STRUCT_PTR(BLOCK_LINK, next_used, block->prev_used); } block->next_used= NULL; -#if !defined(DBUG_OFF) +#ifdef DBUG_ASSERT_EXISTS /* This makes it easier to see it's not in a chain during debugging. And some DBUG_ASSERT() rely on it. @@ -2436,7 +2429,7 @@ restart: The call is thread safe because only the current thread might change the block->hash_link value */ - error= my_pwrite(block->hash_link->file, + error= (int)my_pwrite(block->hash_link->file, block->buffer + block->offset, block->length - block->offset, block->hash_link->diskpos + block->offset, @@ -2610,12 +2603,11 @@ restart: SYNOPSIS - read_block() + read_block_{primary|secondary}() keycache pointer to a key cache data structure block block to which buffer the data is to be read read_length size of data to be read min_length at least so much data must be read - primary <-> the current thread will read the data RETURN VALUE None @@ -2629,90 +2621,100 @@ restart: portion is less than read_length, but not less than min_length. */ -static void read_block(SIMPLE_KEY_CACHE_CB *keycache, - BLOCK_LINK *block, uint read_length, - uint min_length, my_bool primary) +static void read_block_primary(SIMPLE_KEY_CACHE_CB *keycache, + BLOCK_LINK *block, uint read_length, + uint min_length) { size_t got_length; /* On entry cache_lock is locked */ - KEYCACHE_THREAD_TRACE("read_block"); - if (primary) - { - /* - This code is executed only by threads that submitted primary - requests. Until block->status contains BLOCK_READ, all other - request for the block become secondary requests. For a primary - request the block must be properly initialized. - */ - DBUG_ASSERT(((block->status & ~BLOCK_FOR_UPDATE) == BLOCK_IN_USE) || - fail_block(block)); - DBUG_ASSERT((block->length == 0) || fail_block(block)); - DBUG_ASSERT((block->offset == keycache->key_cache_block_size) || - fail_block(block)); - DBUG_ASSERT((block->requests > 0) || fail_block(block)); - - KEYCACHE_DBUG_PRINT("read_block", - ("page to be read by primary request")); - - keycache->global_cache_read++; - /* Page is not in buffer yet, is to be read from disk */ - keycache_pthread_mutex_unlock(&keycache->cache_lock); - /* - Here other threads may step in and register as secondary readers. - They will register in block->wqueue[COND_FOR_REQUESTED]. - */ - got_length= my_pread(block->hash_link->file, block->buffer, - read_length, block->hash_link->diskpos, MYF(0)); - keycache_pthread_mutex_lock(&keycache->cache_lock); - /* - The block can now have been marked for free (in case of - FLUSH_RELEASE). Otherwise the state must be unchanged. - */ - DBUG_ASSERT(((block->status & ~(BLOCK_REASSIGNED | - BLOCK_FOR_UPDATE)) == BLOCK_IN_USE) || - fail_block(block)); - DBUG_ASSERT((block->length == 0) || fail_block(block)); - DBUG_ASSERT((block->offset == keycache->key_cache_block_size) || - fail_block(block)); - DBUG_ASSERT((block->requests > 0) || fail_block(block)); - - if (got_length < min_length) - block->status|= BLOCK_ERROR; - else - { - block->status|= BLOCK_READ; - block->length= got_length; - /* - Do not set block->offset here. If this block is marked - BLOCK_CHANGED later, we want to flush only the modified part. So - only a writer may set block->offset down from - keycache->key_cache_block_size. - */ - } - KEYCACHE_DBUG_PRINT("read_block", - ("primary request: new page in cache")); - /* Signal that all pending requests for this page now can be processed */ - release_whole_queue(&block->wqueue[COND_FOR_REQUESTED]); - } + KEYCACHE_THREAD_TRACE("read_block_primary"); + + /* + This code is executed only by threads that submitted primary + requests. Until block->status contains BLOCK_READ, all other + request for the block become secondary requests. For a primary + request the block must be properly initialized. + */ + DBUG_ASSERT(((block->status & ~BLOCK_FOR_UPDATE) == BLOCK_IN_USE) || + fail_block(block)); + DBUG_ASSERT((block->length == 0) || fail_block(block)); + DBUG_ASSERT((block->offset == keycache->key_cache_block_size) || + fail_block(block)); + DBUG_ASSERT((block->requests > 0) || fail_block(block)); + + KEYCACHE_DBUG_PRINT("read_block_primary", + ("page to be read by primary request")); + + keycache->global_cache_read++; + /* Page is not in buffer yet, is to be read from disk */ + keycache_pthread_mutex_unlock(&keycache->cache_lock); + /* + Here other threads may step in and register as secondary readers. + They will register in block->wqueue[COND_FOR_REQUESTED]. + */ + got_length= my_pread(block->hash_link->file, block->buffer, + read_length, block->hash_link->diskpos, MYF(0)); + keycache_pthread_mutex_lock(&keycache->cache_lock); + /* + The block can now have been marked for free (in case of + FLUSH_RELEASE). Otherwise the state must be unchanged. + */ + DBUG_ASSERT(((block->status & ~(BLOCK_REASSIGNED | + BLOCK_FOR_UPDATE)) == BLOCK_IN_USE) || + fail_block(block)); + DBUG_ASSERT((block->length == 0) || fail_block(block)); + DBUG_ASSERT((block->offset == keycache->key_cache_block_size) || + fail_block(block)); + DBUG_ASSERT((block->requests > 0) || fail_block(block)); + + if (got_length < min_length) + block->status|= BLOCK_ERROR; else { + block->status|= BLOCK_READ; + block->length= (uint)got_length; /* - This code is executed only by threads that submitted secondary - requests. At this point it could happen that the cache block is - not yet assigned to the hash_link for the requested file block. - But at awake from the wait this should be the case. Unfortunately - we cannot assert this here because we do not know the hash_link - for the requested file block nor the file and position. So we have - to assert this in the caller. + Do not set block->offset here. If this block is marked + BLOCK_CHANGED later, we want to flush only the modified part. So + only a writer may set block->offset down from + keycache->key_cache_block_size. */ - KEYCACHE_DBUG_PRINT("read_block", - ("secondary request waiting for new page to be read")); - wait_on_queue(&block->wqueue[COND_FOR_REQUESTED], &keycache->cache_lock); - KEYCACHE_DBUG_PRINT("read_block", - ("secondary request: new page in cache")); } + KEYCACHE_DBUG_PRINT("read_block_primary", + ("primary request: new page in cache")); + /* Signal that all pending requests for this page now can be processed */ + release_whole_queue(&block->wqueue[COND_FOR_REQUESTED]); + + DBUG_ASSERT(keycache->can_be_used); +} + + +static void read_block_secondary(SIMPLE_KEY_CACHE_CB *keycache, + BLOCK_LINK *block) +{ + KEYCACHE_THREAD_TRACE("read_block_secondary"); + + /* + This code is executed only by threads that submitted secondary + requests. At this point it could happen that the cache block is + not yet assigned to the hash_link for the requested file block. + But at awake from the wait this should be the case. Unfortunately + we cannot assert this here because we do not know the hash_link + for the requested file block nor the file and position. So we have + to assert this in the caller. + */ + KEYCACHE_DBUG_PRINT("read_block_secondary", + ("secondary request waiting for new page to be read")); + + wait_on_queue(&block->wqueue[COND_FOR_REQUESTED], &keycache->cache_lock); + + KEYCACHE_DBUG_PRINT("read_block_secondary", + ("secondary request: new page in cache")); + + DBUG_ASSERT(keycache->can_be_used); + DBUG_ASSERT(block->status & (BLOCK_READ | BLOCK_IN_USE)); } @@ -2857,22 +2859,24 @@ uchar *simple_key_cache_read(SIMPLE_KEY_CACHE_CB *keycache, } if (!(block->status & BLOCK_ERROR)) { - if (page_st != PAGE_READ) + if (page_st == PAGE_TO_BE_READ) + { + MYSQL_KEYCACHE_READ_MISS(); + read_block_primary(keycache, block, + keycache->key_cache_block_size, read_length+offset); + } + else if (page_st == PAGE_WAIT_TO_BE_READ) { MYSQL_KEYCACHE_READ_MISS(); /* The requested page is to be read into the block buffer */ - read_block(keycache, block, - keycache->key_cache_block_size, read_length+offset, - (my_bool)(page_st == PAGE_TO_BE_READ)); + read_block_secondary(keycache, block); + /* A secondary request must now have the block assigned to the - requested file block. It does not hurt to check it for - primary requests too. + requested file block. */ - DBUG_ASSERT(keycache->can_be_used); DBUG_ASSERT(block->hash_link->file == file); DBUG_ASSERT(block->hash_link->diskpos == filepos); - DBUG_ASSERT(block->status & (BLOCK_READ | BLOCK_IN_USE)); } else if (block->length < read_length + offset) { @@ -3076,23 +3080,30 @@ int simple_key_cache_insert(SIMPLE_KEY_CACHE_CB *keycache, } if (!(block->status & BLOCK_ERROR)) { - if ((page_st == PAGE_WAIT_TO_BE_READ) || - ((page_st == PAGE_TO_BE_READ) && - (offset || (read_length < keycache->key_cache_block_size)))) + if (page_st == PAGE_WAIT_TO_BE_READ) { /* - Either - this is a secondary request for a block to be read into the cache. The block is in eviction. It is not yet assigned to the requested file block (It does not point to the right hash_link). So we cannot call remove_reader() on the block. And we cannot access the hash_link directly here. We need to - wait until the assignment is complete. read_block() executes - the correct wait when called with primary == FALSE. - - Or + wait until the assignment is complete. read_block_secondary() + executes the correct wait. + */ + read_block_secondary(keycache, block); + /* + A secondary request must now have the block assigned to the + requested file block. + */ + DBUG_ASSERT(block->hash_link->file == file); + DBUG_ASSERT(block->hash_link->diskpos == filepos); + } + else if (page_st == PAGE_TO_BE_READ && + (offset || (read_length < keycache->key_cache_block_size))) + { + /* this is a primary request for a block to be read into the cache and the supplied data does not fill the whole block. @@ -3107,17 +3118,8 @@ int simple_key_cache_insert(SIMPLE_KEY_CACHE_CB *keycache, Though reading again what the caller did read already is an expensive operation, we need to do this for correctness. */ - read_block(keycache, block, keycache->key_cache_block_size, - read_length + offset, (page_st == PAGE_TO_BE_READ)); - /* - A secondary request must now have the block assigned to the - requested file block. It does not hurt to check it for - primary requests too. - */ - DBUG_ASSERT(keycache->can_be_used); - DBUG_ASSERT(block->hash_link->file == file); - DBUG_ASSERT(block->hash_link->diskpos == filepos); - DBUG_ASSERT(block->status & (BLOCK_READ | BLOCK_IN_USE)); + read_block_primary(keycache, block, keycache->key_cache_block_size, + read_length + offset); } else if (page_st == PAGE_TO_BE_READ) { @@ -3412,25 +3414,29 @@ int simple_key_cache_write(SIMPLE_KEY_CACHE_CB *keycache, reading the file block. If the read completes after us, it overwrites our new contents with the old contents. So we have to wait for the other thread to complete the read of this block. - read_block() takes care for the wait. + read_block_primary|secondary() takes care for the wait. */ - if (!(block->status & BLOCK_ERROR) && - ((page_st == PAGE_TO_BE_READ && - (offset || read_length < keycache->key_cache_block_size)) || - (page_st == PAGE_WAIT_TO_BE_READ))) + if (!(block->status & BLOCK_ERROR)) { - read_block(keycache, block, - offset + read_length >= keycache->key_cache_block_size? - offset : keycache->key_cache_block_size, - offset, (page_st == PAGE_TO_BE_READ)); - DBUG_ASSERT(keycache->can_be_used); - DBUG_ASSERT(block->status & (BLOCK_READ | BLOCK_IN_USE)); - /* - Prevent block from flushing and from being selected for to be - freed. This must be set when we release the cache_lock. - Here we set it in case we could not set it above. - */ - block->status|= BLOCK_FOR_UPDATE; + if (page_st == PAGE_TO_BE_READ && + (offset || read_length < keycache->key_cache_block_size)) + { + read_block_primary(keycache, block, + offset + read_length >= keycache->key_cache_block_size? + offset : keycache->key_cache_block_size, + offset); + /* + Prevent block from flushing and from being selected for to be + freed. This must be set when we release the cache_lock. + Here we set it in case we could not set it above. + */ + block->status|= BLOCK_FOR_UPDATE; + } + else if (page_st == PAGE_WAIT_TO_BE_READ) + { + read_block_secondary(keycache, block); + block->status|= BLOCK_FOR_UPDATE; + } } /* The block should always be assigned to the requested file block @@ -3803,7 +3809,7 @@ static int flush_cached_blocks(SIMPLE_KEY_CACHE_CB *keycache, (BLOCK_READ | BLOCK_IN_FLUSH | BLOCK_CHANGED | BLOCK_IN_USE)); block->status|= BLOCK_IN_FLUSHWRITE; keycache_pthread_mutex_unlock(&keycache->cache_lock); - error= my_pwrite(file, block->buffer + block->offset, + error= (int)my_pwrite(file, block->buffer + block->offset, block->length - block->offset, block->hash_link->diskpos + block->offset, MYF(MY_NABP | MY_WAIT_IF_FULL)); @@ -4309,10 +4315,8 @@ restart: } /* if (keycache->disk_blocks > 0 */ -#ifndef DBUG_OFF DBUG_EXECUTE("check_keycache", test_key_cache(keycache, "end of flush_key_blocks", 0);); -#endif err: if (cache != cache_buff) my_free(cache); @@ -4790,11 +4794,12 @@ void keycache_debug_log_close(void) #endif /* defined(KEYCACHE_DEBUG) */ -#if !defined(DBUG_OFF) +#ifdef DBUG_ASSERT_EXISTS #define F_B_PRT(_f_, _v_) DBUG_PRINT("assert_fail", (_f_, _v_)) -static int fail_block(BLOCK_LINK *block) +static int fail_block(BLOCK_LINK *block __attribute__((unused))) { +#ifndef DBUG_OFF F_B_PRT("block->next_used: %p\n", block->next_used); F_B_PRT("block->prev_used: %p\n", block->prev_used); F_B_PRT("block->next_changed: %p\n", block->next_changed); @@ -4805,10 +4810,13 @@ static int fail_block(BLOCK_LINK *block) F_B_PRT("block->offset: %u\n", block->offset); F_B_PRT("block->requests: %u\n", block->requests); F_B_PRT("block->temperature: %u\n", block->temperature); +#endif return 0; /* Let the assert fail. */ } +#endif -static int fail_hlink(HASH_LINK *hlink) +#ifndef DBUG_OFF +static int fail_hlink(HASH_LINK *hlink __attribute__((unused))) { F_B_PRT("hlink->next: %p\n", hlink->next); F_B_PRT("hlink->prev: %p\n", hlink->prev); @@ -5276,7 +5284,8 @@ int resize_partitioned_key_cache(PARTITIONED_KEY_CACHE_CB *keycache, { for (i= 0; i < partitions; i++) { - finish_resize_simple_key_cache(keycache->partition_array[i], 1); + keycache_pthread_mutex_lock(&keycache->partition_array[i]->cache_lock); + finish_resize_simple_key_cache(keycache->partition_array[i]); } } DBUG_RETURN(blocks); diff --git a/mysys/mf_tempfile.c b/mysys/mf_tempfile.c index a535d35a53f..25bfca344f2 100644 --- a/mysys/mf_tempfile.c +++ b/mysys/mf_tempfile.c @@ -22,7 +22,9 @@ #include <paths.h> #endif - +#ifdef HAVE_MKOSTEMP +#define mkstemp(A) mkostemp(A, O_CLOEXEC) +#endif /* @brief @@ -45,8 +47,11 @@ implementation, it's main use is to generate a file with a name that does not already exist. - When passing O_TEMPORARY flag in "mode" the file should - be automatically deleted + When passing MY_TEMPORARY flag in MyFlags the file is automatically deleted + + "mode" bits that always must be used for newly created files with + unique file names (O_EXCL | O_TRUNC | O_CREAT | O_RDWR) are added + automatically, and shouldn't be specified by the caller. The implementation using mkstemp should be considered the reference implementation when adding a new or modifying an @@ -55,51 +60,55 @@ */ File create_temp_file(char *to, const char *dir, const char *prefix, - int mode __attribute__((unused)), - myf MyFlags __attribute__((unused))) + int mode, myf MyFlags) { File file= -1; -#ifdef __WIN__ - TCHAR path_buf[MAX_PATH-14]; -#endif DBUG_ENTER("create_temp_file"); DBUG_PRINT("enter", ("dir: %s, prefix: %s", dir, prefix)); -#if defined (__WIN__) - - /* - Use GetTempPath to determine path for temporary files. - This is because the documentation for GetTempFileName - has the following to say about this parameter: - "If this parameter is NULL, the function fails." - */ - if (!dir) - { - if(GetTempPath(sizeof(path_buf), path_buf) > 0) - dir = path_buf; - } - /* - Use GetTempFileName to generate a unique filename, create - the file and release it's handle - - uses up to the first three letters from prefix - */ - if (GetTempFileName(dir, prefix, 0, to) == 0) - DBUG_RETURN(-1); - - DBUG_PRINT("info", ("name: %s", to)); - - /* - Open the file without the "open only if file doesn't already exist" - since the file has already been created by GetTempFileName - */ - if ((file= my_open(to, (mode & ~O_EXCL), MyFlags)) < 0) + DBUG_ASSERT((mode & (O_EXCL | O_TRUNC | O_CREAT | O_RDWR)) == 0); + + mode|= O_TRUNC | O_CREAT | O_RDWR; /* not O_EXCL, see Windows code below */ + +#ifdef _WIN32 { - /* Open failed, remove the file created by GetTempFileName */ - int tmp= my_errno; - (void) my_delete(to, MYF(0)); - my_errno= tmp; + TCHAR path_buf[MAX_PATH-14]; + /* + Use GetTempPath to determine path for temporary files. + This is because the documentation for GetTempFileName + has the following to say about this parameter: + "If this parameter is NULL, the function fails." + */ + if (!dir) + { + if(GetTempPath(sizeof(path_buf), path_buf) > 0) + dir = path_buf; + } + /* + Use GetTempFileName to generate a unique filename, create + the file and release it's handle + - uses up to the first three letters from prefix + */ + if (GetTempFileName(dir, prefix, 0, to) == 0) + DBUG_RETURN(-1); + + DBUG_PRINT("info", ("name: %s", to)); + + if (MyFlags & MY_TEMPORARY) + mode|= O_SHORT_LIVED | O_TEMPORARY; + + /* + Open the file without O_EXCL flag + since the file has already been created by GetTempFileName + */ + if ((file= my_open(to, mode, MyFlags)) < 0) + { + /* Open failed, remove the file created by GetTempFileName */ + int tmp= my_errno; + (void) my_delete(to, MYF(0)); + my_errno= tmp; + } } - #elif defined(HAVE_MKSTEMP) { char prefix_buff[30]; @@ -119,8 +128,8 @@ File create_temp_file(char *to, const char *dir, const char *prefix, } strmov(convert_dirname(to,dir,NullS),prefix_buff); org_file=mkstemp(to); - if (mode & O_TEMPORARY) - (void) my_delete(to, MYF(MY_WME | ME_NOINPUT)); + if (org_file >= 0 && (MyFlags & MY_TEMPORARY)) + (void) my_delete(to, MYF(MY_WME)); file=my_register_filename(org_file, to, FILE_BY_MKSTEMP, EE_CANTCREATEFILE, MyFlags); /* If we didn't manage to register the name, remove the temp file */ @@ -132,46 +141,10 @@ File create_temp_file(char *to, const char *dir, const char *prefix, my_errno=tmp; } } -#elif defined(HAVE_TEMPNAM) - { - extern char **environ; - - char *res,**old_env,*temp_env[1]; - if (dir && !dir[0]) - { /* Change empty string to current dir */ - to[0]= FN_CURLIB; - to[1]= 0; - dir=to; - } - - old_env= (char**) environ; - if (dir) - { /* Don't use TMPDIR if dir is given */ - environ=(const char**) temp_env; - temp_env[0]=0; - } - - if ((res=tempnam((char*) dir, (char*) prefix))) - { - strmake(to,res,FN_REFLEN-1); - (*free)(res); - file=my_create(to,0, - (int) (O_RDWR | O_BINARY | O_TRUNC | O_EXCL | O_NOFOLLOW | - O_TEMPORARY | O_SHORT_LIVED), - MYF(MY_WME)); - - } - else - { - DBUG_PRINT("error",("Got error: %d from tempnam",errno)); - } - - environ=(const char**) old_env; - } #else #error No implementation found for create_temp_file #endif if (file >= 0) - thread_safe_increment(my_tmp_file_created,&THR_LOCK_open); + statistic_increment(my_tmp_file_created,&THR_LOCK_open); DBUG_RETURN(file); } diff --git a/mysys/my_access.c b/mysys/my_access.c index 4fbea8bd3b0..65746295391 100644 --- a/mysys/my_access.c +++ b/mysys/my_access.c @@ -38,7 +38,7 @@ int my_access(const char *path, int amode) attributes = GetFileAttributes(path); if (attributes == INVALID_FILE_ATTRIBUTES || - (attributes & FILE_ATTRIBUTE_READONLY) && (amode & W_OK)) + ((attributes & FILE_ATTRIBUTE_READONLY) && (amode & W_OK))) { my_errno= errno= EACCES; return -1; diff --git a/mysys/my_alloc.c b/mysys/my_alloc.c index d67b8be9bb8..a7d4ec1c248 100644 --- a/mysys/my_alloc.c +++ b/mysys/my_alloc.c @@ -35,6 +35,7 @@ SYNOPSIS init_alloc_root() mem_root - memory root to initialize + name - name of memroot (for debugging) block_size - size of chunks (blocks) used for memory allocation (It is external size of chunk i.e. it should include memory required for internal structures, thus it @@ -54,13 +55,13 @@ Because of this, we store in MY_THREAD_SPECIFIC as bit 1 in block_size */ -void init_alloc_root(MEM_ROOT *mem_root, size_t block_size, +void init_alloc_root(MEM_ROOT *mem_root, const char *name, size_t block_size, size_t pre_alloc_size __attribute__((unused)), myf my_flags) { DBUG_ENTER("init_alloc_root"); - DBUG_PRINT("enter",("root: %p prealloc: %zu", mem_root, - pre_alloc_size)); + DBUG_PRINT("enter",("root: %p name: %s prealloc: %zu", mem_root, + name, pre_alloc_size)); mem_root->free= mem_root->used= mem_root->pre_alloc= 0; mem_root->min_malloc= 32; @@ -71,6 +72,8 @@ void init_alloc_root(MEM_ROOT *mem_root, size_t block_size, mem_root->error_handler= 0; mem_root->block_num= 4; /* We shift this with >>2 */ mem_root->first_block_usage= 0; + mem_root->total_alloc= 0; + mem_root->name= name; #if !(defined(HAVE_valgrind) && defined(EXTRA_DEBUG)) if (pre_alloc_size) @@ -80,6 +83,7 @@ void init_alloc_root(MEM_ROOT *mem_root, size_t block_size, MYF(my_flags)))) { mem_root->free->size= pre_alloc_size+ALIGN_SIZE(sizeof(USED_MEM)); + mem_root->total_alloc= pre_alloc_size+ALIGN_SIZE(sizeof(USED_MEM)); mem_root->free->left= pre_alloc_size; mem_root->free->next= 0; TRASH_MEM(mem_root->free); @@ -138,6 +142,7 @@ void reset_root_defaults(MEM_ROOT *mem_root, size_t block_size, { /* remove block from the list and free it */ *prev= mem->next; + mem_root->total_alloc-= mem->size; my_free(mem); } else @@ -149,9 +154,10 @@ void reset_root_defaults(MEM_ROOT *mem_root, size_t block_size, block_size))))) { mem->size= size; + mem_root->total_alloc+= size; mem->left= pre_alloc_size; mem->next= *prev; - *prev= mem_root->pre_alloc= mem; + *prev= mem_root->pre_alloc= mem; TRASH_MEM(mem); } else @@ -173,7 +179,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: %p", mem_root)); + DBUG_PRINT("enter",("root: %p name: %s", mem_root, mem_root->name)); DBUG_ASSERT(alloc_root_inited(mem_root)); @@ -195,8 +201,10 @@ void *alloc_root(MEM_ROOT *mem_root, size_t length) DBUG_RETURN((uchar*) 0); /* purecov: inspected */ } next->next= mem_root->used; + next->left= 0; next->size= length; mem_root->used= next; + mem_root->total_alloc+= length; DBUG_PRINT("exit",("ptr: %p", (((char*) next)+ ALIGN_SIZE(sizeof(USED_MEM))))); DBUG_RETURN((uchar*) (((char*) next)+ALIGN_SIZE(sizeof(USED_MEM)))); @@ -205,9 +213,9 @@ void *alloc_root(MEM_ROOT *mem_root, size_t length) uchar* point; reg1 USED_MEM *next= 0; reg2 USED_MEM **prev; - size_t original_length = length; + size_t original_length __attribute__((unused)) = length; DBUG_ENTER("alloc_root"); - DBUG_PRINT("enter",("root: %p", mem_root)); + DBUG_PRINT("enter",("root: %p name: %s", mem_root, mem_root->name)); DBUG_ASSERT(alloc_root_inited(mem_root)); DBUG_EXECUTE_IF("simulate_out_of_memory", @@ -250,6 +258,7 @@ void *alloc_root(MEM_ROOT *mem_root, size_t length) DBUG_RETURN((void*) 0); /* purecov: inspected */ } mem_root->block_num++; + mem_root->total_alloc+= get_size; next->next= *prev; next->size= get_size; next->left= get_size-ALIGN_SIZE(sizeof(USED_MEM)); @@ -299,6 +308,10 @@ void *multi_alloc_root(MEM_ROOT *root, ...) char **ptr, *start, *res; size_t tot_length, length; DBUG_ENTER("multi_alloc_root"); + /* + We don't need to do DBUG_PRINT here as it will be done when alloc_root + is called + */ va_start(args, root); tot_length= 0; @@ -324,7 +337,9 @@ void *multi_alloc_root(MEM_ROOT *root, ...) DBUG_RETURN((void*) start); } -/* Mark all data in blocks free for reusage */ + +#if !(defined(HAVE_valgrind) && defined(EXTRA_DEBUG)) +/** Mark all data in blocks free for reusage */ static inline void mark_blocks_free(MEM_ROOT* root) { @@ -352,7 +367,9 @@ static inline void mark_blocks_free(MEM_ROOT* root) /* Now everything is set; Indicate that nothing is used anymore */ root->used= 0; root->first_block_usage= 0; + root->block_num= 4; } +#endif /* @@ -378,13 +395,20 @@ void free_root(MEM_ROOT *root, myf MyFlags) { reg1 USED_MEM *next,*old; DBUG_ENTER("free_root"); - DBUG_PRINT("enter",("root: %p flags: %u", root, (uint) MyFlags)); + DBUG_PRINT("enter",("root: %p name: %s flags: %u", root, root->name, + (uint) MyFlags)); +#if !(defined(HAVE_valgrind) && defined(EXTRA_DEBUG)) + /* + There is no point in using mark_blocks_free when using valgrind as + it will not reclaim any memory + */ if (MyFlags & MY_MARK_BLOCKS_FREE) { mark_blocks_free(root); DBUG_VOID_RETURN; } +#endif if (!(MyFlags & MY_KEEP_PREALLOC)) root->pre_alloc=0; @@ -392,13 +416,19 @@ void free_root(MEM_ROOT *root, myf MyFlags) { old=next; next= next->next ; if (old != root->pre_alloc) + { + root->total_alloc-= old->size; my_free(old); + } } for (next=root->free ; next ;) { old=next; next= next->next; if (old != root->pre_alloc) + { + root->total_alloc-= old->size; my_free(old); + } } root->used=root->free=0; if (root->pre_alloc) diff --git a/mysys/my_atomic.c b/mysys/my_atomic.c deleted file mode 100644 index 72d7f132988..00000000000 --- a/mysys/my_atomic.c +++ /dev/null @@ -1,67 +0,0 @@ -/* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; version 2 of the License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ - -#include <my_global.h> -#include <my_sys.h> - -#include <my_atomic.h> - -/* - checks that the current build of atomic ops - can run on this machine - - RETURN - ATOMIC_xxx values, see my_atomic.h -*/ -int my_atomic_initialize() -{ - compile_time_assert(sizeof(intptr) == sizeof(void *)); - /* currently the only thing worth checking is SMP/UP issue */ -#ifdef MY_ATOMIC_MODE_DUMMY - return my_getncpus() == 1 ? MY_ATOMIC_OK : MY_ATOMIC_NOT_1CPU; -#else - return MY_ATOMIC_OK; -#endif -} - -#ifdef SAFE_MUTEX -#undef pthread_mutex_init -#undef pthread_mutex_destroy -#undef pthread_mutex_lock -#undef pthread_mutex_unlock - -void plain_pthread_mutex_init(safe_mutex_t *m) -{ - pthread_mutex_init(& m->mutex, NULL); -} - -void plain_pthread_mutex_destroy(safe_mutex_t *m) -{ - pthread_mutex_destroy(& m->mutex); -} - -void plain_pthread_mutex_lock(safe_mutex_t *m) -{ - pthread_mutex_lock(& m->mutex); -} - -void plain_pthread_mutex_unlock(safe_mutex_t *m) -{ - pthread_mutex_unlock(& m->mutex); -} - -#endif - - diff --git a/mysys/my_atomic_writes.c b/mysys/my_atomic_writes.c index 0b54a207713..7f1e353c121 100644 --- a/mysys/my_atomic_writes.c +++ b/mysys/my_atomic_writes.c @@ -259,7 +259,7 @@ static my_bool shannon_has_atomic_write(File file, int page_size) ************************************************************************/ /* - Initalize automic write sub systems. + Initialize automic write sub systems. Checks if we have any devices that supports atomic write */ diff --git a/mysys/my_basename.c b/mysys/my_basename.c index 68f5565b269..54c4b0aaf1c 100644 --- a/mysys/my_basename.c +++ b/mysys/my_basename.c @@ -13,6 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ +#include <my_global.h> #include <my_sys.h> /** @brief retrieve last component of the filename. diff --git a/mysys/my_bitmap.c b/mysys/my_bitmap.c index f857101af02..5111174d749 100644 --- a/mysys/my_bitmap.c +++ b/mysys/my_bitmap.c @@ -166,11 +166,15 @@ static inline uint get_first_set(my_bitmap_map value, uint word_pos) return MY_BIT_NONE; /* Impossible */ } +/* + Initialize a bitmap object. All bits will be set to zero +*/ my_bool my_bitmap_init(MY_BITMAP *map, my_bitmap_map *buf, uint n_bits, my_bool thread_safe) { DBUG_ENTER("my_bitmap_init"); + map->mutex= 0; if (!buf) { uint size_in_bytes= bitmap_buffer_size(n_bits); @@ -180,7 +184,6 @@ my_bool my_bitmap_init(MY_BITMAP *map, my_bitmap_map *buf, uint n_bits, size_in_bytes= ALIGN_SIZE(size_in_bytes); extra= sizeof(mysql_mutex_t); } - map->mutex= 0; if (!(buf= (my_bitmap_map*) my_malloc(size_in_bytes+extra, MYF(MY_WME)))) DBUG_RETURN(1); if (thread_safe) diff --git a/mysys/my_compare.c b/mysys/my_compare.c index ff704aa40f0..358cb77008e 100644 --- a/mysys/my_compare.c +++ b/mysys/my_compare.c @@ -20,8 +20,8 @@ #include <my_compare.h> #include <my_sys.h> -int ha_compare_text(CHARSET_INFO *charset_info, const uchar *a, uint a_length, - const uchar *b, uint b_length, my_bool part_key) +int ha_compare_text(CHARSET_INFO *charset_info, const uchar *a, size_t a_length, + const uchar *b, size_t b_length, my_bool part_key) { if (!part_key) return charset_info->coll->strnncollsp(charset_info, a, a_length, diff --git a/mysys/my_conio.c b/mysys/my_conio.c index bac9f4afdf9..04750635dd3 100644 --- a/mysys/my_conio.c +++ b/mysys/my_conio.c @@ -47,7 +47,7 @@ static HANDLE my_coninpfh= 0; /* console input */ static int my_pthread_auto_mutex_lock(HANDLE* ph, const char* name, int id, int time) { - int res; + DWORD res; char tname[FN_REFLEN]; sprintf(tname, "%s-%08X", name, id); @@ -203,7 +203,7 @@ char* my_cgets(char *buffer, size_t clen, size_t* plen) if (*plen > 0 && buffer[*plen - 1] == '\r') { char tmp[3]; - int tmplen= sizeof(tmp); + DWORD tmplen= (DWORD)sizeof(tmp); *plen= *plen - 1; /* read /n left in the buffer */ diff --git a/mysys/my_context.c b/mysys/my_context.c index f3aef23e07a..4153927d335 100644 --- a/mysys/my_context.c +++ b/mysys/my_context.c @@ -714,7 +714,7 @@ my_context_continue(struct my_context *c) { /* This seems to be a common trick to run ConvertThreadToFiber() only on the - first occurence in a thread, in a way that works on multiple Windows + first occurrence in a thread, in a way that works on multiple Windows versions. */ void *current_fiber= GetCurrentFiber(); diff --git a/mysys/my_cpu.c b/mysys/my_cpu.c new file mode 100644 index 00000000000..3b0ef08a326 --- /dev/null +++ b/mysys/my_cpu.c @@ -0,0 +1,94 @@ +/* Copyright (c) 2019, MariaDB Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include <my_global.h> +#include <my_atomic.h> +#include <my_cpu.h> + +#ifdef HAVE_PAUSE_INSTRUCTION +/** How many times to invoke PAUSE in a loop */ +unsigned my_cpu_relax_multiplier = 200; + +# include <stdint.h> + +# ifdef _MSC_VER +# include <intrin.h> +# define my_timer_cycles __rdtsc +# elif !defined __x86_64__ +/* On some x86 targets, __rdtsc() causes an unresolved external symbol error, +instead of being inlined. Let us fall back to my_timer_cycles(), which +internally invokes rdtsc. */ +# include <my_rdtsc.h> +# else +# include <x86intrin.h> +# define my_timer_cycles __rdtsc +# endif + +#define PAUSE4 MY_RELAX_CPU(); MY_RELAX_CPU(); MY_RELAX_CPU(); MY_RELAX_CPU() +#define PAUSE16 PAUSE4; PAUSE4; PAUSE4; PAUSE4 + +/** + Initialize my_cpu_relax_multiplier. + + Determine the duration of a PAUSE instruction by running an + unrolled loop of 16 PAUSE instructions twice, and taking the + faster of the two runs. In this way, even if the execution is + interrupted by the operating system, it should be extremely + unlikely that both loops get interrupted. + + On the Intel Skylake microarchitecture, the PAUSE instruction takes + around 140 clock cycles, while on earlier microarchitectures it could + be 10 clock cycles or less. Scale the PAUSE loop counter accordingly. + + On a pre-Skylake Intel Xeon CPU E5-2630 v4 @ 2.20GHz running an AMD64 + executable, the numbers would be between 172 and 220 when all the code + is inlined as follows: + + rdtsc,mov,shl,or, 16*pause, + rdtsc,mov,shl,or, 16*pause, + rdtsc. + + That would yield 11 to 14 cycles per PAUSE instruction even if we + (wrongly) ignore the overhead of the other instructions. + + On a Skylake mobile processor Intel Core i7-6500U CPU @ 2.50GHz, the + numbers would range from 1896 to 2410 (or 1976 if taking the minimum + of two runs), yielding 118 to 151 (or 123) cycles per PAUSE instruction. + + Let us define a threshold at roughly 30 cycles per PAUSE instruction, + and use a shorter delay if the PAUSE instruction takes longer than + that. In some AMD processors, the PAUSE instruction could take 40 or + 50 cycles. Let us use a shorter delay multiplier for them as well. + + The 1/2 scaling factor (200/100) was derived experimentally by + Steve Shaw from Intel and Sergey Vojtovich from MariaDB Foundation. + In an earlier experiment on MySQL code base, a 1/10 scaling factor + (200/20) seemed to work best. + + The basic idea of the detection algorithm (run 16 PAUSE instructions + between RDTSC) was suggested by Mikhail Sinyavin from Intel. +*/ +void my_cpu_init(void) +{ + uint64_t t0, t1, t2; + t0= my_timer_cycles(); + PAUSE16; + t1= my_timer_cycles(); + PAUSE16; + t2= my_timer_cycles(); + if (t2 - t1 > 30 * 16 && t1 - t0 > 30 * 16) + my_cpu_relax_multiplier= 100; +} +#endif diff --git a/mysys/my_default.c b/mysys/my_default.c index c1d971f5062..7c6ef8fffa0 100644 --- a/mysys/my_default.c +++ b/mysys/my_default.c @@ -519,7 +519,7 @@ int my_load_defaults(const char *conf_file, const char **groups, uint args_sep= my_getopt_use_args_separator ? 1 : 0; DBUG_ENTER("load_defaults"); - init_alloc_root(&alloc, 512, 0, MYF(0)); + init_alloc_root(&alloc, "my_load_defaults", 512, 0, MYF(0)); if ((dirs= init_default_directories(&alloc)) == NULL) goto err; /* @@ -1047,7 +1047,7 @@ void my_print_default_files(const char *conf_file) { const char **dirs; MEM_ROOT alloc; - init_alloc_root(&alloc, 512, 0, MYF(0)); + init_alloc_root(&alloc, "my_print_defaults", 512, 0, MYF(0)); if ((dirs= init_default_directories(&alloc)) == NULL) { @@ -1196,7 +1196,7 @@ static const char **init_default_directories(MEM_ROOT *alloc) { errors += add_directory(alloc, fname_buffer, dirs); - strncat(fname_buffer, "/data", sizeof(fname_buffer)); + strcat_s(fname_buffer, sizeof(fname_buffer), "/data"); errors += add_directory(alloc, fname_buffer, dirs); } } diff --git a/mysys/my_delete.c b/mysys/my_delete.c index 6cdecef22f1..cce26643ef7 100644 --- a/mysys/my_delete.c +++ b/mysys/my_delete.c @@ -84,7 +84,7 @@ static int my_win_unlink(const char *name) { HANDLE handle= INVALID_HANDLE_VALUE; DWORD attributes; - DWORD last_error; + uint last_error; char unique_filename[MAX_PATH + 35]; unsigned long long tsc; /* time stamp counter, for unique filename*/ @@ -170,7 +170,7 @@ static int my_win_unlink(const char *name) name, tsc); if (!MoveFile(name, unique_filename)) { - DBUG_PRINT("warning", ("moving %s to unique filename failed, error %u\n", + DBUG_PRINT("warning", ("moving %s to unique filename failed, error %lu\n", name,GetLastError())); } diff --git a/mysys/my_fopen.c b/mysys/my_fopen.c index 924d219c3a8..2450ec85d60 100644 --- a/mysys/my_fopen.c +++ b/mysys/my_fopen.c @@ -19,10 +19,6 @@ #include <errno.h> #include "mysys_err.h" -#if defined(__FreeBSD__) -extern int getosreldate(void); -#endif - static void make_ftype(char * to,int flag); /* @@ -65,15 +61,13 @@ FILE *my_fopen(const char *filename, int flags, myf MyFlags) int filedesc= my_fileno(fd); if ((uint)filedesc >= my_file_limit) { - thread_safe_increment(my_stream_opened,&THR_LOCK_open); + statistic_increment(my_stream_opened,&THR_LOCK_open); DBUG_RETURN(fd); /* safeguard */ } - mysql_mutex_lock(&THR_LOCK_open); my_file_info[filedesc].name= (char*) my_strdup(filename,MyFlags); - my_stream_opened++; - my_file_total_opened++; + statistic_increment(my_stream_opened, &THR_LOCK_open); + statistic_increment(my_file_total_opened, &THR_LOCK_open); my_file_info[filedesc].type= STREAM_BY_FOPEN; - mysql_mutex_unlock(&THR_LOCK_open); DBUG_PRINT("exit",("stream: %p", fd)); DBUG_RETURN(fd); } @@ -130,52 +124,6 @@ static FILE *my_win_freopen(const char *path, const char *mode, FILE *stream) return stream; } -#elif defined(__FreeBSD__) - -/* No close operation hook. */ - -static int no_close(void *cookie __attribute__((unused))) -{ - return 0; -} - -/* - A hack around a race condition in the implementation of freopen. - - The race condition stems from the fact that the current fd of - the stream is closed before its number is used to duplicate the - new file descriptor. This defeats the desired atomicity of the - close and duplicate of dup2(). - - See PR number 79887 for reference: - http://www.freebsd.org/cgi/query-pr.cgi?pr=79887 -*/ - -static FILE *my_freebsd_freopen(const char *path, const char *mode, FILE *stream) -{ - int old_fd; - FILE *result; - - flockfile(stream); - - old_fd= fileno(stream); - - /* Use a no operation close hook to avoid having the fd closed. */ - stream->_close= no_close; - - /* Relies on the implicit dup2 to close old_fd. */ - result= freopen(path, mode, stream); - - /* If successful, the _close hook was replaced. */ - - if (result == NULL) - close(old_fd); - else - funlockfile(result); - - return result; -} - #endif @@ -199,16 +147,6 @@ FILE *my_freopen(const char *path, const char *mode, FILE *stream) #if defined(_WIN32) result= my_win_freopen(path, mode, stream); -#elif defined(__FreeBSD__) - /* - XXX: Once the fix is ported to the stable releases, this should - be dependent upon the specific FreeBSD versions. Check at: - http://www.freebsd.org/cgi/query-pr.cgi?pr=79887 - */ - if (getosreldate() > 900027) - result= freopen(path, mode, stream); - else - result= my_freebsd_freopen(path, mode, stream); #else result= freopen(path, mode, stream); #endif @@ -221,11 +159,17 @@ FILE *my_freopen(const char *path, const char *mode, FILE *stream) int my_fclose(FILE *fd, myf MyFlags) { int err,file; + char *name= NULL; DBUG_ENTER("my_fclose"); DBUG_PRINT("my",("stream: %p MyFlags: %lu", fd, MyFlags)); - mysql_mutex_lock(&THR_LOCK_open); file= my_fileno(fd); + if ((uint) file < my_file_limit && my_file_info[file].type != UNOPEN) + { + name= my_file_info[file].name; + my_file_info[file].name= NULL; + my_file_info[file].type= UNOPEN; + } #ifndef _WIN32 err= fclose(fd); #else @@ -236,16 +180,15 @@ int my_fclose(FILE *fd, myf MyFlags) my_errno=errno; if (MyFlags & (MY_FAE | MY_WME)) my_error(EE_BADCLOSE, MYF(ME_BELL+ME_WAITTANG), - my_filename(file),errno); + name,errno); } else - my_stream_opened--; - if ((uint) file < my_file_limit && my_file_info[file].type != UNOPEN) + statistic_decrement(my_stream_opened, &THR_LOCK_open); + + if (name) { - my_file_info[file].type = UNOPEN; - my_free(my_file_info[file].name); + my_free(name); } - mysql_mutex_unlock(&THR_LOCK_open); DBUG_RETURN(err); } /* my_fclose */ @@ -275,13 +218,12 @@ FILE *my_fdopen(File Filedes, const char *name, int Flags, myf MyFlags) } else { - mysql_mutex_lock(&THR_LOCK_open); - my_stream_opened++; + statistic_increment(my_stream_opened, &THR_LOCK_open); if ((uint) Filedes < (uint) my_file_limit) { if (my_file_info[Filedes].type != UNOPEN) { - my_file_opened--; /* File is opened with my_open ! */ + thread_safe_decrement32(&my_file_opened); /* File is opened with my_open ! */ } else { @@ -289,7 +231,6 @@ FILE *my_fdopen(File Filedes, const char *name, int Flags, myf MyFlags) } my_file_info[Filedes].type = STREAM_BY_FDOPEN; } - mysql_mutex_unlock(&THR_LOCK_open); } DBUG_PRINT("exit",("stream: %p", fd)); @@ -320,6 +261,8 @@ FILE *my_fdopen(File Filedes, const char *name, int Flags, myf MyFlags) r+ == O_RDWR w+ == O_RDWR|O_TRUNC|O_CREAT a+ == O_RDWR|O_APPEND|O_CREAT + b == FILE_BINARY + e == O_CLOEXEC */ static void make_ftype(register char * to, register int flag) diff --git a/mysys/my_getopt.c b/mysys/my_getopt.c index 13d0d7c240e..7d539cd44fe 100644 --- a/mysys/my_getopt.c +++ b/mysys/my_getopt.c @@ -23,7 +23,6 @@ #include <mysys_err.h> #include <my_getopt.h> #include <errno.h> -#include <m_string.h> typedef void (*init_func_p)(const struct my_option *option, void *variable, longlong value); @@ -237,7 +236,7 @@ int handle_options(int *argc, char ***argv, { is_cmdline_arg= 1; - /* save the separator too if skip unkown options */ + /* save the separator too if skip unknown options */ if (my_getopt_skip_unknown) (*argv)[argvpos++]= cur_arg; else @@ -827,7 +826,7 @@ static int setval(const struct my_option *opts, void *value, char *argument, *((ulonglong*)value)= find_set_from_flags(opts->typelib, opts->typelib->count, *(ulonglong *)value, opts->def_value, - argument, strlen(argument), + argument, (uint)strlen(argument), &error, &error_len); if (error) { @@ -836,6 +835,27 @@ static int setval(const struct my_option *opts, void *value, char *argument, }; } break; + case GET_BIT: + { + uint tmp; + ulonglong bit= (opts->block_size >= 0 ? + opts->block_size : + -opts->block_size); + /* + This sets a bit stored in a longlong. + The bit to set is stored in block_size. If block_size is positive + then setting the bit means value is true. If block_size is negatitive, + then setting the bit means value is false. + */ + tmp= get_bool_argument(opts, argument); + if (opts->block_size < 0) + tmp= !tmp; + if (tmp) + (*(ulonglong*)value)|= bit; + else + (*(ulonglong*)value)&= ~bit; + break; + } case GET_NO_ARG: /* get_one_option has taken care of the value already */ default: /* dummy default to avoid compiler warnings */ break; @@ -949,31 +969,43 @@ my_bool getopt_compare_strings(register const char *s, register const char *t, /* function: eval_num_suffix - Transforms suffix like k/m/g to their real value. + Transforms suffix like k/m/g/t/p/e to their real value. */ -static inline long eval_num_suffix(char *suffix, int *error) +static inline ulonglong eval_num_suffix(char *suffix, int *error) { - long num= 1; - if (*suffix == 'k' || *suffix == 'K') - num*= 1024L; - else if (*suffix == 'm' || *suffix == 'M') - num*= 1024L * 1024L; - else if (*suffix == 'g' || *suffix == 'G') - num*= 1024L * 1024L * 1024L; - else if (*suffix) - { + switch (*suffix) { + case '\0': + return 1ULL; + case 'k': + case 'K': + return 1ULL << 10; + case 'm': + case 'M': + return 1ULL << 20; + case 'g': + case 'G': + return 1ULL << 30; + case 't': + case 'T': + return 1ULL << 40; + case 'p': + case 'P': + return 1ULL << 50; + case 'e': + case 'E': + return 1ULL << 60; + default: *error= 1; - return 0; + return 0ULL; } - return num; } /* function: eval_num_suffix_ll Transforms a number with a suffix to real number. Suffix can - be k|K for kilo, m|M for mega or g|G for giga. + be k|K for kilo, m|M for mega, etc. */ static longlong eval_num_suffix_ll(char *argument, @@ -1006,7 +1038,7 @@ static longlong eval_num_suffix_ll(char *argument, function: eval_num_suffix_ull Transforms a number with a suffix to positive Integer. Suffix can - be k|K for kilo, m|M for mega or g|G for giga. + be k|K for kilo, m|M for mega, etc. */ static ulonglong eval_num_suffix_ull(char *argument, @@ -1289,6 +1321,19 @@ static void init_one_value(const struct my_option *option, void *variable, case GET_FLAGSET: *((ulonglong*) variable)= (ulonglong) value; break; + case GET_BIT: + { + ulonglong bit= (option->block_size >= 0 ? + option->block_size : + -option->block_size); + if (option->block_size < 0) + value= !value; + if (value) + (*(ulonglong*)variable)|= bit; + else + (*(ulonglong*)variable)&= ~bit; + break; + } case GET_DOUBLE: *((double*) variable)= getopt_ulonglong2double(value); break; @@ -1477,7 +1522,8 @@ void my_print_help(const struct my_option *options) printf("--"); col+= 2 + print_name(optp); if (optp->arg_type == NO_ARG || - (optp->var_type & GET_TYPE_MASK) == GET_BOOL) + (optp->var_type & GET_TYPE_MASK) == GET_BOOL || + (optp->var_type & GET_TYPE_MASK) == GET_BIT) { putchar(' '); col++; @@ -1575,7 +1621,7 @@ void my_print_variables(const struct my_option *options) for (optp= options; optp->name; optp++) { - length= strlen(optp->name)+1; + length= (uint)strlen(optp->name)+1; if (length > name_space) name_space= length; } @@ -1627,6 +1673,16 @@ void my_print_variables(const struct my_option *options) case GET_BOOL: printf("%s\n", *((my_bool*) value) ? "TRUE" : "FALSE"); break; + case GET_BIT: + { + ulonglong bit= (optp->block_size >= 0 ? + optp->block_size : + -optp->block_size); + my_bool reverse= optp->block_size < 0; + printf("%s\n", ((*((ulonglong*) value) & bit) != 0) ^ reverse ? + "TRUE" : "FALSE"); + break; + } case GET_INT: printf("%d\n", *((int*) value)); break; diff --git a/mysys/my_getsystime.c b/mysys/my_getsystime.c index e2e6e8e398a..18218c2e8de 100644 --- a/mysys/my_getsystime.c +++ b/mysys/my_getsystime.c @@ -18,9 +18,12 @@ #include "mysys_priv.h" #include "my_static.h" -#ifdef __WIN__ +#ifdef _WIN32 #define OFFSET_TO_EPOC 116444736000000000LL static ulonglong query_performance_frequency; +typedef void (WINAPI* get_system_time_as_filetime_t)(LPFILETIME); +static get_system_time_as_filetime_t + my_GetSystemTimePreciseAsFileTime= GetSystemTimeAsFileTime; #endif #ifdef HAVE_LINUX_UNISTD_H #include <linux/unistd.h> @@ -53,7 +56,7 @@ ulonglong my_interval_timer() return tp.tv_sec*1000000000ULL+tp.tv_nsec; #elif defined(HAVE_GETHRTIME) return gethrtime(); -#elif defined(__WIN__) +#elif defined(_WIN32) LARGE_INTEGER t_cnt; if (query_performance_frequency) { @@ -65,7 +68,7 @@ ulonglong my_interval_timer() else { ulonglong newtime; - GetSystemTimeAsFileTime((FILETIME*)&newtime); + my_GetSystemTimePreciseAsFileTime((FILETIME*)&newtime); return newtime*100ULL; } #else @@ -82,11 +85,10 @@ ulonglong my_interval_timer() my_hrtime_t my_hrtime() { my_hrtime_t hrtime; -#if defined(__WIN__) +#if defined(_WIN32) ulonglong newtime; - GetSystemTimeAsFileTime((FILETIME*)&newtime); - newtime -= OFFSET_TO_EPOC; - hrtime.val= newtime/10; + my_GetSystemTimePreciseAsFileTime((FILETIME*)&newtime); + hrtime.val= (newtime - OFFSET_TO_EPOC)/10; #elif defined(HAVE_CLOCK_GETTIME) struct timespec tp; clock_gettime(CLOCK_REALTIME, &tp); @@ -97,17 +99,44 @@ my_hrtime_t my_hrtime() while (gettimeofday(&t, NULL) != 0) {} hrtime.val= t.tv_sec*1000000ULL + t.tv_usec; #endif + DBUG_EXECUTE_IF("system_time_plus_one_hour", hrtime.val += 3600*1000000ULL;); + DBUG_EXECUTE_IF("system_time_minus_one_hour", hrtime.val -= 3600*1000000ULL;); return hrtime; } +#ifdef _WIN32 + +/* + Low accuracy, "coarse" timer. + Has lower latency than my_hrtime(). Used in situations, where microsecond + precision is not needed, e.g in Windows pthread_cond_timedwait, where POSIX + interface needs nanoseconds, yet the underlying Windows function only + accepts millisecons. +*/ +my_hrtime_t my_hrtime_coarse() +{ + my_hrtime_t hrtime; + ulonglong t; + GetSystemTimeAsFileTime((FILETIME*)&t); + hrtime.val= (t - OFFSET_TO_EPOC)/10; + return hrtime; +} + +#endif void my_time_init() { -#ifdef __WIN__ +#ifdef _WIN32 compile_time_assert(sizeof(LARGE_INTEGER) == sizeof(query_performance_frequency)); if (QueryPerformanceFrequency((LARGE_INTEGER *)&query_performance_frequency) == 0) query_performance_frequency= 0; + + get_system_time_as_filetime_t f= (get_system_time_as_filetime_t) + GetProcAddress(GetModuleHandle("kernel32"), + "GetSystemTimePreciseAsFileTime"); + if (f) + my_GetSystemTimePreciseAsFileTime= f; #endif } diff --git a/mysys/my_init.c b/mysys/my_init.c index eb7623f75b6..d8fb2003052 100644 --- a/mysys/my_init.c +++ b/mysys/my_init.c @@ -105,6 +105,10 @@ my_bool my_init(void) if (my_thread_global_init()) return 1; +#if defined(SAFEMALLOC) && !defined(DBUG_OFF) + dbug_sanity= sf_sanity; +#endif + /* $HOME is needed early to parse configuration files located in ~/ */ if ((home_dir= getenv("HOME")) != 0) home_dir= intern_filename(home_dir_buff, home_dir); @@ -118,6 +122,9 @@ my_bool my_init(void) #ifdef __WIN__ win32_init_tcp_ip(); #endif +#ifdef CHECK_UNLIKELY + init_my_likely(); +#endif DBUG_RETURN(0); } } /* my_init */ @@ -151,17 +158,36 @@ void my_end(int infoflag) } if ((infoflag & MY_CHECK_ERROR) || print_info) + { /* Test if some file is left open */ + char ebuff[512]; + uint i, open_files, open_streams; + + for (open_streams= open_files= i= 0 ; i < my_file_limit ; i++) + { + if (my_file_info[i].type == UNOPEN) + continue; + if (my_file_info[i].type == STREAM_BY_FOPEN || + my_file_info[i].type == STREAM_BY_FDOPEN) + open_streams++; + else + open_files++; - { /* Test if some file is left open */ - if (my_file_opened | my_stream_opened) +#ifdef EXTRA_DEBUG + fprintf(stderr, EE(EE_FILE_NOT_CLOSED), my_file_info[i].name, i); + fputc('\n', stderr); +#endif + } + if (open_files || open_streams) { - char ebuff[512]; my_snprintf(ebuff, sizeof(ebuff), EE(EE_OPEN_WARNING), - my_file_opened, my_stream_opened); + open_files, open_streams); my_message_stderr(EE_OPEN_WARNING, ebuff, ME_BELL); DBUG_PRINT("error", ("%s", ebuff)); - my_print_open_files(); } + +#ifdef CHECK_UNLIKELY + end_my_likely(info_file); +#endif } free_charsets(); my_error_unregister_all(); @@ -229,7 +255,7 @@ Voluntary context switches %ld, Involuntary context switches %ld\n", my_init_done= my_thr_key_mysys_exists= 0; } /* my_end */ -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS /* Dummy tag function for debugging */ void my_debug_put_break_here(void) diff --git a/mysys/my_lib.c b/mysys/my_lib.c index c40b75e1faf..8ce23f65fd3 100644 --- a/mysys/my_lib.c +++ b/mysys/my_lib.c @@ -136,7 +136,7 @@ MY_DIR *my_dir(const char *path, myf MyFlags) MYF(MyFlags))) goto error; - init_alloc_root(&dirh->root, NAMES_START_SIZE, NAMES_START_SIZE, + init_alloc_root(&dirh->root, "dir", NAMES_START_SIZE, NAMES_START_SIZE, MYF(MyFlags)); dp= (struct dirent*) dirent_tmp; @@ -210,12 +210,12 @@ MY_DIR *my_dir(const char *path, myf MyFlags) ushort mode; char tmp_path[FN_REFLEN], *tmp_file,attrib; #ifdef _WIN64 - __int64 handle; + __int64 handle= -1; #else - long handle; + long handle= -1; #endif DBUG_ENTER("my_dir"); - DBUG_PRINT("my",("path: '%s' MyFlags: %d",path,MyFlags)); + DBUG_PRINT("my",("path: '%s' MyFlags: %d",path,(int)MyFlags)); /* Put LIB-CHAR as last path-character if not there */ tmp_file=tmp_path; @@ -239,7 +239,7 @@ MY_DIR *my_dir(const char *path, myf MyFlags) MYF(MyFlags))) goto error; - init_alloc_root(&dirh->root, NAMES_START_SIZE, NAMES_START_SIZE, + init_alloc_root(&dirh->root, "dir", NAMES_START_SIZE, NAMES_START_SIZE, MYF(MyFlags)); if ((handle=_findfirst(tmp_path,&find)) == -1L) diff --git a/mysys/my_likely.c b/mysys/my_likely.c new file mode 100644 index 00000000000..c6fca5b7146 --- /dev/null +++ b/mysys/my_likely.c @@ -0,0 +1,173 @@ +/* Copyright (c) 2018, MariaDB Corporation Ab. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +/* + Checks that my_likely/my_unlikely is correctly used + + Note that we can't use mysql_mutex or my_malloc here as these + uses likely() macros and the likely_mutex would be used twice +*/ + +#include "mysys_priv.h" +#include <hash.h> +#include <m_ctype.h> + +#ifndef CHECK_UNLIKEY +my_bool likely_inited= 0; + +typedef struct st_likely_entry +{ + const char *key; + size_t key_length; + uint line; + ulonglong ok,fail; +} LIKELY_ENTRY; + +static uchar *get_likely_key(LIKELY_ENTRY *part, size_t *length, + my_bool not_used __attribute__((unused))) +{ + *length= part->key_length; + return (uchar*) part->key; +} + +pthread_mutex_t likely_mutex; +HASH likely_hash; + +void init_my_likely() +{ + /* Allocate big enough to avoid malloc calls */ + my_hash_init2(&likely_hash, 10000, &my_charset_bin, + 1024, 0, 0, + (my_hash_get_key) get_likely_key, 0, + free, HASH_UNIQUE); + likely_inited= 1; + pthread_mutex_init(&likely_mutex, MY_MUTEX_INIT_FAST); +} + +static int likely_cmp(LIKELY_ENTRY **a, LIKELY_ENTRY **b) +{ + int cmp; + if ((cmp= strcmp((*a)->key, (*b)->key))) + return cmp; + return (int) ((*a)->line - (*b)->line); +} + + +void end_my_likely(FILE *out) +{ + uint i; + FILE *likely_file; + my_bool do_close= 0; + LIKELY_ENTRY **sort_ptr= 0; + + likely_inited= 0; + + if (!(likely_file= out)) + { + char name[80]; + sprintf(name, "/tmp/unlikely-%lu.out", (ulong) getpid()); + if ((likely_file= my_fopen(name, O_TRUNC | O_WRONLY, MYF(MY_WME)))) + do_close= 1; + else + likely_file= stderr; + } + fflush(likely_file); + fputs("Wrong likely/unlikely usage:\n", likely_file); + if (!(sort_ptr= (LIKELY_ENTRY**) + malloc(sizeof(LIKELY_ENTRY*) *likely_hash.records))) + { + fprintf(stderr, "ERROR: Out of memory in end_my_likely\n"); + goto err; + } + + for (i=0 ; i < likely_hash.records ; i++) + sort_ptr[i]= (LIKELY_ENTRY *) my_hash_element(&likely_hash, i); + + my_qsort(sort_ptr, likely_hash.records, sizeof(LIKELY_ENTRY*), + (qsort_cmp) likely_cmp); + + for (i=0 ; i < likely_hash.records ; i++) + { + LIKELY_ENTRY *entry= sort_ptr[i]; + if (entry->fail > entry->ok) + fprintf(likely_file, + "%50s line: %6u ok: %8lld fail: %8lld\n", + entry->key, entry->line, entry->ok, entry->fail); + } + fputs("\n", likely_file); + fflush(likely_file); +err: + free((void*) sort_ptr); + if (do_close) + my_fclose(likely_file, MYF(MY_WME)); + pthread_mutex_destroy(&likely_mutex); + my_hash_free(&likely_hash); +} + + +static LIKELY_ENTRY *my_likely_find(const char *file_name, uint line) +{ + char key[80], *pos; + LIKELY_ENTRY *entry; + size_t length; + + if (!likely_inited) + return 0; + + pos= strnmov(key, file_name, sizeof(key)-4); + int3store(pos+1, line); + length= (size_t) (pos-key)+4; + + pthread_mutex_lock(&likely_mutex); + if (!(entry= (LIKELY_ENTRY*) my_hash_search(&likely_hash, (uchar*) key, + length))) + { + if (!(entry= (LIKELY_ENTRY *) malloc(sizeof(*entry) + length))) + return 0; + entry->key= (char*) (entry+1); + memcpy((void*) entry->key, key, length); + entry->key_length= length; + entry->line= line; + entry->ok= entry->fail= 0; + + if (my_hash_insert(&likely_hash, (void*) entry)) + { + pthread_mutex_unlock(&likely_mutex); + free(entry); + return 0; + } + } + pthread_mutex_unlock(&likely_mutex); + return entry; +} + + +int my_likely_ok(const char *file_name, uint line) +{ + LIKELY_ENTRY *entry= my_likely_find(file_name, line); + if (entry) + entry->ok++; + return 0; +} + + +int my_likely_fail(const char *file_name, uint line) +{ + LIKELY_ENTRY *entry= my_likely_find(file_name, line); + if (entry) + entry->fail++; + return 0; +} +#endif /* CHECK_UNLIKEY */ diff --git a/mysys/my_lock.c b/mysys/my_lock.c index 0fd280ca963..3319f6d8cb2 100644 --- a/mysys/my_lock.c +++ b/mysys/my_lock.c @@ -34,7 +34,6 @@ static int win_lock(File fd, int locktype, my_off_t start, my_off_t length, DWORD dwFlags; OVERLAPPED ov= {0}; HANDLE hFile= (HANDLE)my_get_osfhandle(fd); - DWORD lastError= 0; int i; int timeout_millis= timeout_sec * 1000; diff --git a/mysys/my_lockmem.c b/mysys/my_lockmem.c index ce569b78c73..3ce0f07af58 100644 --- a/mysys/my_lockmem.c +++ b/mysys/my_lockmem.c @@ -20,7 +20,6 @@ #include <my_list.h> #ifdef HAVE_MLOCK -#include <sys/mman.h> struct st_mem_list { diff --git a/mysys/my_malloc.c b/mysys/my_malloc.c index 0640c0e577e..55ef2673ef2 100644 --- a/mysys/my_malloc.c +++ b/mysys/my_malloc.c @@ -201,8 +201,6 @@ void *my_realloc(void *oldpoint, size_t size, myf my_flags) /** Free memory allocated with my_malloc. - @remark Relies on free being able to handle a NULL argument. - @param ptr Pointer to the memory allocated by my_malloc. */ void my_free(void *ptr) @@ -215,6 +213,13 @@ void my_free(void *ptr) my_bool old_flags; old_size= MALLOC_SIZE_AND_FLAG(ptr, &old_flags); update_malloc_size(- (longlong) old_size - MALLOC_PREFIX_SIZE, old_flags); +#ifndef SAFEMALLOC + /* + Trash memory if not safemalloc. We don't have to do this if safemalloc + is used as safemalloc will also do trashing + */ + TRASH_FREE(ptr, old_size); +#endif sf_free(MALLOC_FIX_POINTER_FOR_FREE(ptr)); } DBUG_VOID_RETURN; diff --git a/mysys/my_minidump.cc b/mysys/my_minidump.cc new file mode 100644 index 00000000000..d81aab2f764 --- /dev/null +++ b/mysys/my_minidump.cc @@ -0,0 +1,115 @@ +/* Copyright (c) 2021, MariaDB Corporation + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ + +#include <windows.h> +#include <dbghelp.h> +#include <stdio.h> +#include <my_minidump.h> + +#define VERBOSE(fmt,...) \ + if (verbose) { fprintf(stderr, "my_create_minidump : " fmt,__VA_ARGS__); } + +extern "C" BOOL my_create_minidump(DWORD pid, BOOL verbose) +{ + HANDLE file = 0; + HANDLE process= 0; + DWORD size= MAX_PATH; + char path[MAX_PATH]; + char working_dir[MAX_PATH]; + char tmpname[MAX_PATH]; + char *filename= 0; + bool ret= FALSE; + process= OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid); + if (!process) + { + VERBOSE("cannot open process pid=%lu to create dump, last error %lu\n", + pid, GetLastError()); + goto exit; + } + + if (QueryFullProcessImageName(process, 0, path, &size) == 0) + { + VERBOSE("cannot read process path for pid %lu, last error %lu\n", + pid, GetLastError()); + goto exit; + } + + filename= strrchr(path, '\\'); + if (filename) + { + filename++; + // We are not interested in dump of some proceses (my_safe_process.exe,cmd.exe) + // since they are only used to start up other programs. + // We're interested however in their children; + const char *exclude_programs[] = {"my_safe_process.exe","cmd.exe", 0}; + for(size_t i=0; exclude_programs[i]; i++) + if (_stricmp(filename, exclude_programs[i]) == 0) + goto exit; + } + else + filename= path; + + // Add .dmp extension + char *p; + if ((p= strrchr(filename, '.')) == 0) + p= filename + strlen(filename); + + strncpy(p, ".dmp", path + MAX_PATH - p); + + // Íf file with this name exist, generate unique name with .dmp extension + if (GetFileAttributes(filename) != INVALID_FILE_ATTRIBUTES) + { + if (!GetTempFileName(".", filename, 0, tmpname)) + { + fprintf(stderr, "GetTempFileName failed, last error %lu", GetLastError()); + goto exit; + } + strncat_s(tmpname, ".dmp", sizeof(tmpname)); + filename= tmpname; + } + + if (!GetCurrentDirectory(MAX_PATH, working_dir)) + { + VERBOSE("GetCurrentDirectory failed, last error %lu", GetLastError()); + goto exit; + } + + file= CreateFile(filename, GENERIC_READ | GENERIC_WRITE, + 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); + + if (file == INVALID_HANDLE_VALUE) + { + VERBOSE("CreateFile() failed for file %s, working dir %s, last error = %lu\n", + filename, working_dir, GetLastError()); + goto exit; + } + + if (!MiniDumpWriteDump(process, pid, file, MiniDumpNormal, 0, 0, 0)) + { + VERBOSE("Failed to write minidump to %s, working dir %s, last error %lu\n", + filename, working_dir, GetLastError()); + goto exit; + } + + VERBOSE("Minidump written to %s, directory %s\n", filename, working_dir); + ret= TRUE; +exit: + if (process != 0 && process != INVALID_HANDLE_VALUE) + CloseHandle(process); + + if (file != 0 && file != INVALID_HANDLE_VALUE) + CloseHandle(file); + return ret; +} diff --git a/mysys/my_open.c b/mysys/my_open.c index 951a52c28dc..8edef360f92 100644 --- a/mysys/my_open.c +++ b/mysys/my_open.c @@ -76,17 +76,20 @@ File my_open(const char *FileName, int Flags, myf MyFlags) int my_close(File fd, myf MyFlags) { int err; + char *name= NULL; DBUG_ENTER("my_close"); DBUG_PRINT("my",("fd: %d MyFlags: %lu",fd, MyFlags)); if (!(MyFlags & (MY_WME | MY_FAE))) MyFlags|= my_global_flags; - mysql_mutex_lock(&THR_LOCK_open); -#ifndef _WIN32 - do + if ((uint) fd < my_file_limit && my_file_info[fd].type != UNOPEN) { - err= close(fd); - } while (err == -1 && errno == EINTR); + name= my_file_info[fd].name; + my_file_info[fd].name= NULL; + my_file_info[fd].type= UNOPEN; + } +#ifndef _WIN32 + err= close(fd); #else err= my_win_close(fd); #endif @@ -96,15 +99,13 @@ int my_close(File fd, myf MyFlags) my_errno=errno; if (MyFlags & (MY_FAE | MY_WME)) my_error(EE_BADCLOSE, MYF(ME_BELL | ME_WAITTANG | (MyFlags & (ME_JUST_INFO | ME_NOREFRESH))), - my_filename(fd),errno); + name,errno); } - if ((uint) fd < my_file_limit && my_file_info[fd].type != UNOPEN) + if (name) { - my_free(my_file_info[fd].name); - my_file_info[fd].type = UNOPEN; + my_free(name); } - my_file_opened--; - mysql_mutex_unlock(&THR_LOCK_open); + thread_safe_decrement32(&my_file_opened); DBUG_RETURN(err); } /* my_close */ @@ -132,17 +133,12 @@ File my_register_filename(File fd, const char *FileName, enum file_type DBUG_ENTER("my_register_filename"); if ((int) fd >= MY_FILE_MIN) { + thread_safe_increment32(&my_file_opened); if ((uint) fd >= my_file_limit) - { - thread_safe_increment(my_file_opened,&THR_LOCK_open); - DBUG_RETURN(fd); /* safeguard */ - } - mysql_mutex_lock(&THR_LOCK_open); + DBUG_RETURN(fd); my_file_info[fd].name = (char*) my_strdup(FileName, MyFlags); - my_file_opened++; - my_file_total_opened++; + statistic_increment(my_file_total_opened,&THR_LOCK_open); my_file_info[fd].type = type_of_file; - mysql_mutex_unlock(&THR_LOCK_open); DBUG_PRINT("exit",("fd: %d",fd)); DBUG_RETURN(fd); } @@ -159,26 +155,3 @@ File my_register_filename(File fd, const char *FileName, enum file_type } DBUG_RETURN(-1); } - - - - -#ifdef EXTRA_DEBUG - -void my_print_open_files(void) -{ - if (my_file_opened | my_stream_opened) - { - uint i; - for (i= 0 ; i < my_file_limit ; i++) - { - if (my_file_info[i].type != UNOPEN) - { - fprintf(stderr, EE(EE_FILE_NOT_CLOSED), my_file_info[i].name, i); - fputc('\n', stderr); - } - } - } -} - -#endif diff --git a/mysys/my_pthread.c b/mysys/my_pthread.c index b8121f9a52f..e2795ed7bb9 100644 --- a/mysys/my_pthread.c +++ b/mysys/my_pthread.c @@ -21,6 +21,7 @@ #include <signal.h> #include <m_string.h> #include <thr_alarm.h> +#include <my_pthread.h> #if (defined(__BSD__) || defined(_BSDI_VERSION)) #define SCHED_POLICY SCHED_RR diff --git a/mysys/my_read.c b/mysys/my_read.c index caf47a6ed4d..9a78e8d7977 100644 --- a/mysys/my_read.c +++ b/mysys/my_read.c @@ -44,7 +44,7 @@ size_t my_read(File Filedes, uchar *Buffer, size_t Count, myf MyFlags) for (;;) { - errno= 0; /* Linux, Windows don't reset this on EOF/success */ + errno= 0; /* Linux, Windows don't reset this on EOF/success */ #ifdef _WIN32 readbytes= my_win_read(Filedes, Buffer, Count); #else @@ -70,7 +70,7 @@ size_t my_read(File Filedes, uchar *Buffer, size_t Count, myf MyFlags) my_errno= HA_ERR_FILE_TOO_SHORT; if ((readbytes == 0 || (int) readbytes == -1) && got_errno == EINTR) - { + { DBUG_PRINT("debug", ("my_read() was interrupted and returned %ld", (long) readbytes)); continue; /* Interrupted */ @@ -99,11 +99,11 @@ size_t my_read(File Filedes, uchar *Buffer, size_t Count, myf MyFlags) } if (readbytes == (size_t) -1 || ((MyFlags & (MY_FNABP | MY_NABP)) && !(MyFlags & MY_FULL_IO))) - DBUG_RETURN(MY_FILE_ERROR); /* Return with error */ + DBUG_RETURN(MY_FILE_ERROR); /* Return with error */ } if (MyFlags & (MY_NABP | MY_FNABP)) - readbytes= 0; /* Ok on read */ + readbytes= 0; /* Ok on read */ else readbytes+= save_count; break; diff --git a/mysys/my_rename.c b/mysys/my_rename.c index 81b43064ea8..5702af94272 100644 --- a/mysys/my_rename.c +++ b/mysys/my_rename.c @@ -19,8 +19,62 @@ #include "m_string.h" #undef my_rename - /* On unix rename deletes to file if it exists */ +#ifdef _WIN32 + +#define RENAME_MAX_RETRIES 50 + +/* + On Windows, bad 3rd party programs (backup or anitivirus, or something else) + can have file open with a sharing mode incompatible with renaming, i.e they + won't use FILE_SHARE_DELETE when opening file. + + The following function will do a couple of retries, in case MoveFileEx returns + ERROR_SHARING_VIOLATION. +*/ +static BOOL win_rename_with_retries(const char *from, const char *to) +{ +#ifndef DBUG_OFF + FILE *fp = NULL; + DBUG_EXECUTE_IF("rename_sharing_violation", + { + fp= fopen(from, "r"); + DBUG_ASSERT(fp); + } + ); +#endif + + for (int retry= RENAME_MAX_RETRIES; retry--;) + { + DWORD ret = MoveFileEx(from, to, + MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING); + + DBUG_ASSERT(fp == NULL || (ret == FALSE && GetLastError() == ERROR_SHARING_VIOLATION)); + + if (!ret && (GetLastError() == ERROR_SHARING_VIOLATION)) + { +#ifndef DBUG_OFF + /* + If error was injected in via DBUG_EXECUTE_IF, close the file + that is causing ERROR_SHARING_VIOLATION, so that retry succeeds. + */ + if (fp) + { + fclose(fp); + fp= NULL; + } +#endif + + Sleep(10); + } + else + return ret; + } + return FALSE; +} +#endif + + /* On unix rename deletes to file if it exists */ int my_rename(const char *from, const char *to, myf MyFlags) { int error = 0; @@ -28,8 +82,7 @@ int my_rename(const char *from, const char *to, myf MyFlags) DBUG_PRINT("my",("from %s to %s MyFlags %lu", from, to, MyFlags)); #if defined(__WIN__) - if (!MoveFileEx(from, to, MOVEFILE_COPY_ALLOWED | - MOVEFILE_REPLACE_EXISTING)) + if (!win_rename_with_retries(from, to)) { my_osmaperr(GetLastError()); #elif defined(HAVE_RENAME) diff --git a/mysys/my_static.c b/mysys/my_static.c index 8916b42f9fb..2b5f6c3eb1d 100644 --- a/mysys/my_static.c +++ b/mysys/my_static.c @@ -1,5 +1,5 @@ /* Copyright (c) 2000, 2011, Oracle and/or its affiliates. - Copyright (c) 2009, 2012, Monty Program Ab. + Copyright (c) 2009, 2019, MariaDB Corporation. 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 @@ -31,14 +31,18 @@ char *mysql_data_home= (char*) "."; const char *my_progname= NULL, *my_progname_short= NULL; char curr_dir[FN_REFLEN]= {0}, home_dir_buff[FN_REFLEN]= {0}; -ulong my_stream_opened=0,my_file_opened=0, my_tmp_file_created=0; +ulong my_stream_opened=0,my_tmp_file_created=0; ulong my_file_total_opened= 0; int my_umask=0664, my_umask_dir=0777; myf my_global_flags= 0; +#ifndef DBUG_OFF +my_bool my_assert= 1; +#endif my_bool my_assert_on_error= 0; struct st_my_file_info my_file_info_default[MY_NFILE]; uint my_file_limit= MY_NFILE; +int32 my_file_opened=0; struct st_my_file_info *my_file_info= my_file_info_default; /* From mf_brkhant */ diff --git a/mysys/my_thr_init.c b/mysys/my_thr_init.c index 5686dabf35d..66c2543c51d 100644 --- a/mysys/my_thr_init.c +++ b/mysys/my_thr_init.c @@ -341,14 +341,12 @@ void my_thread_end(void) tmp, pthread_self(), tmp ? (long) tmp->id : 0L); #endif -#ifdef HAVE_PSI_INTERFACE /* Remove the instrumentation for this thread. This must be done before trashing st_my_thread_var, because the LF_HASH depends on it. */ - PSI_THREAD_CALL(delete_current_thread)(); -#endif + PSI_CALL_delete_current_thread(); /* We need to disable DBUG early for this thread to ensure that the diff --git a/mysys/my_wincond.c b/mysys/my_wincond.c index c2814e37b93..64c48c5aeba 100644 --- a/mysys/my_wincond.c +++ b/mysys/my_wincond.c @@ -98,9 +98,10 @@ int pthread_attr_init(pthread_attr_t *connect_att) return 0; } -int pthread_attr_setstacksize(pthread_attr_t *connect_att,DWORD stack) +int pthread_attr_setstacksize(pthread_attr_t *connect_att,size_t stack) { - connect_att->dwStackSize=stack; + DBUG_ASSERT(stack < UINT_MAX); + connect_att->dwStackSize=(DWORD)stack; return 0; } diff --git a/mysys/my_windac.c b/mysys/my_windac.c index 677207b2e07..9a2686a44f4 100644 --- a/mysys/my_windac.c +++ b/mysys/my_windac.c @@ -17,21 +17,6 @@ #include "m_string.h" #ifdef __WIN__ -/* Windows NT/2000 discretionary access control utility functions. */ - -/* - Check if the operating system is built on NT technology. - - RETURN - 0 Windows 95/98/Me - 1 otherwise -*/ - -static my_bool is_nt() -{ - return GetVersion() < 0x80000000; -} - /* Auxiliary structure to store pointers to the data which we need to keep around while SECURITY_ATTRIBUTES is in use. @@ -86,12 +71,6 @@ int my_security_attr_create(SECURITY_ATTRIBUTES **psa, const char **perror, PSID owner_sid; My_security_attr *attr; - if (! is_nt()) - { - *psa= 0; - return 0; - } - /* Get SID of Everyone group. Easier to retrieve all SIDs each time this function is called than worry about thread safety. diff --git a/mysys/my_winerr.c b/mysys/my_winerr.c index 299db267289..dd4156daebd 100644 --- a/mysys/my_winerr.c +++ b/mysys/my_winerr.c @@ -96,7 +96,7 @@ access violations */ static int get_errno_from_oserr(unsigned long oserrno) { - int i; + size_t i; /* check the table for the OS error code */ for (i= 0; i < ERRTABLESIZE; ++i) diff --git a/mysys/my_winfile.c b/mysys/my_winfile.c index a250b3d2e06..0c76eb25560 100644 --- a/mysys/my_winfile.c +++ b/mysys/my_winfile.c @@ -528,7 +528,7 @@ FILE *my_win_fopen(const char *filename, const char *type) { FILE *file; int flags= 0; - DBUG_ENTER("my_win_open"); + DBUG_ENTER("my_win_fopen"); /* If we are not creating, then we need to use my_access to make sure @@ -585,7 +585,7 @@ int my_win_fclose(FILE *file) { File fd; - DBUG_ENTER("my_win_close"); + DBUG_ENTER("my_win_fclose"); fd= my_fileno(file); if(fd < 0) DBUG_RETURN(-1); diff --git a/mysys/my_winthread.c b/mysys/my_winthread.c index 4f8f5dcf44b..b74804fbe2a 100644 --- a/mysys/my_winthread.c +++ b/mysys/my_winthread.c @@ -79,10 +79,10 @@ int pthread_create(pthread_t *thread_id, const pthread_attr_t *attr, par->arg= param; stack_size= attr?attr->dwStackSize:0; - handle= _beginthreadex(NULL, stack_size , pthread_start, par, 0, thread_id); + handle= _beginthreadex(NULL, stack_size , pthread_start, par, 0, (uint *)thread_id); if (!handle) goto error_return; - DBUG_PRINT("info", ("thread id=%u",*thread_id)); + DBUG_PRINT("info", ("thread id=%lu",*thread_id)); /* Do not need thread handle, close it */ CloseHandle((HANDLE)handle); diff --git a/mysys/mysys_priv.h b/mysys/mysys_priv.h index c0a040bd482..0fa6a4c553f 100644 --- a/mysys/mysys_priv.h +++ b/mysys/mysys_priv.h @@ -146,8 +146,12 @@ const char *my_open_parent_dir_nosymlinks(const char *pathname, int *pdfd); return NOAT; #endif +#ifndef _WIN32 #define CREATE_NOSYMLINK_FUNCTION(PROTO,AT,NOAT) \ static int PROTO { NOSYMLINK_FUNCTION_BODY(AT,NOAT) } +#else +#define CREATE_NOSYMLINK_FUNCTION(PROTO,AT,NOAT) +#endif #ifdef _WIN32 #include <sys/stat.h> diff --git a/mysys/psi_noop.c b/mysys/psi_noop.c index b20aae84849..8ab17f22cc4 100644 --- a/mysys/psi_noop.c +++ b/mysys/psi_noop.c @@ -763,6 +763,13 @@ struct PSI_bootstrap *PSI_hook= NULL; PSI *PSI_server= & PSI_noop; +/** + Global performance schema flag. + Indicate if the performance schema is enabled. + This flag is set at startup, and never changes. +*/ +my_bool pfs_enabled= FALSE; + void set_psi_server(PSI *psi) { PSI_server= psi; diff --git a/mysys/ptr_cmp.c b/mysys/ptr_cmp.c index 287e884305c..7ea15baf86d 100644 --- a/mysys/ptr_cmp.c +++ b/mysys/ptr_cmp.c @@ -27,14 +27,18 @@ * written in assembler. for example one in /usr/lib/libc/libc_hwcap*.so.1. * on Solaris, or on Windows inside C runtime linrary. * - * On Solaris, native implementation is also usually faster than the - * built-in memcmp supplied by GCC, so it is recommended to build + * On Solaris, native implementation is also usually faster than the + * built-in memcmp supplied by GCC, so it is recommended to build * with "-fno-builtin-memcmp"in CFLAGS if building with GCC on Solaris. */ -#if defined (__sun) || defined (_WIN32) +/* + Daniel Blacks tests shows that libc memcmp is generally faster than + ptr_cmp() at least of x86 and power8 platforms, so we use the libc + code as deafult for now +*/ + #define USE_NATIVE_MEMCMP 1 -#endif #ifdef USE_NATIVE_MEMCMP @@ -45,7 +49,12 @@ static int native_compare(size_t *length, unsigned char **a, unsigned char **b) return memcmp(*a, *b, *length); } -#else /* USE_NATIVE_MEMCMP */ +qsort2_cmp get_ptr_compare (size_t size __attribute__((unused))) +{ + return (qsort2_cmp) native_compare; +} + +#else /* USE_NATIVE_MEMCMP */ 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); @@ -57,16 +66,7 @@ static int degenerate_compare_func(size_t *compare_length, uchar **a, uchar **b) DBUG_ASSERT(*compare_length == 0); return 0; } -#endif /* __sun */ - /* Get a pointer to a optimal byte-compare function for a given size */ - -#ifdef USE_NATIVE_MEMCMP -qsort2_cmp get_ptr_compare (size_t size __attribute__((unused))) -{ - return (qsort2_cmp) native_compare; -} -#else qsort2_cmp get_ptr_compare (size_t size) { if (size == 0) @@ -81,9 +81,6 @@ qsort2_cmp get_ptr_compare (size_t size) } return 0; /* Impossible */ } -#endif /* USE_NATIVE_MEMCMP */ - - /* Compare to keys to see witch is smaller. Loop unrolled to make it quick !! @@ -91,8 +88,6 @@ qsort2_cmp get_ptr_compare (size_t size) #define cmp(N) if (first[N] != last[N]) return (int) first[N] - (int) last[N] -#ifndef USE_NATIVE_MEMCMP - static int ptr_compare(size_t *compare_length, uchar **a, uchar **b) { size_t length= *compare_length; @@ -196,7 +191,7 @@ static int ptr_compare_3(size_t *compare_length,uchar **a, uchar **b) return (0); } -#endif /* !__sun */ +#endif /* USE_NATIVE_MEMCMP */ void my_store_ptr(uchar *buff, size_t pack_length, my_off_t pos) { @@ -234,4 +229,3 @@ my_off_t my_get_ptr(uchar *ptr, size_t pack_length) } return pos; } - diff --git a/mysys/safemalloc.c b/mysys/safemalloc.c index 58d4a13e9a6..034b6f2536b 100644 --- a/mysys/safemalloc.c +++ b/mysys/safemalloc.c @@ -293,7 +293,7 @@ static void warn(const char *format,...) { void *frame[SF_REMEMBER_FRAMES + SF_FRAMES_SKIP]; int frames= backtrace(frame, array_elements(frame)); - fprintf(stderr, " "); + fprintf(stderr, " at "); if (frames < SF_REMEMBER_FRAMES + SF_FRAMES_SKIP) frame[frames]= 0; print_stack(frame + SF_FRAMES_SKIP); @@ -318,7 +318,8 @@ static int bad_ptr(const char *where, void *ptr) } if (irem->marker != MAGICSTART) { - warn("Error: %s unallocated data or underrun buffer", where); + DBUG_PRINT("error",("Unallocated data or underrun buffer %p", ptr)); + warn("Error: %s unallocated data or underrun buffer %p", where, ptr); return 1; } @@ -328,7 +329,8 @@ static int bad_ptr(const char *where, void *ptr) magicend[2] != MAGICEND2 || magicend[3] != MAGICEND3) { - warn("Error: %s overrun buffer ", where); + DBUG_PRINT("error",("Overrun buffer %p", ptr)); + warn("Error: %s overrun buffer %p", where, ptr); fprintf(stderr, "Allocated at "); print_stack(irem->frame); return 1; @@ -338,7 +340,7 @@ static int bad_ptr(const char *where, void *ptr) } /* check all allocated memory list for consistency */ -static int sf_sanity() +int sf_sanity() { struct st_irem *irem; int flag= 0; diff --git a/mysys/stacktrace.c b/mysys/stacktrace.c index 86cf272a437..f5e6352a72e 100644 --- a/mysys/stacktrace.c +++ b/mysys/stacktrace.c @@ -51,7 +51,7 @@ returns 1, it does not mean 100% that the pointer is corrupted. */ -int my_safe_print_str(const char* val, int max_len) +int my_safe_print_str(const char* val, size_t max_len) { const char *orig_val= val; if (!val) @@ -83,12 +83,13 @@ int my_safe_print_str(const char* val, int max_len) #include <ucontext.h> void my_print_stacktrace(uchar* stack_bottom __attribute__((unused)), - ulong thread_stack __attribute__((unused))) + ulong thread_stack __attribute__((unused)), + my_bool silent) { if (printstack(fileno(stderr)) == -1) my_safe_printf_stderr("%s", "Error when traversing the stack, stack appears corrupt.\n"); - else + else if (!silent) my_safe_printf_stderr("%s", "Please read " "http://dev.mysql.com/doc/refman/5.1/en/resolve-stack-dump.html\n" @@ -165,7 +166,8 @@ static int print_with_addr_resolve(void **addrs, int n) } #endif -void my_print_stacktrace(uchar* stack_bottom, ulong thread_stack) +void my_print_stacktrace(uchar* stack_bottom, ulong thread_stack, + my_bool silent __attribute__((unused))) { void *addrs[128]; char **strings __attribute__((unused)) = NULL; @@ -239,7 +241,8 @@ inline uint32* find_prev_pc(uint32* pc, uchar** fp) } #endif /* defined(__alpha__) && defined(__GNUC__) */ -void my_print_stacktrace(uchar* stack_bottom, ulong thread_stack) +void my_print_stacktrace(uchar* stack_bottom, ulong thread_stack, + my_bool silent) { uchar** UNINIT_VAR(fp); uint frame_count = 0, sigreturn_frame_count; @@ -354,7 +357,8 @@ void my_print_stacktrace(uchar* stack_bottom, ulong thread_stack) "Stack trace seems successful - bottom reached\n"); end: - my_safe_printf_stderr("%s", + if (!silent) + my_safe_printf_stderr("%s", "Please read " "http://dev.mysql.com/doc/refman/5.1/en/resolve-stack-dump.html\n" "and follow instructions on how to resolve the stack trace.\n" @@ -510,7 +514,7 @@ static void get_symbol_path(char *path, size_t size) #define SYMOPT_NO_PROMPTS 0 #endif -void my_print_stacktrace(uchar* unused1, ulong unused2) +void my_print_stacktrace(uchar* unused1, ulong unused2, my_bool silent) { HANDLE hProcess= GetCurrentProcess(); HANDLE hThread= GetCurrentThread(); @@ -599,7 +603,7 @@ void my_print_stacktrace(uchar* unused1, ulong unused2) if(have_source) { const char *base_file_name= my_basename(line.FileName); - my_safe_printf_stderr("[%s:%u]", + my_safe_printf_stderr("[%s:%lu]", base_file_name, line.LineNumber); } my_safe_printf_stderr("%s", "\n"); @@ -629,7 +633,7 @@ void my_write_core(int unused) if(GetModuleFileName(NULL, path, sizeof(path))) { _splitpath(path, NULL, NULL,dump_fname,NULL); - strncat(dump_fname, ".dmp", sizeof(dump_fname)); + strcat_s(dump_fname, sizeof(dump_fname), ".dmp"); } hFile= CreateFile(dump_fname, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, @@ -659,7 +663,7 @@ void my_write_core(int unused) } -int my_safe_print_str(const char *val, int len) +int my_safe_print_str(const char *val, size_t len) { __try { @@ -676,7 +680,7 @@ int my_safe_print_str(const char *val, int len) size_t my_write_stderr(const void *buf, size_t count) { - return (size_t) write(fileno(stderr), buf, count); + return (size_t) write(fileno(stderr), buf, (uint)count); } diff --git a/mysys/string.c b/mysys/string.c index 1b4254fa60d..7c56b95af25 100644 --- a/mysys/string.c +++ b/mysys/string.c @@ -178,9 +178,9 @@ my_bool dynstr_append_quoted(DYNAMIC_STRING *str, const char *append, size_t len, char quote) { - uint additional= (str->alloc_increment ? str->alloc_increment : 10); - uint lim= additional; - uint i; + size_t additional= (str->alloc_increment ? str->alloc_increment : 10); + size_t lim= additional; + size_t i; if (dynstr_realloc(str, len + additional + 2)) return TRUE; str->str[str->length++]= quote; diff --git a/mysys/thr_alarm.c b/mysys/thr_alarm.c index a1ff5b6c44a..8a89762de2a 100644 --- a/mysys/thr_alarm.c +++ b/mysys/thr_alarm.c @@ -36,13 +36,6 @@ #define ETIME ETIMEDOUT #endif -uint thr_client_alarm; -static int alarm_aborted=1; /* No alarm thread */ -my_bool thr_alarm_inited= 0, my_disable_thr_alarm= 0; -volatile my_bool alarm_thread_running= 0; -time_t next_alarm_expire_time= ~ (time_t) 0; -static sig_handler process_alarm_part2(int sig); - #ifdef DBUG_OFF #define reset_index_in_queue(alarm_data) #else @@ -55,9 +48,16 @@ static sig_handler process_alarm_part2(int sig); #define one_signal_hand_sigmask(A,B,C) #endif +my_bool thr_alarm_inited= 0, my_disable_thr_alarm= 0; #if !defined(__WIN__) +uint thr_client_alarm; +static int alarm_aborted=1; /* No alarm thread */ +volatile my_bool alarm_thread_running= 0; +time_t next_alarm_expire_time= ~ (time_t) 0; +static sig_handler process_alarm_part2(int sig); + static mysql_mutex_t LOCK_alarm; static mysql_cond_t COND_alarm; static sigset_t full_signal_set; @@ -261,7 +261,8 @@ void thr_end_alarm(thr_alarm_t *alarmed) alarm_data= (ALARM*) ((uchar*) *alarmed - offsetof(ALARM,alarmed)); mysql_mutex_lock(&LOCK_alarm); DBUG_ASSERT(alarm_data->index_in_queue != 0); - DBUG_ASSERT(queue_element(&alarm_queue, alarm_data->index_in_queue) == + DBUG_ASSERT((ALARM*) queue_element(&alarm_queue, + alarm_data->index_in_queue) == alarm_data); queue_remove(&alarm_queue, alarm_data->index_in_queue); mysql_mutex_unlock(&LOCK_alarm); diff --git a/mysys/thr_lock.c b/mysys/thr_lock.c index 03412cf5663..c798c8595e7 100644 --- a/mysys/thr_lock.c +++ b/mysys/thr_lock.c @@ -357,7 +357,8 @@ static void check_locks(THR_LOCK *lock, const char *where, data && count < MAX_LOCKS; data=data->next) { - if (data->type != TL_WRITE_CONCURRENT_INSERT) + if (data->type != TL_WRITE_CONCURRENT_INSERT && + data->type != TL_WRITE_ALLOW_WRITE) { fprintf(stderr, "Warning at '%s': Found TL_WRITE_CONCURRENT_INSERT lock mixed with other write lock: %d\n", @@ -1540,7 +1541,7 @@ void thr_downgrade_write_lock(THR_LOCK_DATA *in_data, enum thr_lock_type new_lock_type) { THR_LOCK *lock=in_data->lock; -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS enum thr_lock_type old_lock_type= in_data->type; #endif DBUG_ENTER("thr_downgrade_write_only_lock"); @@ -1683,31 +1684,35 @@ void thr_print_locks(void) uint count=0; mysql_mutex_lock(&THR_LOCK_lock); - puts("Current locks:"); + puts("Current active THR (table level locks):"); for (list= thr_lock_thread_list; list && count++ < MAX_THREADS; list= list_rest(list)) { THR_LOCK *lock=(THR_LOCK*) list->data; mysql_mutex_lock(&lock->mutex); - printf("lock:%p:", lock); - if ((lock->write_wait.data || lock->read_wait.data) && - (! lock->read.data && ! lock->write.data)) - printf(" WARNING: "); - if (lock->write.data) - printf(" write"); - if (lock->write_wait.data) - printf(" write_wait"); - if (lock->read.data) - printf(" read"); - if (lock->read_wait.data) - printf(" read_wait"); - puts(""); - thr_print_lock("write",&lock->write); - thr_print_lock("write_wait",&lock->write_wait); - thr_print_lock("read",&lock->read); - thr_print_lock("read_wait",&lock->read_wait); + if ((lock->write.data || lock->read.data || + lock->write_wait.data || lock->read_wait.data)) + { + printf("lock: %p:", lock); + if ((lock->write_wait.data || lock->read_wait.data) && + (! lock->read.data && ! lock->write.data)) + printf(" WARNING: "); + if (lock->write.data) + printf(" write"); + if (lock->write_wait.data) + printf(" write_wait"); + if (lock->read.data) + printf(" read"); + if (lock->read_wait.data) + printf(" read_wait"); + puts(""); + thr_print_lock("write",&lock->write); + thr_print_lock("write_wait",&lock->write_wait); + thr_print_lock("read",&lock->read); + thr_print_lock("read_wait",&lock->read_wait); + puts(""); + } mysql_mutex_unlock(&lock->mutex); - puts(""); } fflush(stdout); mysql_mutex_unlock(&THR_LOCK_lock); diff --git a/mysys/thr_mutex.c b/mysys/thr_mutex.c index f6971c890bd..568ed3ecb46 100644 --- a/mysys/thr_mutex.c +++ b/mysys/thr_mutex.c @@ -149,10 +149,11 @@ static inline void remove_from_active_list(safe_mutex_t *mp) } /* - We initialise the hashes for deadlock detection lazily. - This greatly helps with performance when lots of mutexes are initiased but - only a few of them are actually used (eg. XtraDB). + We initialize the hashes for deadlock detection lazily. + This greatly helps with performance when lots of mutexes are initialized but + only a few of them are actually used (eg. InnoDB). */ + static int safe_mutex_lazy_init_deadlock_detection(safe_mutex_t *mp) { if (!my_multi_malloc(MY_FAE | MY_WME, @@ -236,16 +237,16 @@ int safe_mutex_lock(safe_mutex_t *mp, myf my_flags, const char *file, int error; DBUG_PRINT("mutex", ("%s (0x%lx) locking", mp->name ? mp->name : "Null", (ulong) mp)); + + pthread_mutex_lock(&mp->global); if (!mp->file) { fprintf(stderr, - "safe_mutex: Trying to lock unitialized mutex at %s, line %d\n", + "safe_mutex: Trying to lock uninitialized mutex at %s, line %d\n", file, line); fflush(stderr); abort(); } - - pthread_mutex_lock(&mp->global); if (mp->count > 0) { /* @@ -584,7 +585,7 @@ int safe_mutex_destroy(safe_mutex_t *mp, const char *file, uint line) if (!mp->file) { fprintf(stderr, - "safe_mutex: Trying to destroy unitialized mutex at %s, line %d\n", + "safe_mutex: Trying to destroy uninitialized mutex at %s, line %d\n", file, line); fflush(stderr); abort(); diff --git a/mysys/tree.c b/mysys/tree.c index ccf0aa05509..8cae30e2d3e 100644 --- a/mysys/tree.c +++ b/mysys/tree.c @@ -66,7 +66,7 @@ #define DEFAULT_ALLOC_SIZE 8192 #define DEFAULT_ALIGN_SIZE 8192 -static void delete_tree_element(TREE *,TREE_ELEMENT *); +static int delete_tree_element(TREE *,TREE_ELEMENT *, my_bool abort); static int tree_walk_left_root_right(TREE *,TREE_ELEMENT *, tree_walk_action,void *); static int tree_walk_right_root_left(TREE *,TREE_ELEMENT *, @@ -128,28 +128,37 @@ void init_tree(TREE *tree, size_t default_alloc_size, size_t memory_limit, } if (!(tree->with_delete= MY_TEST(my_flags & MY_TREE_WITH_DELETE))) { - init_alloc_root(&tree->mem_root, default_alloc_size, 0, MYF(my_flags)); + init_alloc_root(&tree->mem_root, "tree", default_alloc_size, 0, + MYF(my_flags)); tree->mem_root.min_malloc= sizeof(TREE_ELEMENT)+tree->size_of_element; } DBUG_VOID_RETURN; } -static void free_tree(TREE *tree, myf free_flags) +static int free_tree(TREE *tree, my_bool abort, myf free_flags) { + int error, first_error= 0; DBUG_ENTER("free_tree"); DBUG_PRINT("enter",("tree: %p", tree)); if (tree->root) /* If initialized */ { if (tree->with_delete) - delete_tree_element(tree,tree->root); + { + if ((error= delete_tree_element(tree, tree->root, abort))) + { + first_error= first_error ? first_error : error; + abort= 1; + } + } else { if (tree->free) { if (tree->memory_limit) (*tree->free)(NULL, free_init, tree->custom_arg); - delete_tree_element(tree,tree->root); + if ((error= delete_tree_element(tree, tree->root, abort))) + first_error= first_error ? first_error : error; if (tree->memory_limit) (*tree->free)(NULL, free_end, tree->custom_arg); } @@ -160,32 +169,61 @@ static void free_tree(TREE *tree, myf free_flags) tree->elements_in_tree=0; tree->allocated=0; - DBUG_VOID_RETURN; + DBUG_RETURN(first_error); } -void delete_tree(TREE* tree) + +/** + Delete tree. + + @param tree Tree + @param abort 0 if normal, 1 if tree->free should not be called. + + @return 0 ok + <> 0 Returns first <> 0 from (tree->free)(*,free_free,*) + + @Notes + If one (tree->free)(,free_free,) returns <> 0, no future + tree->free(*,free_free,*) will be called. + Other tree->free operations (free_init, free_end) will be called +*/ + + +int delete_tree(TREE* tree, my_bool abort) { - free_tree(tree, MYF(0)); /* my_free() mem_root if applicable */ + return free_tree(tree, abort, MYF(0)); /* my_free() mem_root if applicable */ } -void reset_tree(TREE* tree) +int reset_tree(TREE* tree) { /* do not free mem_root, just mark blocks as free */ - free_tree(tree, MYF(MY_MARK_BLOCKS_FREE)); + return free_tree(tree, 0, MYF(MY_MARK_BLOCKS_FREE)); } -static void delete_tree_element(TREE *tree, TREE_ELEMENT *element) +static int delete_tree_element(TREE *tree, TREE_ELEMENT *element, + my_bool abort) { + int error, first_error= 0; if (element != &null_element) { - delete_tree_element(tree,element->left); - if (tree->free) - (*tree->free)(ELEMENT_KEY(tree,element), free_free, tree->custom_arg); - delete_tree_element(tree,element->right); + if ((first_error= delete_tree_element(tree, element->left, abort))) + abort= 1; + if (!abort && tree->free) + { + if ((error= (*tree->free)(ELEMENT_KEY(tree,element), free_free, + tree->custom_arg))) + { + first_error= first_error ? first_error : error; + abort= 1; + } + } + if ((error= delete_tree_element(tree, element->right, abort))) + first_error= first_error ? first_error : error; if (tree->with_delete) my_free(element); } + return first_error; } diff --git a/mysys/waiting_threads.c b/mysys/waiting_threads.c index 35f19aa7edb..4564059b31a 100644 --- a/mysys/waiting_threads.c +++ b/mysys/waiting_threads.c @@ -172,6 +172,7 @@ !! thread4 should not wait for thread2. */ +#include <my_global.h> #include <waiting_threads.h> #include <m_string.h> @@ -617,7 +618,7 @@ retry: { rc= *shared_ptr; lf_pin(arg->thd->pins, 0, rc); - } while (rc != *shared_ptr && LF_BACKOFF); + } while (rc != *shared_ptr && LF_BACKOFF()); if (rc == 0) { diff --git a/mysys/wqueue.c b/mysys/wqueue.c index 15785048040..84fac01fda4 100644 --- a/mysys/wqueue.c +++ b/mysys/wqueue.c @@ -15,6 +15,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ +#include <my_global.h> #include <wqueue.h> #define STRUCT_PTR(TYPE, MEMBER, a) \ |