summaryrefslogtreecommitdiff
path: root/mysys
diff options
context:
space:
mode:
Diffstat (limited to 'mysys')
-rw-r--r--mysys/Makefile.am3
-rw-r--r--mysys/default.c190
-rw-r--r--mysys/default_modify.c65
-rw-r--r--mysys/my_bitmap.c33
-rw-r--r--mysys/my_tempnam.c173
-rw-r--r--mysys/thr_lock.c184
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 */