diff options
Diffstat (limited to 'mysys')
-rw-r--r-- | mysys/Makefile.am | 3 | ||||
-rw-r--r-- | mysys/default.c | 190 | ||||
-rw-r--r-- | mysys/default_modify.c | 65 | ||||
-rw-r--r-- | mysys/my_bitmap.c | 33 | ||||
-rw-r--r-- | mysys/my_tempnam.c | 173 | ||||
-rw-r--r-- | mysys/thr_lock.c | 184 |
6 files changed, 302 insertions, 346 deletions
diff --git a/mysys/Makefile.am b/mysys/Makefile.am index ad075f3324a..6b983dc38dd 100644 --- a/mysys/Makefile.am +++ b/mysys/Makefile.am @@ -45,7 +45,7 @@ libmysys_a_SOURCES = my_init.c my_getwd.c mf_getdate.c my_mmap.c \ ptr_cmp.c mf_radix.c queues.c \ tree.c trie.c list.c hash.c array.c string.c typelib.c \ my_copy.c my_append.c my_lib.c \ - my_delete.c my_rename.c my_redel.c my_tempnam.c \ + my_delete.c my_rename.c my_redel.c \ my_chsize.c my_lread.c my_lwrite.c my_clock.c \ my_quick.c my_lockmem.c my_static.c \ my_sync.c my_getopt.c my_mkdir.c \ @@ -69,6 +69,7 @@ DEFS = -DDEFAULT_BASEDIR=\"$(prefix)\" \ -DDEFAULT_CHARSET_HOME="\"$(MYSQLBASEdir)\"" \ -DSHAREDIR="\"$(MYSQLSHAREdir)\"" \ -DDEFAULT_HOME_ENV=MYSQL_HOME \ + -DDEFAULT_GROUP_SUFFIX_ENV=MYSQL_GROUP_SUFFIX \ @DEFS@ libmysys_a_DEPENDENCIES= @THREAD_LOBJECTS@ diff --git a/mysys/default.c b/mysys/default.c index 15c92e816a6..6b78d031291 100644 --- a/mysys/default.c +++ b/mysys/default.c @@ -30,8 +30,8 @@ --no-defaults ; no options are read. --defaults-file=full-path-to-default-file ; Only this file will be read. --defaults-extra-file=full-path-to-default-file ; Read this file before ~/ - --print-defaults ; Print the modified command line and exit - --instance ; also read groups with concat(group, instance) + --defaults-group-suffix ; Also read groups with concat(group, suffix) + --print-defaults ; Print the modified command line and exit ****************************************************************************/ #include "mysys_priv.h" @@ -42,8 +42,7 @@ #include <winbase.h> #endif -const char *defaults_instance=0; -static const char instance_option[] = "--instance="; +const char *defaults_group_suffix=0; char *defaults_extra_file=0; /* Which directories are searched for options (and in which order) */ @@ -61,6 +60,9 @@ static const char *f_extensions[]= { ".cnf", 0 }; #define NEWLINE "\n" #endif +static int handle_default_option(void *in_ctx, const char *group_name, + const char *option); + /* This structure defines the context that we pass to callback function 'handle_default_option' used in search_default_file @@ -101,35 +103,81 @@ static char *remove_end_comment(char *ptr); func_ctx It's context. Usually it is the structure to store additional options. DESCRIPTION + Process the default options from argc & argv + Read through each found config file looks and calls 'func' to process + each option. + + NOTES + --defaults-group-suffix is only processed if we are called from + load_defaults(). - This function looks for config files in default directories. Then it - travesrses each of the files and calls func to process each option. RETURN 0 ok 1 given cinf_file doesn't exist + + The global variable 'defaults_group_suffix' is updated with value for + --defaults_group_suffix */ int my_search_option_files(const char *conf_file, int *argc, char ***argv, - uint *args_used, Process_option_func func, - void *func_ctx) + uint *args_used, Process_option_func func, + void *func_ctx) { const char **dirs, *forced_default_file, *forced_extra_defaults; int error= 0; DBUG_ENTER("my_search_option_files"); /* Check if we want to force the use a specific default file */ - get_defaults_files(*argc - *args_used, *argv + *args_used, - (char **)&forced_default_file, - (char **)&forced_extra_defaults); - if (forced_default_file) - forced_default_file= strchr(forced_default_file,'=')+1; - if (forced_extra_defaults) - defaults_extra_file= strchr(forced_extra_defaults,'=')+1; + *args_used+= get_defaults_options(*argc - *args_used, *argv + *args_used, + (char **) &forced_default_file, + (char **) &forced_extra_defaults, + (char **) &defaults_group_suffix); - (*args_used)+= (forced_default_file ? 1 : 0) + - (forced_extra_defaults ? 1 : 0); + if (! defaults_group_suffix) + defaults_group_suffix= getenv(STRINGIFY_ARG(DEFAULT_GROUP_SUFFIX_ENV)); + + /* + We can only handle 'defaults-group-suffix' if we are called from + load_defaults() as otherwise we can't know the type of 'func_ctx' + */ + if (defaults_group_suffix && func == handle_default_option) + { + /* Handle --defaults-group-suffix= */ + uint i; + const char **extra_groups; + const uint instance_len= strlen(defaults_group_suffix); + struct handle_option_ctx *ctx= (struct handle_option_ctx*) func_ctx; + char *ptr; + TYPELIB *group= ctx->group; + + if (!(extra_groups= + (const char**)alloc_root(ctx->alloc, + (2*group->count+1)*sizeof(char*)))) + goto err; + + for (i= 0; i < group->count; i++) + { + uint len; + extra_groups[i]= group->type_names[i]; /** copy group */ + + len= strlen(extra_groups[i]); + if (!(ptr= alloc_root(ctx->alloc, len+instance_len+1))) + goto err; + + extra_groups[i+group->count]= ptr; + + /** Construct new group */ + memcpy(ptr, extra_groups[i], len); + memcpy(ptr+len, defaults_group_suffix, instance_len+1); + } + + group->count*= 2; + group->type_names= extra_groups; + group->type_names[group->count]= 0; + } + if (forced_default_file) { if ((error= search_default_file_with_ext(func, func_ctx, "", "", @@ -222,32 +270,54 @@ static int handle_default_option(void *in_ctx, const char *group_name, /* - Gets --defaults-file and --defaults-extra-file options from command line. + Gets options from the command line SYNOPSIS - get_defaults_files() + get_defaults_options() argc Pointer to argc of original program argv Pointer to argv of original program defaults --defaults-file option extra_defaults --defaults-extra-file option RETURN - defaults and extra_defaults will be set to appropriate items - of argv array, or to NULL if there are no such options + # Number of arguments used from *argv + defaults and extra_defaults will be set to option of the appropriate + items of argv array, or to NULL if there are no such options */ -void get_defaults_files(int argc, char **argv, - char **defaults, char **extra_defaults) +int get_defaults_options(int argc, char **argv, + char **defaults, + char **extra_defaults, + char **group_suffix) { - *defaults=0; - *extra_defaults=0; - if (argc >= 2) + int org_argc= argc, prev_argc= 0; + *defaults= *extra_defaults= *group_suffix= 0; + + while (argc >= 2 && argc != prev_argc) { - if (is_prefix(argv[1],"--defaults-file=")) - *defaults= argv[1]; - else if (is_prefix(argv[1],"--defaults-extra-file=")) - *extra_defaults= argv[1]; + /* Skip program name or previously handled argument */ + argv++; + prev_argc= argc; /* To check if we found */ + if (!*defaults && is_prefix(*argv,"--defaults-file=")) + { + *defaults= *argv + sizeof("--defaults-file=")-1; + argc--; + continue; + } + if (!*extra_defaults && is_prefix(*argv,"--defaults-extra-file=")) + { + *extra_defaults= *argv + sizeof("--defaults-extra-file=")-1; + argc--; + continue; + } + if (!*group_suffix && is_prefix(*argv, "--defaults-group-suffix=")) + { + *group_suffix= *argv + sizeof("--defaults-group-suffix=")-1; + argc--; + continue; + } } + return org_argc - argc; } @@ -297,6 +367,10 @@ int load_defaults(const char *conf_file, const char **groups, init_default_directories(); init_alloc_root(&alloc,512,0); + /* + Check if the user doesn't want any default option processing + --no-defaults is always the first option + */ if (*argc >= 2 && !strcmp(argv[0][1],"--no-defaults")) { /* remove the --no-defaults argument and return only the other arguments */ @@ -329,51 +403,8 @@ int load_defaults(const char *conf_file, const char **groups, ctx.args= &args; ctx.group= &group; - if (*argc >= 2 && - is_prefix(argv[0][1], instance_option)) - { - args_used++; - defaults_instance= argv[0][args_used]+sizeof(instance_option)-1; - } - else - { - defaults_instance= getenv("MYSQL_INSTANCE"); - } - - if (defaults_instance) - { - /** Handle --instance= */ - uint i, len; - const char **extra_groups; - const uint instance_len= strlen(defaults_instance); - - if (!(extra_groups= - (const char**)alloc_root(&alloc, (2*group.count+1)*sizeof(char*)))) - goto err; - - for (i= 0; i<group.count; i++) - { - extra_groups[i]= group.type_names[i]; /** copy group */ - - len= strlen(extra_groups[i]); - if (!(ptr= alloc_root(&alloc, len+instance_len+1))) - goto err; - - extra_groups[i+group.count]= ptr; - - /** Construct new group */ - memcpy(ptr, extra_groups[i], len); - ptr+= len; - memcpy(ptr, defaults_instance, instance_len+1); - } - - group.count*= 2; - group.type_names= extra_groups; - group.type_names[group.count]= 0; - } - error= my_search_option_files(conf_file, argc, argv, &args_used, - handle_default_option, (void *) &ctx); + handle_default_option, (void *) &ctx); /* Here error contains <> 0 only if we have a fully specified conf_file or a forced default file @@ -386,11 +417,14 @@ int load_defaults(const char *conf_file, const char **groups, /* copy name + found arguments + command line arguments to new array */ res[0]= argv[0][0]; /* Name MUST be set, even by embedded library */ memcpy((gptr) (res+1), args.buffer, args.elements*sizeof(char*)); - /* Skip --defaults-file and --defaults-extra-file */ + /* Skip --defaults-xxx options */ (*argc)-= args_used; (*argv)+= args_used; - /* Check if we wan't to see the new argument list */ + /* + Check if we wan't to see the new argument list + This options must always be the last of the default options + */ if (*argc >= 2 && !strcmp(argv[0][1],"--print-defaults")) { found_print_defaults=1; @@ -851,14 +885,14 @@ void print_defaults(const char *conf_file, const char **groups) fputs(*groups,stdout); } - if (defaults_instance) + if (defaults_group_suffix) { groups= groups_save; for ( ; *groups ; groups++) { fputc(' ',stdout); fputs(*groups,stdout); - fputs(defaults_instance,stdout); + fputs(defaults_group_suffix,stdout); } } puts("\nThe following options may be given as the first argument:\n\ diff --git a/mysys/default_modify.c b/mysys/default_modify.c index ea384f9f27a..de03d783c68 100644 --- a/mysys/default_modify.c +++ b/mysys/default_modify.c @@ -20,8 +20,7 @@ #include <my_dir.h> #define BUFF_SIZE 1024 -/* should be big enough to handle at least one line */ -#define RESERVE 1024 +#define RESERVE 1024 /* Extend buffer with this extent */ #ifdef __WIN__ #define NEWLINE "\r\n" @@ -70,7 +69,7 @@ int modify_defaults_file(const char *file_location, const char *option, char linebuff[BUFF_SIZE], *src_ptr, *dst_ptr, *file_buffer; uint opt_len, optval_len, sect_len, nr_newlines= 0, buffer_size; my_bool in_section= FALSE, opt_applied= 0; - uint reserve_extended= 1, old_opt_len= 0; + uint reserve_extended; uint new_opt_len; int reserve_occupied= 0; DBUG_ENTER("modify_defaults_file"); @@ -88,25 +87,21 @@ int modify_defaults_file(const char *file_location, const char *option, new_opt_len= opt_len + 1 + optval_len + NEWLINE_LEN; /* calculate the size of the buffer we need */ - buffer_size= sizeof(char) * (file_stat.st_size + - /* option name len */ - opt_len + - /* reserve for '=' char */ - 1 + - /* option value len */ - optval_len + - /* reserve space for newline */ - NEWLINE_LEN + - /* The ending zero */ - 1 + - /* reserve some additional space */ - RESERVE); + reserve_extended= (opt_len + + 1 + /* For '=' char */ + optval_len + /* Option value len */ + NEWLINE_LEN + /* Space for newline */ + RESERVE); /* Some additional space */ + + buffer_size= (file_stat.st_size + + 1); /* The ending zero */ /* Reserve space to read the contents of the file and some more for the option we want to add. */ - if (!(file_buffer= (char*) my_malloc(buffer_size, MYF(MY_WME)))) + if (!(file_buffer= (char*) my_malloc(buffer_size + reserve_extended, + MYF(MY_WME)))) goto malloc_err; sect_len= (uint) strlen(section_name); @@ -130,31 +125,20 @@ int modify_defaults_file(const char *file_location, const char *option, my_isspace(&my_charset_latin1, *(src_ptr + opt_len)) || *(src_ptr + opt_len) == '\0')) { - /* - we should change all options. If opt_applied is set, we are running - into reserved memory area. Hence we should check for overruns. - */ - if (opt_applied) + char *old_src_ptr= src_ptr; + src_ptr= strend(src_ptr+ opt_len); /* Find the end of the line */ + + /* could be negative */ + reserve_occupied+= (int) new_opt_len - (int) (src_ptr - old_src_ptr); + if (reserve_occupied >= (int) reserve_extended) { - src_ptr+= opt_len; /* If we correct an option, we know it's name */ - old_opt_len= opt_len; - - while (*src_ptr++) /* Find the end of the line */ - old_opt_len++; - - /* could be negative */ - reserve_occupied+= (int) new_opt_len - (int) old_opt_len; - if ((int) reserve_occupied > (int) (RESERVE*reserve_extended)) - { - if (!(file_buffer= (char*) my_realloc(file_buffer, buffer_size + - RESERVE*reserve_extended, - MYF(MY_WME|MY_FREE_ON_ERROR)))) - goto malloc_err; - reserve_extended++; - } + reserve_extended= (uint) reserve_occupied + RESERVE; + if (!(file_buffer= (char*) my_realloc(file_buffer, buffer_size + + reserve_extended, + MYF(MY_WME|MY_FREE_ON_ERROR)))) + goto malloc_err; } - else - opt_applied= 1; + opt_applied= 1; dst_ptr= add_option(dst_ptr, option_value, option, remove_option); } else @@ -164,6 +148,7 @@ int modify_defaults_file(const char *file_location, const char *option, { dst_ptr= add_option(dst_ptr, option_value, option, remove_option); opt_applied= 1; /* set the flag to do write() later */ + reserve_occupied= new_opt_len+ opt_len + 1 + NEWLINE_LEN; } for (; nr_newlines; nr_newlines--) diff --git a/mysys/my_bitmap.c b/mysys/my_bitmap.c index b5f928fee1e..ec03e345109 100644 --- a/mysys/my_bitmap.c +++ b/mysys/my_bitmap.c @@ -202,6 +202,7 @@ my_bool bitmap_test_and_set(MY_BITMAP *map, uint bitmap_bit) bitmap_lock(map); res= bitmap_fast_test_and_set(map, bitmap_bit); bitmap_unlock(map); + return res; } uint bitmap_set_next(MY_BITMAP *map) @@ -327,10 +328,40 @@ void bitmap_intersect(MY_BITMAP *map, const MY_BITMAP *map2) } +/* + Set/clear all bits above a bit. + + SYNOPSIS + bitmap_set_above() + map RETURN The bitmap to change. + from_byte The bitmap buffer byte offset to start with. + use_bit The bit value (1/0) to use for all upper bits. + + NOTE + You can only set/clear full bytes. + The function is meant for the situation that you copy a smaller bitmap + to a bigger bitmap. Bitmap lengths are always multiple of eigth (the + size of a byte). Using 'from_byte' saves multiplication and division + by eight during parameter passing. + + RETURN + void +*/ + +void bitmap_set_above(MY_BITMAP *map, uint from_byte, uint use_bit) +{ + uchar use_byte= use_bit ? 0xff : 0; + uchar *to= map->bitmap + from_byte; + uchar *end= map->bitmap + map->bitmap_size; + + while (to < end) + *to++= use_byte; +} + + void bitmap_subtract(MY_BITMAP *map, const MY_BITMAP *map2) { uint32 *to= map->bitmap, *from= map2->bitmap, *end; - DBUG_ASSERT(map->bitmap && map2->bitmap && map->n_bits==map2->n_bits); diff --git a/mysys/my_tempnam.c b/mysys/my_tempnam.c deleted file mode 100644 index 9f765298fb6..00000000000 --- a/mysys/my_tempnam.c +++ /dev/null @@ -1,173 +0,0 @@ -/* Copyright (C) 2000 MySQL AB - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -/* - This function is only used by some old ISAM code. - When we remove ISAM support from MySQL, we should also delete this file - - One should instead use the functions in mf_tempfile.c -*/ - -#include "mysys_priv.h" -#include <m_string.h> - -/* HPUX 11.0 doesn't allow us to change the environ pointer */ -#ifdef HPUX11 -#undef HAVE_TEMPNAM -#endif - -#include "my_static.h" -#include "mysys_err.h" - -#define TMP_EXT ".tmp" /* Extension of tempfile */ -#if ! defined(P_tmpdir) -#define P_tmpdir "" -#endif - -#ifdef HAVE_TEMPNAM -#if !defined( MSDOS) && !defined(OS2) && !defined(__NETWARE__) -extern char **environ; -#endif -#endif - -/* Make a uniq temp file name by using dir and adding something after - pfx to make name uniq. Name is made by adding a uniq 8 length-string and - TMP_EXT after pfx. - Returns pointer to malloced area for filename. Should be freed by - free(). - The name should be uniq, but it isn't checked if it file allready exists. - Uses tempnam() if function exist on system. - This function fixes that if dir is given it's used. For example - MSDOS tempnam() uses always TMP environment-variable if it exists. -*/ - /* ARGSUSED */ - -my_string my_tempnam(const char *dir, const char *pfx, - myf MyFlags __attribute__((unused))) -{ -#ifdef _MSC_VER - char temp[FN_REFLEN],*end,*res,**old_env,*temp_env[1]; - old_env=environ; - if (dir) - { - end=strend(dir)-1; - if (!dir[0]) - { /* Change empty string to current dir */ - temp[0]= FN_CURLIB; - temp[1]= 0; - dir=temp; - } - else if (*end == FN_DEVCHAR) - { /* Get current dir for drive */ - _fullpath(temp,dir,FN_REFLEN); - dir=temp; - } - else if (*end == FN_LIBCHAR && dir < end && end[-1] != FN_DEVCHAR) - { - strmake(temp,dir,(uint) (end-dir)); /* Copy and remove last '\' */ - dir=temp; - } - environ=temp_env; /* Force use of dir (dir not checked) */ - temp_env[0]=0; - } - res=tempnam((char*) dir,(my_string) pfx); - environ=old_env; - return res; -#else -#ifdef __ZTC__ - if (!dir) - { /* If empty test first if TMP can be used */ - dir=getenv("TMP"); - } - return tempnam((char*) dir,(my_string) pfx); /* Use stand. dir with prefix */ -#else -#ifdef HAVE_TEMPNAM - char temp[2],*res,**old_env,*temp_env[1]; - - if (dir && !dir[0]) - { /* Change empty string to current dir */ - temp[0]= FN_CURLIB; - temp[1]= 0; - dir=temp; - } -#ifdef OS2 - /* changing environ variable doesn't work with VACPP */ - char buffer[256], *end; - buffer[sizeof[buffer)-1]= 0; - end= strxnmov(buffer, sizeof(buffer)-1, (char*) "TMP=", dir, NullS); - /* remove ending backslash */ - if (end[-1] == '\\') - end[-1]= 0; - putenv(buffer); -#elif !defined(__NETWARE__) - old_env=(char**)environ; - if (dir) - { /* Don't use TMPDIR if dir is given */ - /* - The following strange cast is required because the IBM compiler on AIX - doesn't allow us to cast the value of environ. - The cast of environ is needed as some systems doesn't allow us to - update environ with a char ** pointer. (const mismatch) - */ - (*(char***) &environ)=(char**) temp_env; - temp_env[0]=0; - } -#endif - res=tempnam((char*) dir,(my_string) pfx); /* Use stand. dir with prefix */ -#if !defined(OS2) && !defined(__NETWARE__) - (*(char***) &environ)=(char**) old_env; -#endif - if (!res) - DBUG_PRINT("error",("Got error: %d from tempnam",errno)); - return res; -#else - register long uniq; - register int length; - my_string pos,end_pos; - DBUG_ENTER("my_tempnam"); - /* Make a uniq nummber */ - pthread_mutex_lock(&THR_LOCK_open); - uniq= ((long) getpid() << 20) + (long) _my_tempnam_used++ ; - pthread_mutex_unlock(&THR_LOCK_open); - if (!dir && !(dir=getenv("TMPDIR"))) /* Use this if possibly */ - dir=P_tmpdir; /* Use system default */ - length=strlen(dir)+strlen(pfx)+1; - - DBUG_PRINT("test",("mallocing %d byte",length+8+sizeof(TMP_EXT)+1)); - if (!(pos=(char*) malloc(length+8+sizeof(TMP_EXT)+1))) - { - if (MyFlags & MY_FAE+MY_WME) - my_error(EE_OUTOFMEMORY, MYF(ME_BELL+ME_WAITTANG), - length+8+sizeof(TMP_EXT)+1); - DBUG_RETURN(NullS); - } - end_pos=strmov(pos,dir); - if (end_pos != pos && end_pos[-1] != FN_LIBCHAR) - *end_pos++=FN_LIBCHAR; - end_pos=strmov(end_pos,pfx); - - for (length=0 ; length < 8 && uniq ; length++) - { - *end_pos++= _dig_vec_upper[(int) (uniq & 31)]; - uniq >>= 5; - } - VOID(strmov(end_pos,TMP_EXT)); - DBUG_PRINT("exit",("tempnam: '%s'",pos)); - DBUG_RETURN(pos); -#endif /* HAVE_TEMPNAM */ -#endif /* __ZTC__ */ -#endif /* _MSC_VER */ -} /* my_tempnam */ diff --git a/mysys/thr_lock.c b/mysys/thr_lock.c index 85a2b34b851..b12f8234c26 100644 --- a/mysys/thr_lock.c +++ b/mysys/thr_lock.c @@ -84,6 +84,7 @@ multiple read locks. my_bool thr_lock_inited=0; ulong locks_immediate = 0L, locks_waited = 0L; +ulong table_lock_wait_timeout; enum thr_lock_type thr_upgraded_concurrent_insert_lock = TL_WRITE; /* The following constants are only for debug output */ @@ -109,25 +110,32 @@ my_bool init_thr_lock() return 0; } +static inline my_bool +thr_lock_owner_equal(THR_LOCK_OWNER *rhs, THR_LOCK_OWNER *lhs) +{ + return rhs == lhs; +} + + #ifdef EXTRA_DEBUG #define MAX_FOUND_ERRORS 10 /* Report 10 first errors */ static uint found_errors=0; static int check_lock(struct st_lock_list *list, const char* lock_type, - const char *where, my_bool same_thread, bool no_cond) + const char *where, my_bool same_owner, bool no_cond) { THR_LOCK_DATA *data,**prev; uint count=0; - pthread_t first_thread; - LINT_INIT(first_thread); + THR_LOCK_OWNER *first_owner; + LINT_INIT(first_owner); prev= &list->data; if (list->data) { enum thr_lock_type last_lock_type=list->data->type; - if (same_thread && list->data) - first_thread=list->data->thread; + if (same_owner && list->data) + first_owner= list->data->owner; for (data=list->data; data && count++ < MAX_LOCKS ; data=data->next) { if (data->type != last_lock_type) @@ -139,7 +147,8 @@ static int check_lock(struct st_lock_list *list, const char* lock_type, count, lock_type, where); return 1; } - if (same_thread && ! pthread_equal(data->thread,first_thread) && + if (same_owner && + !thr_lock_owner_equal(data->owner, first_owner) && last_lock_type != TL_WRITE_ALLOW_WRITE) { fprintf(stderr, @@ -255,8 +264,8 @@ static void check_locks(THR_LOCK *lock, const char *where, } if (lock->read.data) { - if (!pthread_equal(lock->write.data->thread, - lock->read.data->thread) && + if (!thr_lock_owner_equal(lock->write.data->owner, + lock->read.data->owner) && ((lock->write.data->type > TL_WRITE_DELAYED && lock->write.data->type != TL_WRITE_ONLY) || ((lock->write.data->type == TL_WRITE_CONCURRENT_INSERT || @@ -330,24 +339,32 @@ void thr_lock_delete(THR_LOCK *lock) DBUG_VOID_RETURN; } + +void thr_lock_info_init(THR_LOCK_INFO *info) +{ + info->thread= pthread_self(); + info->thread_id= my_thread_id(); /* for debugging */ + info->n_cursors= 0; +} + /* Initialize a lock instance */ void thr_lock_data_init(THR_LOCK *lock,THR_LOCK_DATA *data, void *param) { data->lock=lock; data->type=TL_UNLOCK; - data->thread=pthread_self(); - data->thread_id=my_thread_id(); /* for debugging */ + data->owner= 0; /* no owner yet */ data->status_param=param; data->cond=0; } -static inline my_bool have_old_read_lock(THR_LOCK_DATA *data,pthread_t thread) +static inline my_bool +have_old_read_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner) { for ( ; data ; data=data->next) { - if ((pthread_equal(data->thread,thread))) + if (thr_lock_owner_equal(data->owner, owner)) return 1; /* Already locked by thread */ } return 0; @@ -365,12 +382,16 @@ static inline my_bool have_specific_lock(THR_LOCK_DATA *data, } -static my_bool wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data, - my_bool in_wait_list) +static enum enum_thr_lock_result +wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data, + my_bool in_wait_list) { - pthread_cond_t *cond=get_cond(); - struct st_my_thread_var *thread_var=my_thread_var; - int result; + struct st_my_thread_var *thread_var= my_thread_var; + pthread_cond_t *cond= &thread_var->suspend; + struct timeval now; + struct timespec wait_timeout; + enum enum_thr_lock_result result= THR_LOCK_ABORTED; + my_bool can_deadlock= test(data->owner->info->n_cursors); if (!in_wait_list) { @@ -382,31 +403,56 @@ static my_bool wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data, /* Set up control struct to allow others to abort locks */ thread_var->current_mutex= &data->lock->mutex; thread_var->current_cond= cond; + data->cond= cond; - data->cond=cond; + if (can_deadlock) + { + gettimeofday(&now, 0); + wait_timeout.tv_sec= now.tv_sec + table_lock_wait_timeout; + wait_timeout.tv_nsec= now.tv_usec * 1000; + } while (!thread_var->abort || in_wait_list) { - pthread_cond_wait(cond,&data->lock->mutex); - if (data->cond != cond) + int rc= can_deadlock ? pthread_cond_timedwait(cond, &data->lock->mutex, + &wait_timeout) : + pthread_cond_wait(cond, &data->lock->mutex); + /* + We must break the wait if one of the following occurs: + - the connection has been aborted (!thread_var->abort), but + this is not a delayed insert thread (in_wait_list). For a delayed + insert thread the proper action at shutdown is, apparently, to + acquire the lock and complete the insert. + - the lock has been granted (data->cond is set to NULL by the granter), + or the waiting has been aborted (additionally data->type is set to + TL_UNLOCK). + - the wait has timed out (rc == ETIMEDOUT) + Order of checks below is important to not report about timeout + if the predicate is true. + */ + if (data->cond == 0) + break; + if (rc == ETIMEDOUT) + { + result= THR_LOCK_WAIT_TIMEOUT; break; + } } if (data->cond || data->type == TL_UNLOCK) { - if (data->cond) /* aborted */ + if (data->cond) /* aborted or timed out */ { if (((*data->prev)=data->next)) /* remove from wait-list */ data->next->prev= data->prev; else wait->last=data->prev; + data->type= TL_UNLOCK; /* No lock */ } - data->type=TL_UNLOCK; /* No lock */ - result=1; /* Didn't get lock */ check_locks(data->lock,"failed wait_for_lock",0); } else { - result=0; + result= THR_LOCK_SUCCESS; statistic_increment(locks_waited, &THR_LOCK_lock); if (data->lock->get_status) (*data->lock->get_status)(data->status_param, 0); @@ -423,20 +469,24 @@ static my_bool wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data, } -int thr_lock(THR_LOCK_DATA *data,enum thr_lock_type lock_type) +enum enum_thr_lock_result +thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner, + enum thr_lock_type lock_type) { THR_LOCK *lock=data->lock; - int result=0; + enum enum_thr_lock_result result= THR_LOCK_SUCCESS; + struct st_lock_list *wait_queue; + THR_LOCK_DATA *lock_owner; DBUG_ENTER("thr_lock"); data->next=0; data->cond=0; /* safety */ data->type=lock_type; - data->thread=pthread_self(); /* Must be reset ! */ - data->thread_id=my_thread_id(); /* Must be reset ! */ + data->owner= owner; /* Must be reset ! */ VOID(pthread_mutex_lock(&lock->mutex)); DBUG_PRINT("lock",("data: 0x%lx thread: %ld lock: 0x%lx type: %d", - data,data->thread_id,lock,(int) lock_type)); + data, data->owner->info->thread_id, + lock, (int) lock_type)); check_locks(lock,(uint) lock_type <= (uint) TL_READ_NO_INSERT ? "enter read_lock" : "enter write_lock",0); if ((int) lock_type <= (int) TL_READ_NO_INSERT) @@ -454,8 +504,8 @@ int thr_lock(THR_LOCK_DATA *data,enum thr_lock_type lock_type) */ DBUG_PRINT("lock",("write locked by thread: %ld", - lock->write.data->thread_id)); - if (pthread_equal(data->thread,lock->write.data->thread) || + lock->write.data->owner->info->thread_id)); + if (thr_lock_owner_equal(data->owner, lock->write.data->owner) || (lock->write.data->type <= TL_WRITE_DELAYED && (((int) lock_type <= (int) TL_READ_HIGH_PRIORITY) || (lock->write.data->type != TL_WRITE_CONCURRENT_INSERT && @@ -476,14 +526,14 @@ int thr_lock(THR_LOCK_DATA *data,enum thr_lock_type lock_type) { /* We are not allowed to get a READ lock in this case */ data->type=TL_UNLOCK; - result=1; /* Can't wait for this one */ + result= THR_LOCK_ABORTED; /* Can't wait for this one */ goto end; } } else if (!lock->write_wait.data || lock->write_wait.data->type <= TL_WRITE_LOW_PRIORITY || lock_type == TL_READ_HIGH_PRIORITY || - have_old_read_lock(lock->read.data,data->thread)) + have_old_read_lock(lock->read.data, data->owner)) { /* No important write-locks */ (*lock->read.last)=data; /* Add to running FIFO */ data->prev=lock->read.last; @@ -496,8 +546,12 @@ int thr_lock(THR_LOCK_DATA *data,enum thr_lock_type lock_type) statistic_increment(locks_immediate,&THR_LOCK_lock); goto end; } - /* Can't get lock yet; Wait for it */ - DBUG_RETURN(wait_for_lock(&lock->read_wait,data,0)); + /* + We're here if there is an active write lock or no write + lock but a high priority write waiting in the write_wait queue. + In the latter case we should yield the lock to the writer. + */ + wait_queue= &lock->read_wait; } else /* Request for WRITE lock */ { @@ -506,7 +560,7 @@ int thr_lock(THR_LOCK_DATA *data,enum thr_lock_type lock_type) if (lock->write.data && lock->write.data->type == TL_WRITE_ONLY) { data->type=TL_UNLOCK; - result=1; /* Can't wait for this one */ + result= THR_LOCK_ABORTED; /* Can't wait for this one */ goto end; } /* @@ -540,7 +594,7 @@ int thr_lock(THR_LOCK_DATA *data,enum thr_lock_type lock_type) { /* We are not allowed to get a lock in this case */ data->type=TL_UNLOCK; - result=1; /* Can't wait for this one */ + result= THR_LOCK_ABORTED; /* Can't wait for this one */ goto end; } @@ -549,7 +603,7 @@ int thr_lock(THR_LOCK_DATA *data,enum thr_lock_type lock_type) TL_WRITE_ALLOW_WRITE, TL_WRITE_ALLOW_READ or TL_WRITE_DELAYED in the same thread, but this will never happen within MySQL. */ - if (pthread_equal(data->thread,lock->write.data->thread) || + if (thr_lock_owner_equal(data->owner, lock->write.data->owner) || (lock_type == TL_WRITE_ALLOW_WRITE && !lock->write_wait.data && lock->write.data->type == TL_WRITE_ALLOW_WRITE)) @@ -572,7 +626,7 @@ int thr_lock(THR_LOCK_DATA *data,enum thr_lock_type lock_type) goto end; } DBUG_PRINT("lock",("write locked by thread: %ld", - lock->write.data->thread_id)); + lock->write.data->owner->info->thread_id)); } else { @@ -608,10 +662,24 @@ int thr_lock(THR_LOCK_DATA *data,enum thr_lock_type lock_type) } } DBUG_PRINT("lock",("write locked by thread: %ld, type: %ld", - lock->read.data->thread_id,data->type)); + lock->read.data->owner->info->thread_id, data->type)); } - DBUG_RETURN(wait_for_lock(&lock->write_wait,data,0)); + wait_queue= &lock->write_wait; } + /* + Try to detect a trivial deadlock when using cursors: attempt to + lock a table that is already locked by an open cursor within the + same connection. lock_owner can be zero if we succumbed to a high + priority writer in the write_wait queue. + */ + lock_owner= lock->read.data ? lock->read.data : lock->write.data; + if (lock_owner && lock_owner->owner->info == owner->info) + { + result= THR_LOCK_DEADLOCK; + goto end; + } + /* Can't get lock yet; Wait for it */ + DBUG_RETURN(wait_for_lock(wait_queue, data, 0)); end: pthread_mutex_unlock(&lock->mutex); DBUG_RETURN(result); @@ -656,7 +724,7 @@ static inline void free_all_read_locks(THR_LOCK *lock, lock->read_no_write_count++; } DBUG_PRINT("lock",("giving read lock to thread: %ld", - data->thread_id)); + data->owner->info->thread_id)); data->cond=0; /* Mark thread free */ VOID(pthread_cond_signal(cond)); } while ((data=data->next)); @@ -674,7 +742,7 @@ void thr_unlock(THR_LOCK_DATA *data) enum thr_lock_type lock_type=data->type; DBUG_ENTER("thr_unlock"); DBUG_PRINT("lock",("data: 0x%lx thread: %ld lock: 0x%lx", - data,data->thread_id,lock)); + data, data->owner->info->thread_id, lock)); pthread_mutex_lock(&lock->mutex); check_locks(lock,"start of release lock",0); @@ -734,7 +802,7 @@ void thr_unlock(THR_LOCK_DATA *data) (*lock->check_status)(data->status_param)) data->type=TL_WRITE; /* Upgrade lock */ DBUG_PRINT("lock",("giving write lock of type %d to thread: %ld", - data->type,data->thread_id)); + data->type, data->owner->info->thread_id)); { pthread_cond_t *cond=data->cond; data->cond=0; /* Mark thread free */ @@ -842,7 +910,8 @@ static void sort_locks(THR_LOCK_DATA **data,uint count) } -int thr_multi_lock(THR_LOCK_DATA **data,uint count) +enum enum_thr_lock_result +thr_multi_lock(THR_LOCK_DATA **data, uint count, THR_LOCK_OWNER *owner) { THR_LOCK_DATA **pos,**end; DBUG_ENTER("thr_multi_lock"); @@ -852,10 +921,11 @@ int thr_multi_lock(THR_LOCK_DATA **data,uint count) /* lock everything */ for (pos=data,end=data+count; pos < end ; pos++) { - if (thr_lock(*pos,(*pos)->type)) + enum enum_thr_lock_result result= thr_lock(*pos, owner, (*pos)->type); + if (result != THR_LOCK_SUCCESS) { /* Aborted */ thr_multi_unlock(data,(uint) (pos-data)); - DBUG_RETURN(1); + DBUG_RETURN(result); } #ifdef MAIN printf("Thread: %s Got lock: 0x%lx type: %d\n",my_thread_name(), @@ -909,7 +979,7 @@ int thr_multi_lock(THR_LOCK_DATA **data,uint count) } while (pos != data); } #endif - DBUG_RETURN(0); + DBUG_RETURN(THR_LOCK_SUCCESS); } /* free all locks */ @@ -932,7 +1002,7 @@ void thr_multi_unlock(THR_LOCK_DATA **data,uint count) else { DBUG_PRINT("lock",("Free lock: data: 0x%lx thread: %ld lock: 0x%lx", - *pos,(*pos)->thread_id,(*pos)->lock)); + *pos, (*pos)->owner->info->thread_id, (*pos)->lock)); } } DBUG_VOID_RETURN; @@ -952,6 +1022,7 @@ void thr_abort_locks(THR_LOCK *lock) for (data=lock->read_wait.data; data ; data=data->next) { data->type=TL_UNLOCK; /* Mark killed */ + /* It's safe to signal the cond first: we're still holding the mutex. */ pthread_cond_signal(data->cond); data->cond=0; /* Removed from list */ } @@ -985,10 +1056,11 @@ void thr_abort_locks_for_thread(THR_LOCK *lock, pthread_t thread) pthread_mutex_lock(&lock->mutex); for (data= lock->read_wait.data; data ; data= data->next) { - if (pthread_equal(thread, data->thread)) + if (pthread_equal(thread, data->owner->info->thread)) { DBUG_PRINT("info",("Aborting read-wait lock")); data->type= TL_UNLOCK; /* Mark killed */ + /* It's safe to signal the cond first: we're still holding the mutex. */ pthread_cond_signal(data->cond); data->cond= 0; /* Removed from list */ @@ -1000,7 +1072,7 @@ void thr_abort_locks_for_thread(THR_LOCK *lock, pthread_t thread) } for (data= lock->write_wait.data; data ; data= data->next) { - if (pthread_equal(thread, data->thread)) + if (pthread_equal(thread, data->owner->info->thread)) { DBUG_PRINT("info",("Aborting write-wait lock")); data->type= TL_UNLOCK; @@ -1117,7 +1189,8 @@ static void thr_print_lock(const char* name,struct st_lock_list *list) prev= &list->data; for (data=list->data; data && count++ < MAX_LOCKS ; data=data->next) { - printf("0x%lx (%lu:%d); ",(ulong) data,data->thread_id,(int) data->type); + printf("0x%lx (%lu:%d); ", (ulong) data, data->owner->info->thread_id, + (int) data->type); if (data->prev != prev) printf("\nWarning: prev didn't point at previous lock\n"); prev= &data->next; @@ -1250,11 +1323,16 @@ static void *test_thread(void *arg) { int i,j,param=*((int*) arg); THR_LOCK_DATA data[MAX_LOCK_COUNT]; + THR_LOCK_OWNER owner; + THR_LOCK_INFO lock_info; THR_LOCK_DATA *multi_locks[MAX_LOCK_COUNT]; my_thread_init(); printf("Thread %s (%d) started\n",my_thread_name(),param); fflush(stdout); + + thr_lock_info_init(&lock_info); + thr_lock_owner_init(&owner, &lock_info); for (i=0; i < lock_counts[param] ; i++) thr_lock_data_init(locks+tests[param][i].lock_nr,data+i,NULL); for (j=1 ; j < 10 ; j++) /* try locking 10 times */ @@ -1264,7 +1342,7 @@ static void *test_thread(void *arg) multi_locks[i]= &data[i]; data[i].type= tests[param][i].lock_type; } - thr_multi_lock(multi_locks,lock_counts[param]); + thr_multi_lock(multi_locks, lock_counts[param], &owner); pthread_mutex_lock(&LOCK_thread_count); { int tmp=rand() & 7; /* Do something from 0-2 sec */ |