summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--KNOWN_BUGS.txt19
-rw-r--r--include/hash.h2
-rw-r--r--include/my_pthread.h51
-rw-r--r--mysys/Makefile.am10
-rw-r--r--mysys/hash.c59
-rw-r--r--mysys/my_init.c5
-rw-r--r--mysys/my_pthread.c6
-rw-r--r--mysys/my_sleep.c2
-rw-r--r--mysys/my_thr_init.c121
-rw-r--r--mysys/mysys_priv.h3
-rw-r--r--mysys/thr_mutex.c426
-rw-r--r--sql/event_queue.cc7
-rw-r--r--sql/event_scheduler.cc8
-rw-r--r--sql/events.cc7
-rw-r--r--sql/ha_ndbcluster_binlog.cc4
-rw-r--r--sql/log.cc7
-rw-r--r--sql/mysqld.cc55
-rw-r--r--sql/protocol.cc4
-rw-r--r--sql/rpl_mi.cc19
-rw-r--r--sql/set_var.cc24
-rw-r--r--sql/sp_cache.cc6
-rw-r--r--sql/sp_cache.h1
-rw-r--r--sql/sql_class.cc46
-rw-r--r--sql/sql_insert.cc11
-rw-r--r--sql/sql_show.cc5
-rw-r--r--storage/innobase/handler/ha_innodb.cc9
-rw-r--r--storage/maria/ha_maria.cc11
-rw-r--r--storage/maria/ha_maria.h1
-rw-r--r--storage/maria/ma_close.c9
-rw-r--r--storage/maria/ma_key.c13
-rw-r--r--storage/maria/ma_loghandler.c4
-rw-r--r--storage/maria/ma_state.c16
-rw-r--r--storage/maria/ma_test1.c3
-rw-r--r--storage/maria/ma_test2.c4
34 files changed, 832 insertions, 146 deletions
diff --git a/KNOWN_BUGS.txt b/KNOWN_BUGS.txt
index 12f9c2fa123..189c7dcd613 100644
--- a/KNOWN_BUGS.txt
+++ b/KNOWN_BUGS.txt
@@ -20,14 +20,29 @@ If you have found a bug that is not listed here, please add it to
http://bugs.mysql.com/ so that we can either fix it for next release
or in the worst case add it here for others to know!
+IMPORTANT:
+
+If you have been using a MySQL-5.1-Maria-alpha build and upgrading to
+MySQL-5.1-Maria-beta you MUST run maria_chk --recover on all your
+Maria tables. This is because we made an incompatible change of how
+transaction id is stored and old transaction id's must be reset!
+
+cd mysql-data-directory
+maria_chk --recover */*.MAI
+
+As the Maria-1.5 engine is now in beta we will do our best to not
+introduce any incompatible changes in the data format for the Maria
+tables; If this would be ever be needed, we will, if possible, support
+both the old and the new version to make upgrades as easy as possible.
Known bugs that we are working on and will be fixed shortly
===========================================================
-- We have some instabilities in log writing that is under investigatation
+- We have some time ago some instabilities in log writing that is was
+ under investigation but we haven't been able to repeat in a while.
This causes mainly assert to triggers in the code and sometimes
the log handler doesn't start up after restart.
- Most of this should now be fixed...
+ Most of this should now be fixed.
- INSERT on a duplicate key against a key inserted by another connection
that has not yet ended will give a duplicate key error instead of
diff --git a/include/hash.h b/include/hash.h
index 4ca8dc0e8bf..5a338bb63f2 100644
--- a/include/hash.h
+++ b/include/hash.h
@@ -32,6 +32,7 @@ extern "C" {
typedef uchar *(*hash_get_key)(const uchar *,size_t*,my_bool);
typedef void (*hash_free_key)(void *);
+typedef my_bool (*hash_walk_action)(void *,void *);
typedef struct st_hash {
size_t key_offset,key_length; /* Length of key if const length */
@@ -66,6 +67,7 @@ my_bool hash_delete(HASH *hash,uchar *record);
my_bool hash_update(HASH *hash,uchar *record,uchar *old_key,size_t old_key_length);
void hash_replace(HASH *hash, HASH_SEARCH_STATE *state, uchar *new_row);
my_bool hash_check(HASH *hash); /* Only in debug library */
+my_bool hash_iterate(HASH *hash, hash_walk_action action, void *argument);
#define hash_clear(H) bzero((char*) (H),sizeof(*(H)))
#define hash_inited(H) ((H)->array.buffer != 0)
diff --git a/include/my_pthread.h b/include/my_pthread.h
index 9df8a9e6a65..2df664006e9 100644
--- a/include/my_pthread.h
+++ b/include/my_pthread.h
@@ -239,13 +239,13 @@ int my_sigwait(const sigset_t *set,int *sig);
#ifdef HAVE_NONPOSIX_PTHREAD_MUTEX_INIT
#ifndef SAFE_MUTEX
-#define pthread_mutex_init(a,b) my_pthread_mutex_init((a),(b))
-extern int my_pthread_mutex_init(pthread_mutex_t *mp,
- const pthread_mutexattr_t *attr);
+#define pthread_mutex_init(a,b) my_pthread_mutex_noposix_init((a),(b))
+extern int my_pthread_mutex_noposix_init(pthread_mutex_t *mp,
+ const pthread_mutexattr_t *attr);
#endif /* SAFE_MUTEX */
-#define pthread_cond_init(a,b) my_pthread_cond_init((a),(b))
-extern int my_pthread_cond_init(pthread_cond_t *mp,
- const pthread_condattr_t *attr);
+#define pthread_cond_init(a,b) my_pthread_cond_noposix_init((a),(b))
+extern int my_pthread_cond_noposix_init(pthread_cond_t *mp,
+ const pthread_condattr_t *attr);
#endif /* HAVE_NONPOSIX_PTHREAD_MUTEX_INIT */
#if defined(HAVE_SIGTHREADMASK) && !defined(HAVE_PTHREAD_SIGMASK)
@@ -449,18 +449,33 @@ int my_pthread_mutex_trylock(pthread_mutex_t *mutex);
#if defined(__NETWARE__) && !defined(SAFE_MUTEX_DETECT_DESTROY)
#define SAFE_MUTEX_DETECT_DESTROY
#endif
+struct st_hash;
typedef struct st_safe_mutex_t
{
pthread_mutex_t global,mutex;
const char *file, *name;
uint line,count;
+ myf create_flags, active_flags;
+ ulong id;
pthread_t thread;
+ struct st_hash *locked_mutex, *used_mutex;
+ struct st_safe_mutex_t *prev, *next;
#ifdef SAFE_MUTEX_DETECT_DESTROY
struct st_safe_mutex_info_t *info; /* to track destroying of mutexes */
#endif
} safe_mutex_t;
+typedef struct st_safe_mutex_deadlock_t
+{
+ const char *file, *name;
+ safe_mutex_t *mutex;
+ uint line;
+ ulong count;
+ ulong id;
+ my_bool warning_only;
+} safe_mutex_deadlock_t;
+
#ifdef SAFE_MUTEX_DETECT_DESTROY
/*
Used to track the destroying of mutexes. This needs to be a seperate
@@ -478,8 +493,10 @@ typedef struct st_safe_mutex_info_t
#endif /* SAFE_MUTEX_DETECT_DESTROY */
int safe_mutex_init(safe_mutex_t *mp, const pthread_mutexattr_t *attr,
- const char *file, uint line, const char *name);
-int safe_mutex_lock(safe_mutex_t *mp, my_bool try_lock, const char *file, uint line);
+ const char *name, myf my_flags,
+ const char *file, uint line);
+int safe_mutex_lock(safe_mutex_t *mp, myf my_flags, const char *file,
+ uint line);
int safe_mutex_unlock(safe_mutex_t *mp,const char *file, uint line);
int safe_mutex_destroy(safe_mutex_t *mp,const char *file, uint line);
int safe_cond_wait(pthread_cond_t *cond, safe_mutex_t *mp,const char *file,
@@ -488,8 +505,12 @@ int safe_cond_timedwait(pthread_cond_t *cond, safe_mutex_t *mp,
struct timespec *abstime, const char *file, uint line);
void safe_mutex_global_init(void);
void safe_mutex_end(FILE *file);
+void safe_mutex_free_deadlock_data(safe_mutex_t *mp);
/* Wrappers if safe mutex is actually used */
+#define MYF_TRY_LOCK 1
+#define MYF_NO_DEADLOCK_DETECTION 2
+
#ifdef SAFE_MUTEX
#undef pthread_mutex_init
#undef pthread_mutex_lock
@@ -501,13 +522,15 @@ void safe_mutex_end(FILE *file);
#undef pthread_cond_wait
#undef pthread_cond_timedwait
#undef pthread_mutex_trylock
-#define pthread_mutex_init(A,B) safe_mutex_init((A),(B),__FILE__,__LINE__,#A)
-#define pthread_mutex_lock(A) safe_mutex_lock((A), FALSE, __FILE__, __LINE__)
+#define my_pthread_mutex_init(A,B,C,D) safe_mutex_init((A),(B),(C),(D),__FILE__,__LINE__)
+#define pthread_mutex_init(A,B) safe_mutex_init((A),(B),#A,0,__FILE__,__LINE__)
+#define pthread_mutex_lock(A) safe_mutex_lock((A), 0, __FILE__, __LINE__)
+#define my_pthread_mutex_lock(A,B) safe_mutex_lock((A), (B), __FILE__, __LINE__)
#define pthread_mutex_unlock(A) safe_mutex_unlock((A),__FILE__,__LINE__)
#define pthread_mutex_destroy(A) safe_mutex_destroy((A),__FILE__,__LINE__)
#define pthread_cond_wait(A,B) safe_cond_wait((A),(B),__FILE__,__LINE__)
#define pthread_cond_timedwait(A,B,C) safe_cond_timedwait((A),(B),(C),__FILE__,__LINE__)
-#define pthread_mutex_trylock(A) safe_mutex_lock((A), TRUE, __FILE__, __LINE__)
+#define pthread_mutex_trylock(A) safe_mutex_lock((A), MYF_TRY_LOCK, __FILE__, __LINE__)
#define pthread_mutex_t safe_mutex_t
#define safe_mutex_assert_owner(mp) \
DBUG_ASSERT((mp)->count > 0 && \
@@ -516,8 +539,11 @@ void safe_mutex_end(FILE *file);
DBUG_ASSERT(! (mp)->count || \
! pthread_equal(pthread_self(), (mp)->thread))
#else
+#define my_pthread_mutex_init(A,B,C,D) pthread_mutex_init((A),(B))
+#define my_pthread_mutex_lock(A,B) pthread_mutex_lock(A)
#define safe_mutex_assert_owner(mp)
#define safe_mutex_assert_not_owner(mp)
+#define safe_mutex_free_deadlock_data(mp)
#endif /* SAFE_MUTEX */
#if defined(MY_PTHREAD_FASTMUTEX) && !defined(SAFE_MUTEX)
@@ -685,6 +711,7 @@ struct st_my_thread_var
void *opt_info;
uint lock_type; /* used by conditional release the queue */
void *stack_ends_here;
+ safe_mutex_t *mutex_in_use;
#ifndef DBUG_OFF
void *dbug;
char name[THREAD_NAME_SIZE+1];
@@ -693,7 +720,9 @@ struct st_my_thread_var
extern struct st_my_thread_var *_my_thread_var(void) __attribute__ ((const));
extern void **my_thread_var_dbug();
+extern safe_mutex_t **my_thread_var_mutex_in_use();
extern uint my_thread_end_wait_time;
+extern my_bool safe_mutex_deadlock_detector;
#define my_thread_var (_my_thread_var())
#define my_errno my_thread_var->thr_errno
/*
diff --git a/mysys/Makefile.am b/mysys/Makefile.am
index e5e7539ece6..6efdd0d75e7 100644
--- a/mysys/Makefile.am
+++ b/mysys/Makefile.am
@@ -79,6 +79,13 @@ DEFS = -DDEFAULT_BASEDIR=\"$(prefix)\" \
# I hope this always does the right thing. Otherwise this is only test programs
FLAGS=$(DEFS) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) @NOINST_LDFLAGS@
+CLEANFILES = test_bitmap$(EXEEXT) test_priority_queue$(EXEEXT) \
+ test_thr_alarm$(EXEEXT) test_thr_lock$(EXEEXT) \
+ test_vsnprintf$(EXEEXT) test_io_cache$(EXEEXT) \
+ test_dir$(EXEEXT) test_charset$(EXEEXT) \
+ testhash$(EXEEXT) test_gethwaddr$(EXEEXT) \
+ test_base64$(EXEEXT) test_thr_mutex$(EXEEXT)
+
#
# The CP .. RM stuff is to avoid problems with some compilers (like alpha ccc)
# which automaticly removes the object files you use to compile a final program
@@ -129,6 +136,9 @@ test_base64$(EXEEXT): base64.c $(LIBRARIES)
$(LINK) $(FLAGS) -DMAIN ./test_base64.c $(LDADD) $(LIBS)
$(RM) -f ./test_base64.c
+test_thr_mutex$(EXEEXT): test_thr_mutex.c $(LIBRARIES)
+ $(LINK) $(FLAGS) $(srcdir)/test_thr_mutex.c $(LDADD) $(LIBS)
+
# Don't update the files from bitkeeper
%::SCCS/s.%
diff --git a/mysys/hash.c b/mysys/hash.c
index 3a9f05a3e0b..0d3f79bc40f 100644
--- a/mysys/hash.c
+++ b/mysys/hash.c
@@ -304,7 +304,13 @@ static int hashcmp(const HASH *hash, HASH_LINK *pos, const uchar *key,
}
- /* Write a hash-key to the hash-index */
+/**
+ Write a hash-key to the hash-index
+
+ @return
+ @retval 0 ok
+ @retval 1 Duplicate key or out of memory
+*/
my_bool my_hash_insert(HASH *info,const uchar *record)
{
@@ -443,11 +449,21 @@ my_bool my_hash_insert(HASH *info,const uchar *record)
}
-/******************************************************************************
-** Remove one record from hash-table. The record with the same record
-** ptr is removed.
-** if there is a free-function it's called for record if found
-******************************************************************************/
+/**
+ Remove one record from hash-table.
+
+ @fn hash_delete()
+ @param hash Hash tree
+ @param record Row to be deleted
+
+ @notes
+ The record with the same record ptr is removed.
+ If there is a free-function it's called if record was found.
+
+ @return
+ @retval 0 ok
+ @retval 1 Record not found
+*/
my_bool hash_delete(HASH *hash,uchar *record)
{
@@ -656,6 +672,37 @@ void hash_replace(HASH *hash, HASH_SEARCH_STATE *current_record, uchar *new_row)
}
+/**
+ Iterate over all elements in hash and call function with the element
+
+ @param hash hash array
+ @param action function to call for each argument
+ @param argument second argument for call to action
+
+ @notes
+ If one of functions calls returns 1 then the iteration aborts
+
+ @retval 0 ok
+ @retval 1 iteration aborted becasue action returned 1
+*/
+
+my_bool hash_iterate(HASH *hash, hash_walk_action action, void *argument)
+{
+ uint records, i;
+ HASH_LINK *data;
+
+ records= hash->records;
+ data= dynamic_element(&hash->array,0,HASH_LINK*);
+
+ for (i= 0 ; i < records ; i++)
+ {
+ if ((*action)(data[i].data, argument))
+ return 1;
+ }
+ return 0;
+}
+
+
#ifndef DBUG_OFF
my_bool hash_check(HASH *hash)
diff --git a/mysys/my_init.c b/mysys/my_init.c
index a153275f87e..453e62b19bb 100644
--- a/mysys/my_init.c
+++ b/mysys/my_init.c
@@ -165,6 +165,9 @@ void my_end(int infoflag)
free_charsets();
my_error_unregister_all();
my_once_free();
+#ifdef THREAD
+ my_thread_destroy_mutex();
+#endif
if ((infoflag & MY_GIVE_INFO) || print_info)
{
@@ -195,6 +198,8 @@ Voluntary context switches %ld, Involuntary context switches %ld\n",
fprintf(info_file,"\nRun time: %.1f\n",(double) clock()/CLOCKS_PER_SEC);
#endif
#if defined(SAFEMALLOC)
+ /* Wait for other threads to free mysys_var */
+ (void) my_wait_for_other_threads_to_die(1);
TERMINATE(stderr, (infoflag & MY_GIVE_INFO) != 0);
#elif defined(__WIN__) && defined(_MSC_VER)
_CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE );
diff --git a/mysys/my_pthread.c b/mysys/my_pthread.c
index aba3e47d754..e97bbe89be0 100644
--- a/mysys/my_pthread.c
+++ b/mysys/my_pthread.c
@@ -429,7 +429,8 @@ int sigwait(sigset_t *setp, int *sigp)
#include <netdb.h>
-int my_pthread_mutex_init(pthread_mutex_t *mp, const pthread_mutexattr_t *attr)
+int my_pthread_mutex_noposix_init(pthread_mutex_t *mp,
+ const pthread_mutexattr_t *attr)
{
int error;
if (!attr)
@@ -439,7 +440,8 @@ int my_pthread_mutex_init(pthread_mutex_t *mp, const pthread_mutexattr_t *attr)
return error;
}
-int my_pthread_cond_init(pthread_cond_t *mp, const pthread_condattr_t *attr)
+int my_pthread_cond_noposix_init(pthread_cond_t *mp,
+ const pthread_condattr_t *attr)
{
int error;
if (!attr)
diff --git a/mysys/my_sleep.c b/mysys/my_sleep.c
index 87170e4af41..cb21c15a925 100644
--- a/mysys/my_sleep.c
+++ b/mysys/my_sleep.c
@@ -30,7 +30,7 @@ void my_sleep(ulong m_seconds)
t.tv_usec= m_seconds % 1000000L;
select(0,0,0,0,&t); /* sleep */
#else
- uint sec= (uint) (m_seconds / 1000000L);
+ uint sec= (uint) ((m_seconds + 999999L) / 1000000L);
ulong start= (ulong) time((time_t*) 0);
while ((ulong) time((time_t*) 0) < start+sec);
#endif
diff --git a/mysys/my_thr_init.c b/mysys/my_thr_init.c
index 1d03577ce34..6ebd1f512f1 100644
--- a/mysys/my_thr_init.c
+++ b/mysys/my_thr_init.c
@@ -115,6 +115,15 @@ my_bool my_thread_global_init(void)
}
#endif /* TARGET_OS_LINUX */
+ /* Mutex used by my_thread_init() and after my_thread_destroy_mutex() */
+ my_pthread_mutex_init(&THR_LOCK_threads, MY_MUTEX_INIT_FAST,
+ "THR_LOCK_threads", MYF_NO_DEADLOCK_DETECTION);
+ my_pthread_mutex_init(&THR_LOCK_malloc, MY_MUTEX_INIT_FAST,
+ "THR_LOCK_malloc", MYF_NO_DEADLOCK_DETECTION);
+
+ if (my_thread_init())
+ return 1;
+
#ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
/*
Set mutex type to "fast" a.k.a "adaptive"
@@ -138,7 +147,7 @@ my_bool my_thread_global_init(void)
PTHREAD_MUTEX_ERRORCHECK);
#endif
- pthread_mutex_init(&THR_LOCK_malloc,MY_MUTEX_INIT_FAST);
+ /* Mutex uses by mysys */
pthread_mutex_init(&THR_LOCK_open,MY_MUTEX_INIT_FAST);
pthread_mutex_init(&THR_LOCK_lock,MY_MUTEX_INIT_FAST);
pthread_mutex_init(&THR_LOCK_isam,MY_MUTEX_INIT_SLOW);
@@ -146,7 +155,6 @@ my_bool my_thread_global_init(void)
pthread_mutex_init(&THR_LOCK_heap,MY_MUTEX_INIT_FAST);
pthread_mutex_init(&THR_LOCK_net,MY_MUTEX_INIT_FAST);
pthread_mutex_init(&THR_LOCK_charset,MY_MUTEX_INIT_FAST);
- pthread_mutex_init(&THR_LOCK_threads,MY_MUTEX_INIT_FAST);
pthread_mutex_init(&THR_LOCK_time,MY_MUTEX_INIT_FAST);
pthread_cond_init(&THR_COND_threads, NULL);
#if defined( __WIN__) || defined(OS2)
@@ -158,44 +166,64 @@ my_bool my_thread_global_init(void)
#ifndef HAVE_GETHOSTBYNAME_R
pthread_mutex_init(&LOCK_gethostbyname_r,MY_MUTEX_INIT_SLOW);
#endif
- if (my_thread_init())
- {
- my_thread_global_end(); /* Clean up */
- return 1;
- }
return 0;
}
-void my_thread_global_end(void)
+/**
+ Wait for all threads in system to die
+ @fn my_wait_for_other_threads_to_die()
+ @param number_of_threads Wait until this number of threads
+
+ @retval 0 Less or equal to number_of_threads left
+ @retval 1 Wait failed
+*/
+
+my_bool my_wait_for_other_threads_to_die(uint number_of_threads)
{
struct timespec abstime;
my_bool all_threads_killed= 1;
set_timespec(abstime, my_thread_end_wait_time);
pthread_mutex_lock(&THR_LOCK_threads);
- while (THR_thread_count > 0)
+ while (THR_thread_count > number_of_threads)
{
int error= pthread_cond_timedwait(&THR_COND_threads, &THR_LOCK_threads,
&abstime);
if (error == ETIMEDOUT || error == ETIME)
{
-#ifdef HAVE_PTHREAD_KILL
- /*
- We shouldn't give an error here, because if we don't have
- pthread_kill(), programs like mysqld can't ensure that all threads
- are killed when we enter here.
- */
- if (THR_thread_count)
- fprintf(stderr,
- "Error in my_thread_global_end(): %d threads didn't exit\n",
- THR_thread_count);
-#endif
all_threads_killed= 0;
break;
}
}
pthread_mutex_unlock(&THR_LOCK_threads);
+ return all_threads_killed;
+}
+
+
+/**
+ End the mysys thread system. Called when ending the last thread
+*/
+
+
+void my_thread_global_end(void)
+{
+ my_bool all_threads_killed;
+
+ if (!(all_threads_killed= my_wait_for_other_threads_to_die(0)))
+ {
+#ifdef HAVE_PTHREAD_KILL
+ /*
+ We shouldn't give an error here, because if we don't have
+ pthread_kill(), programs like mysqld can't ensure that all threads
+ are killed when we enter here.
+ */
+ if (THR_thread_count)
+ fprintf(stderr,
+ "Error in my_thread_global_end(): %d threads didn't exit\n",
+ THR_thread_count);
+#endif
+ }
pthread_key_delete(THR_KEY_mysys);
#ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
@@ -204,7 +232,25 @@ void my_thread_global_end(void)
#ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
pthread_mutexattr_destroy(&my_errorcheck_mutexattr);
#endif
- pthread_mutex_destroy(&THR_LOCK_malloc);
+ if (all_threads_killed)
+ {
+ pthread_mutex_destroy(&THR_LOCK_threads);
+ pthread_cond_destroy(&THR_COND_threads);
+ pthread_mutex_destroy(&THR_LOCK_malloc);
+ }
+}
+
+/* Free all mutex used by mysys */
+
+void my_thread_destroy_mutex(void)
+{
+ struct st_my_thread_var *tmp;
+ tmp= my_pthread_getspecific(struct st_my_thread_var*,THR_KEY_mysys);
+ if (tmp)
+ {
+ safe_mutex_free_deadlock_data(&tmp->mutex);
+ }
+
pthread_mutex_destroy(&THR_LOCK_open);
pthread_mutex_destroy(&THR_LOCK_lock);
pthread_mutex_destroy(&THR_LOCK_isam);
@@ -213,11 +259,6 @@ void my_thread_global_end(void)
pthread_mutex_destroy(&THR_LOCK_net);
pthread_mutex_destroy(&THR_LOCK_time);
pthread_mutex_destroy(&THR_LOCK_charset);
- if (all_threads_killed)
- {
- pthread_mutex_destroy(&THR_LOCK_threads);
- pthread_cond_destroy(&THR_COND_threads);
- }
#if !defined(HAVE_LOCALTIME_R) || !defined(HAVE_GMTIME_R)
pthread_mutex_destroy(&LOCK_localtime_r);
#endif
@@ -287,7 +328,8 @@ my_bool my_thread_init(void)
#else
tmp->pthread_self= pthread_self();
#endif
- pthread_mutex_init(&tmp->mutex,MY_MUTEX_INIT_FAST);
+ my_pthread_mutex_init(&tmp->mutex, MY_MUTEX_INIT_FAST, "mysys_var->mutex",
+ 0);
pthread_cond_init(&tmp->suspend, NULL);
tmp->stack_ends_here= &tmp + STACK_DIRECTION * my_thread_stack_size;
@@ -330,6 +372,13 @@ void my_thread_end(void)
#endif
if (tmp && tmp->init)
{
+
+#if !defined(__bsdi__) && !defined(__OpenBSD__)
+ /* bsdi and openbsd 3.5 dumps core here */
+ pthread_cond_destroy(&tmp->suspend);
+#endif
+ pthread_mutex_destroy(&tmp->mutex);
+
#if !defined(DBUG_OFF)
/* tmp->dbug is allocated inside DBUG library */
if (tmp->dbug)
@@ -339,12 +388,11 @@ void my_thread_end(void)
tmp->dbug=0;
}
#endif
-#if !defined(__bsdi__) && !defined(__OpenBSD__)
- /* bsdi and openbsd 3.5 dumps core here */
- pthread_cond_destroy(&tmp->suspend);
-#endif
- pthread_mutex_destroy(&tmp->mutex);
#if !defined(__WIN__) || defined(USE_TLS)
+#ifndef DBUG_OFF
+ /* To find bugs when accessing unallocated data */
+ bfill(tmp, sizeof(tmp), 0x8F);
+#endif
free(tmp);
#else
tmp->init= 0;
@@ -399,6 +447,15 @@ extern void **my_thread_var_dbug()
}
#endif
+/* Return pointer to mutex_in_use */
+
+safe_mutex_t **my_thread_var_mutex_in_use()
+{
+ struct st_my_thread_var *tmp=
+ my_pthread_getspecific(struct st_my_thread_var*,THR_KEY_mysys);
+ return tmp ? &tmp->mutex_in_use : 0;
+}
+
/****************************************************************************
Get name of current thread.
****************************************************************************/
diff --git a/mysys/mysys_priv.h b/mysys/mysys_priv.h
index 6e0959ae08c..113b64005f2 100644
--- a/mysys/mysys_priv.h
+++ b/mysys/mysys_priv.h
@@ -33,6 +33,7 @@ extern pthread_mutex_t THR_LOCK_charset, THR_LOCK_time;
#include <my_no_pthread.h>
#endif
+
/*
EDQUOT is used only in 3 C files only in mysys/. If it does not exist on
system, we set it to some value which can never happen.
@@ -42,3 +43,5 @@ extern pthread_mutex_t THR_LOCK_charset, THR_LOCK_time;
#endif
void my_error_unregister_all(void);
+void my_thread_destroy_mutex(void);
+my_bool my_wait_for_other_threads_to_die(uint number_of_threads);
diff --git a/mysys/thr_mutex.c b/mysys/thr_mutex.c
index aa46021a938..ddbe613cdae 100644
--- a/mysys/thr_mutex.c
+++ b/mysys/thr_mutex.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2000-2003 MySQL AB
+/* Copyright (C) 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc
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
@@ -24,6 +24,7 @@
#include "mysys_priv.h"
#include "my_static.h"
#include <m_string.h>
+#include <hash.h>
#ifndef DO_NOT_REMOVE_THREAD_WRAPPERS
/* Remove wrappers */
@@ -34,28 +35,68 @@
#undef pthread_mutex_destroy
#undef pthread_cond_wait
#undef pthread_cond_timedwait
+#undef safe_mutex_free_deadlock_data
#ifdef HAVE_NONPOSIX_PTHREAD_MUTEX_INIT
-#define pthread_mutex_init(a,b) my_pthread_mutex_init((a),(b))
+#define pthread_mutex_init(a,b) my_pthread_noposix_mutex_init((a),(b))
#endif
#endif /* DO_NOT_REMOVE_THREAD_WRAPPERS */
static pthread_mutex_t THR_LOCK_mutex;
static ulong safe_mutex_count= 0; /* Number of mutexes created */
+static ulong safe_mutex_id= 0;
+my_bool safe_mutex_deadlock_detector= 1; /* On by default */
+
#ifdef SAFE_MUTEX_DETECT_DESTROY
-static struct st_safe_mutex_info_t *safe_mutex_root= NULL;
+static struct st_safe_mutex_create_info_t *safe_mutex_create_root= NULL;
#endif
+static my_bool add_used_to_locked_mutex(safe_mutex_t *used_mutex,
+ safe_mutex_deadlock_t *locked_mutex);
+static my_bool add_to_locked_mutex(safe_mutex_deadlock_t *locked_mutex,
+ safe_mutex_t *current_mutex);
+static my_bool remove_from_locked_mutex(safe_mutex_t *mp,
+ safe_mutex_t *delete_mutex);
+static my_bool remove_from_used_mutex(safe_mutex_deadlock_t *locked_mutex,
+ safe_mutex_t *mutex);
+static void print_deadlock_warning(safe_mutex_t *new_mutex,
+ safe_mutex_t *conflicting_mutex);
+
void safe_mutex_global_init(void)
{
pthread_mutex_init(&THR_LOCK_mutex,MY_MUTEX_INIT_FAST);
+ safe_mutex_id= safe_mutex_count= 0;
+ safe_mutex_deadlock_detector= 1;
+
+#ifdef SAFE_MUTEX_DETECT_DESTROY
+ safe_mutex_create_root= 0;
+#endif
+}
+
+static inline void remove_from_active_list(safe_mutex_t *mp)
+{
+ if (!(mp->active_flags & (MYF_NO_DEADLOCK_DETECTION | MYF_TRY_LOCK)))
+ {
+ /* Remove mutex from active mutex linked list */
+ if (mp->next)
+ mp->next->prev= mp->prev;
+ if (mp->prev)
+ mp->prev->next= mp->next;
+ else
+ *my_thread_var_mutex_in_use()= mp->next;
+ }
+ mp->prev= mp->next= 0;
}
int safe_mutex_init(safe_mutex_t *mp,
const pthread_mutexattr_t *attr __attribute__((unused)),
+ const char *name,
+ myf my_flags,
const char *file,
- uint line, const char *name)
+ uint line)
{
+ DBUG_ENTER("safe_mutex_init");
+ DBUG_PRINT("enter",("mutex: 0x%lx name: %s", (ulong) mp, name));
bzero((char*) mp,sizeof(*mp));
pthread_mutex_init(&mp->global,MY_MUTEX_INIT_ERRCHK);
pthread_mutex_init(&mp->mutex,attr);
@@ -65,6 +106,36 @@ int safe_mutex_init(safe_mutex_t *mp,
/* Skip the very common '&' prefix from the autogenerated name */
mp->name= name[0] == '&' ? name + 1 : name;
+ if (safe_mutex_deadlock_detector && !( my_flags & MYF_NO_DEADLOCK_DETECTION))
+ {
+ if (!my_multi_malloc(MY_FAE | MY_WME,
+ &mp->locked_mutex, sizeof(*mp->locked_mutex),
+ &mp->used_mutex, sizeof(*mp->used_mutex), NullS))
+ {
+ /* Disable deadlock handling for this mutex */
+ my_flags|= MYF_NO_DEADLOCK_DETECTION;
+ }
+ else
+ {
+ pthread_mutex_lock(&THR_LOCK_mutex);
+ mp->id= ++safe_mutex_id;
+ pthread_mutex_unlock(&THR_LOCK_mutex);
+ hash_init(mp->locked_mutex, &my_charset_bin,
+ 1000,
+ offsetof(safe_mutex_deadlock_t, id),
+ sizeof(mp->id),
+ 0, 0, HASH_UNIQUE);
+ hash_init(mp->used_mutex, &my_charset_bin,
+ 1000,
+ offsetof(safe_mutex_t, id),
+ sizeof(mp->id),
+ 0, 0, HASH_UNIQUE);
+ }
+ }
+ else
+ my_flags|= MYF_NO_DEADLOCK_DETECTION;
+ mp->create_flags= my_flags;
+
#ifdef SAFE_MUTEX_DETECT_DESTROY
/*
Monitor the freeing of mutexes. This code depends on single thread init
@@ -72,7 +143,7 @@ int safe_mutex_init(safe_mutex_t *mp,
*/
if ((mp->info= (safe_mutex_info_t *) malloc(sizeof(safe_mutex_info_t))))
{
- struct st_safe_mutex_info_t *info =mp->info;
+ struct st_safe_mutex_info_t *info= mp->info;
info->init_file= file;
info->init_line= line;
@@ -80,20 +151,21 @@ int safe_mutex_init(safe_mutex_t *mp,
info->next= NULL;
pthread_mutex_lock(&THR_LOCK_mutex);
- if ((info->next= safe_mutex_root))
- safe_mutex_root->prev= info;
- safe_mutex_root= info;
+ if ((info->next= safe_mutex_create_root))
+ safe_mutex_create_root->prev= info;
+ safe_mutex_create_root= info;
safe_mutex_count++;
pthread_mutex_unlock(&THR_LOCK_mutex);
}
#else
thread_safe_increment(safe_mutex_count, &THR_LOCK_mutex);
#endif /* SAFE_MUTEX_DETECT_DESTROY */
- return 0;
+ DBUG_RETURN(0);
}
-int safe_mutex_lock(safe_mutex_t *mp, my_bool try_lock, const char *file, uint line)
+int safe_mutex_lock(safe_mutex_t *mp, myf my_flags, const char *file,
+ uint line)
{
int error;
DBUG_PRINT("mutex", ("%s (0x%lx) locking", mp->name ? mp->name : "Null",
@@ -110,12 +182,13 @@ int safe_mutex_lock(safe_mutex_t *mp, my_bool try_lock, const char *file, uint l
pthread_mutex_lock(&mp->global);
if (mp->count > 0)
{
- if (try_lock)
- {
- pthread_mutex_unlock(&mp->global);
- return EBUSY;
- }
- else if (pthread_equal(pthread_self(),mp->thread))
+ /*
+ Check that we are not trying to lock mutex twice. This is an error
+ even if we are using 'try_lock' as it's not portably what happens
+ if you lock the mutex many times and this is in any case bad
+ behaviour that should not be encouraged
+ */
+ if (pthread_equal(pthread_self(),mp->thread))
{
fprintf(stderr,
"safe_mutex: Trying to lock mutex at %s, line %d, when the"
@@ -143,7 +216,7 @@ int safe_mutex_lock(safe_mutex_t *mp, my_bool try_lock, const char *file, uint l
instead just return EBUSY, since this is the expected behaviour
of trylock().
*/
- if (try_lock)
+ if (my_flags & MYF_TRY_LOCK)
{
error= pthread_mutex_trylock(&mp->mutex);
if (error == EBUSY)
@@ -169,7 +242,93 @@ int safe_mutex_lock(safe_mutex_t *mp, my_bool try_lock, const char *file, uint l
}
mp->file= file;
mp->line= line;
+ mp->active_flags= mp->create_flags | my_flags;
pthread_mutex_unlock(&mp->global);
+
+ /* Deadlock detection */
+
+ mp->prev= mp->next= 0;
+ if (!(mp->active_flags & (MYF_TRY_LOCK | MYF_NO_DEADLOCK_DETECTION)))
+ {
+ safe_mutex_t **mutex_in_use= my_thread_var_mutex_in_use();
+
+ if (!mutex_in_use)
+ {
+ /* thread has not called my_thread_init() */
+ mp->active_flags|= MYF_NO_DEADLOCK_DETECTION;
+ }
+ else
+ {
+ safe_mutex_t *mutex_root;
+ if ((mutex_root= *mutex_in_use)) /* If not first locked */
+ {
+ /*
+ Protect locked_mutex against changes if a mutex is deleted
+ */
+ pthread_mutex_lock(&THR_LOCK_mutex);
+
+ if (!hash_search(mutex_root->locked_mutex, (uchar*) &mp->id, 0))
+ {
+ safe_mutex_deadlock_t *deadlock;
+ safe_mutex_t *mutex;
+
+ /* Create object to store mutex info */
+ if (!(deadlock= my_malloc(sizeof(*deadlock),
+ MYF(MY_ZEROFILL | MY_WME | MY_FAE))))
+ goto abort_loop;
+ deadlock->name= mp->name;
+ deadlock->id= mp->id;
+ deadlock->mutex= mp;
+ /* The following is useful for debugging wrong mutex usage */
+ deadlock->file= file;
+ deadlock->line= line;
+
+ /* Check if potential deadlock */
+ mutex= mutex_root;
+ do
+ {
+ if (hash_search(mp->locked_mutex, (uchar*) &mutex->id, 0))
+ {
+ print_deadlock_warning(mp, mutex);
+ /* Mark wrong usage to avoid future warnings for same error */
+ deadlock->warning_only= 1;
+ add_to_locked_mutex(deadlock, mutex_root);
+ DBUG_ASSERT(deadlock->count > 0);
+ goto abort_loop;
+ }
+ }
+ while ((mutex= mutex->next));
+
+ /*
+ Copy current mutex and all mutex that has been locked
+ after current mutex (mp->locked_mutex) to all mutex that
+ was locked before previous mutex (mutex_root->used_mutex)
+
+ For example if A->B would have been done before and we
+ are now locking (C) in B->C, then we would add C into
+ B->locked_mutex and A->locked_mutex
+ */
+ hash_iterate(mutex_root->used_mutex,
+ (hash_walk_action) add_used_to_locked_mutex,
+ deadlock);
+
+ /*
+ Copy all current mutex and all mutex locked after current one
+ into the prev mutex
+ */
+ add_used_to_locked_mutex(mutex_root, deadlock);
+ DBUG_ASSERT(deadlock->count > 0);
+ }
+ abort_loop:
+ pthread_mutex_unlock(&THR_LOCK_mutex);
+ }
+ /* Link mutex into mutex_in_use list */
+ if ((mp->next= *mutex_in_use))
+ (*mutex_in_use)->prev= mp;
+ *mutex_in_use= mp;
+ }
+ }
+
DBUG_PRINT("mutex", ("%s (0x%lx) locked", mp->name, (ulong) mp));
return error;
}
@@ -182,7 +341,9 @@ int safe_mutex_unlock(safe_mutex_t *mp,const char *file, uint line)
pthread_mutex_lock(&mp->global);
if (mp->count == 0)
{
- fprintf(stderr,"safe_mutex: Trying to unlock mutex %s that wasn't locked at %s, line %d\n"
+ fprintf(stderr,
+ "safe_mutex: Trying to unlock mutex %s that wasn't locked at "
+ "%s, line %d\n"
"Last used at %s, line: %d\n",
mp->name ? mp->name : "Null", file, line,
mp->file ? mp->file : "Null", mp->line);
@@ -191,7 +352,9 @@ int safe_mutex_unlock(safe_mutex_t *mp,const char *file, uint line)
}
if (!pthread_equal(pthread_self(),mp->thread))
{
- fprintf(stderr,"safe_mutex: Trying to unlock mutex %s at %s, line %d that was locked by "
+ fprintf(stderr,
+ "safe_mutex: Trying to unlock mutex %s at %s, line %d that was "
+ "locked by "
"another thread at: %s, line: %d\n",
mp->name, file, line, mp->file, mp->line);
fflush(stderr);
@@ -199,6 +362,9 @@ int safe_mutex_unlock(safe_mutex_t *mp,const char *file, uint line)
}
mp->thread= 0;
mp->count--;
+
+ remove_from_active_list(mp);
+
#ifdef __WIN__
pthread_mutex_unlock(&mp->mutex);
error=0;
@@ -206,8 +372,9 @@ int safe_mutex_unlock(safe_mutex_t *mp,const char *file, uint line)
error=pthread_mutex_unlock(&mp->mutex);
if (error)
{
- fprintf(stderr,"safe_mutex: Got error: %d (%d) when trying to unlock mutex %s at %s, "
- "line %d\n", error, errno, mp->name, file, line);
+ fprintf(stderr,
+ "safe_mutex: Got error: %d (%d) when trying to unlock mutex "
+ "%s at %s, line %d\n", error, errno, mp->name, file, line);
fflush(stderr);
abort();
}
@@ -221,18 +388,23 @@ int safe_cond_wait(pthread_cond_t *cond, safe_mutex_t *mp, const char *file,
uint line)
{
int error;
+ safe_mutex_t save_state;
+
pthread_mutex_lock(&mp->global);
if (mp->count == 0)
{
- fprintf(stderr,"safe_mutex: Trying to cond_wait on a unlocked mutex %s at %s, line %d\n",
+ fprintf(stderr,
+ "safe_mutex: Trying to cond_wait on a unlocked mutex %s at %s, "
+ "line %d\n",
mp->name ? mp->name : "Null", file, line);
fflush(stderr);
abort();
}
if (!pthread_equal(pthread_self(),mp->thread))
{
- fprintf(stderr,"safe_mutex: Trying to cond_wait on a mutex %s at %s, line %d that was "
- "locked by another thread at: %s, line: %d\n",
+ fprintf(stderr,
+ "safe_mutex: Trying to cond_wait on a mutex %s at %s, line %d "
+ "that was locked by another thread at: %s, line: %d\n",
mp->name, file, line, mp->file, mp->line);
fflush(stderr);
abort();
@@ -240,26 +412,37 @@ int safe_cond_wait(pthread_cond_t *cond, safe_mutex_t *mp, const char *file,
if (mp->count-- != 1)
{
- fprintf(stderr,"safe_mutex: Count was %d on locked mutex %s at %s, line %d\n",
+ fprintf(stderr,
+ "safe_mutex: Count was %d on locked mutex %s at %s, line %d\n",
mp->count+1, mp->name, file, line);
fflush(stderr);
abort();
}
+ save_state= *mp;
+ remove_from_active_list(mp);
pthread_mutex_unlock(&mp->global);
error=pthread_cond_wait(cond,&mp->mutex);
pthread_mutex_lock(&mp->global);
+
if (error)
{
- fprintf(stderr,"safe_mutex: Got error: %d (%d) when doing a safe_mutex_wait on %s at %s, "
- "line %d\n", error, errno, mp->name, file, line);
+ fprintf(stderr,
+ "safe_mutex: Got error: %d (%d) when doing a safe_mutex_wait on "
+ "%s at %s, line %d\n", error, errno, mp->name, file, line);
fflush(stderr);
abort();
}
- mp->thread=pthread_self();
+ /* Restore state as it was before */
+ mp->thread= save_state.thread;
+ mp->active_flags= save_state.active_flags;
+ mp->next= save_state.next;
+ mp->prev= save_state.prev;
+
if (mp->count++)
{
fprintf(stderr,
- "safe_mutex: Count was %d in thread 0x%lx when locking mutex %s at %s, line %d\n",
+ "safe_mutex: Count was %d in thread 0x%lx when locking mutex %s "
+ "at %s, line %d\n",
mp->count-1, my_thread_dbug_id(), mp->name, file, line);
fflush(stderr);
abort();
@@ -276,33 +459,44 @@ int safe_cond_timedwait(pthread_cond_t *cond, safe_mutex_t *mp,
const char *file, uint line)
{
int error;
+ safe_mutex_t save_state;
+
pthread_mutex_lock(&mp->global);
if (mp->count != 1 || !pthread_equal(pthread_self(),mp->thread))
{
- fprintf(stderr,"safe_mutex: Trying to cond_wait at %s, line %d on a not hold mutex %s\n",
+ fprintf(stderr,
+ "safe_mutex: Trying to cond_wait at %s, line %d on a not hold "
+ "mutex %s\n",
file, line, mp->name ? mp->name : "Null");
fflush(stderr);
abort();
}
mp->count--; /* Mutex will be released */
+ save_state= *mp;
+ remove_from_active_list(mp);
pthread_mutex_unlock(&mp->global);
error=pthread_cond_timedwait(cond,&mp->mutex,abstime);
#ifdef EXTRA_DEBUG
if (error && (error != EINTR && error != ETIMEDOUT && error != ETIME))
{
fprintf(stderr,
- "safe_mutex: Got error: %d (%d) when doing a safe_mutex_timedwait on %s at %s, "
- "line %d\n",
+ "safe_mutex: Got error: %d (%d) when doing a safe_mutex_timedwait "
+ "on %s at %s, line %d\n",
error, errno, mp->name, file, line);
}
#endif
pthread_mutex_lock(&mp->global);
- mp->thread=pthread_self();
+ /* Restore state as it was before */
+ mp->thread= save_state.thread;
+ mp->active_flags= save_state.active_flags;
+ mp->next= save_state.next;
+ mp->prev= save_state.prev;
+
if (mp->count++)
{
fprintf(stderr,
- "safe_mutex: Count was %d in thread 0x%lx when locking mutex %s at %s, line %d "
- "(error: %d (%d))\n",
+ "safe_mutex: Count was %d in thread 0x%lx when locking mutex "
+ "%s at %s, line %d (error: %d (%d))\n",
mp->count-1, my_thread_dbug_id(), mp->name, file, line,
error, error);
fflush(stderr);
@@ -318,6 +512,8 @@ int safe_cond_timedwait(pthread_cond_t *cond, safe_mutex_t *mp,
int safe_mutex_destroy(safe_mutex_t *mp, const char *file, uint line)
{
int error=0;
+ DBUG_ENTER("safe_mutex_destroy");
+ DBUG_PRINT("enter", ("mutex: 0x%lx name: %s", (ulong) mp, mp->name));
if (!mp->file)
{
fprintf(stderr,
@@ -328,12 +524,17 @@ int safe_mutex_destroy(safe_mutex_t *mp, const char *file, uint line)
}
if (mp->count != 0)
{
- fprintf(stderr,"safe_mutex: Trying to destroy a mutex %s that was locked at %s, "
+ fprintf(stderr,
+ "safe_mutex: Trying to destroy a mutex %s that was locked at %s, "
"line %d at %s, line %d\n",
mp->name, mp->file, mp->line, file, line);
fflush(stderr);
abort();
}
+
+ /* Free all entries that points to this one */
+ safe_mutex_free_deadlock_data(mp);
+
#ifdef __WIN__
pthread_mutex_destroy(&mp->global);
pthread_mutex_destroy(&mp->mutex);
@@ -354,7 +555,7 @@ int safe_mutex_destroy(safe_mutex_t *mp, const char *file, uint line)
if (info->prev)
info->prev->next = info->next;
else
- safe_mutex_root = info->next;
+ safe_mutex_create_root = info->next;
if (info->next)
info->next->prev = info->prev;
safe_mutex_count--;
@@ -366,10 +567,36 @@ int safe_mutex_destroy(safe_mutex_t *mp, const char *file, uint line)
#else
thread_safe_sub(safe_mutex_count, 1, &THR_LOCK_mutex);
#endif /* SAFE_MUTEX_DETECT_DESTROY */
- return error;
+ DBUG_RETURN(error);
}
+/**
+ Free all data related to deadlock detection
+
+ This is also useful together with safemalloc when you don't want to
+ have reports of not freed memory for mysys mutexes.
+*/
+
+void safe_mutex_free_deadlock_data(safe_mutex_t *mp)
+{
+ /* Free all entries that points to this one */
+ if (!(mp->create_flags & MYF_NO_DEADLOCK_DETECTION))
+ {
+ pthread_mutex_lock(&THR_LOCK_mutex);
+ hash_iterate(mp->used_mutex, (hash_walk_action) remove_from_locked_mutex,
+ mp);
+ hash_iterate(mp->locked_mutex, (hash_walk_action) remove_from_used_mutex,
+ mp);
+ pthread_mutex_unlock(&THR_LOCK_mutex);
+
+ hash_free(mp->used_mutex);
+ hash_free(mp->locked_mutex);
+ my_free(mp->locked_mutex, 0);
+ mp->create_flags|= MYF_NO_DEADLOCK_DETECTION;
+ }
+}
+
/*
Free global resources and check that all mutex has been destroyed
@@ -400,7 +627,7 @@ void safe_mutex_end(FILE *file __attribute__((unused)))
}
{
struct st_safe_mutex_info_t *ptr;
- for (ptr= safe_mutex_root ; ptr ; ptr= ptr->next)
+ for (ptr= safe_mutex_create_root ; ptr ; ptr= ptr->next)
{
fprintf(file, "\tMutex %s initiated at line %4u in '%s'\n",
ptr->name, ptr->init_line, ptr->init_file);
@@ -410,6 +637,127 @@ void safe_mutex_end(FILE *file __attribute__((unused)))
#endif /* SAFE_MUTEX_DETECT_DESTROY */
}
+
+static my_bool add_used_to_locked_mutex(safe_mutex_t *used_mutex,
+ safe_mutex_deadlock_t *locked_mutex)
+{
+ /* Add mutex to all parent of the current mutex */
+ if (!locked_mutex->warning_only)
+ {
+ (void) hash_iterate(locked_mutex->mutex->locked_mutex,
+ (hash_walk_action) add_to_locked_mutex,
+ used_mutex);
+ /* mark that locked_mutex is locked after used_mutex */
+ (void) add_to_locked_mutex(locked_mutex, used_mutex);
+ }
+ return 0;
+}
+
+
+/**
+ register that locked_mutex was locked after current_mutex
+*/
+
+static my_bool add_to_locked_mutex(safe_mutex_deadlock_t *locked_mutex,
+ safe_mutex_t *current_mutex)
+{
+ DBUG_ENTER("add_to_locked_mutex");
+ DBUG_PRINT("info", ("inserting 0x%lx into 0x%lx (id: %lu -> %lu)",
+ (ulong) locked_mutex, (long) current_mutex,
+ locked_mutex->id, current_mutex->id));
+ if (my_hash_insert(current_mutex->locked_mutex, (uchar*) locked_mutex))
+ {
+ /* Got mutex through two paths; ignore */
+ DBUG_RETURN(0);
+ }
+ locked_mutex->count++;
+ if (my_hash_insert(locked_mutex->mutex->used_mutex,
+ (uchar*) current_mutex))
+ {
+ DBUG_ASSERT(0);
+ }
+ DBUG_RETURN(0);
+}
+
+
+/**
+ Remove mutex from the locked mutex hash
+ @fn remove_from_used_mutex()
+ @param mp Mutex that has delete_mutex in it's locked_mutex hash
+ @param delete_mutex Mutex should be removed from the hash
+
+ @notes
+ safe_mutex_deadlock_t entries in the locked hash are shared.
+ When counter goes to 0, we delete the safe_mutex_deadlock_t entry.
+*/
+
+static my_bool remove_from_locked_mutex(safe_mutex_t *mp,
+ safe_mutex_t *delete_mutex)
+{
+ safe_mutex_deadlock_t *found;
+ DBUG_ENTER("remove_from_locked_mutex");
+ DBUG_PRINT("enter", ("delete_mutex: 0x%lx mutex: 0x%lx (id: %lu <- %lu)",
+ (ulong) delete_mutex, (ulong) mp,
+ delete_mutex->id, mp->id));
+
+ found= (safe_mutex_deadlock_t *) hash_search(mp->locked_mutex,
+ (uchar*) &delete_mutex->id, 0);
+ DBUG_ASSERT(found);
+ if (found)
+ {
+ if (hash_delete(mp->locked_mutex, (uchar*) found))
+ {
+ DBUG_ASSERT(0);
+ }
+ if (!--found->count)
+ my_free(found, MYF(0));
+ }
+ DBUG_RETURN(0);
+}
+
+static my_bool remove_from_used_mutex(safe_mutex_deadlock_t *locked_mutex,
+ safe_mutex_t *mutex)
+{
+ DBUG_ENTER("remove_from_used_mutex");
+ DBUG_PRINT("enter", ("delete_mutex: 0x%lx mutex: 0x%lx (id: %lu <- %lu)",
+ (ulong) mutex, (ulong) locked_mutex,
+ mutex->id, locked_mutex->id));
+ if (hash_delete(locked_mutex->mutex->used_mutex, (uchar*) mutex))
+ {
+ DBUG_ASSERT(0);
+ }
+ if (!--locked_mutex->count)
+ my_free(locked_mutex, MYF(0));
+ DBUG_RETURN(0);
+}
+
+
+static void print_deadlock_warning(safe_mutex_t *new_mutex,
+ safe_mutex_t *parent_mutex)
+{
+ safe_mutex_t *mutex_root;
+ DBUG_ENTER("print_deadlock_warning");
+ DBUG_PRINT("enter", ("mutex: %s parent: %s",
+ new_mutex->name, parent_mutex->name));
+
+ fprintf(stderr, "safe_mutex: Found wrong usage of mutex "
+ "'%s' and '%s'\n",
+ parent_mutex->name, new_mutex->name);
+ fprintf(stderr, "Mutex currently locked (in reverse order):\n");
+ fprintf(stderr, "%-32.32s %s line %u\n", new_mutex->name, new_mutex->file,
+ new_mutex->line);
+ for (mutex_root= *my_thread_var_mutex_in_use() ;
+ mutex_root;
+ mutex_root= mutex_root->next)
+ {
+ fprintf(stderr, "%-32.32s %s line %u\n", mutex_root->name,
+ mutex_root->file, mutex_root->line);
+ }
+ fflush(stderr);
+ DBUG_VOID_RETURN;
+}
+
+
#endif /* THREAD && SAFE_MUTEX */
#if defined(THREAD) && defined(MY_PTHREAD_FASTMUTEX) && !defined(SAFE_MUTEX)
diff --git a/sql/event_queue.cc b/sql/event_queue.cc
index 04d4f858b43..d68dc8ef479 100644
--- a/sql/event_queue.cc
+++ b/sql/event_queue.cc
@@ -94,7 +94,12 @@ Event_queue::Event_queue()
mutex_queue_data_attempting_lock(FALSE),
waiting_on_cond(FALSE)
{
- pthread_mutex_init(&LOCK_event_queue, MY_MUTEX_INIT_FAST);
+ /*
+ Inconsisent usage between LOCK_event_queue and LOCK_scheduler_state and
+ LOCK_open
+ */
+ my_pthread_mutex_init(&LOCK_event_queue, MY_MUTEX_INIT_FAST,
+ "LOCK_event_queue", MYF_NO_DEADLOCK_DETECTION);
pthread_cond_init(&COND_queue_state, NULL);
}
diff --git a/sql/event_scheduler.cc b/sql/event_scheduler.cc
index 5655a8acc99..d3e51d40822 100644
--- a/sql/event_scheduler.cc
+++ b/sql/event_scheduler.cc
@@ -350,6 +350,14 @@ Event_scheduler::Event_scheduler(Event_queue *queue_arg)
{
pthread_mutex_init(&LOCK_scheduler_state, MY_MUTEX_INIT_FAST);
pthread_cond_init(&COND_state, NULL);
+
+#ifdef SAFE_MUTEX
+ /* Ensure right mutex order */
+ pthread_mutex_lock(&LOCK_scheduler_state);
+ pthread_mutex_lock(&LOCK_global_system_variables);
+ pthread_mutex_unlock(&LOCK_global_system_variables);
+ pthread_mutex_unlock(&LOCK_scheduler_state);
+#endif
}
diff --git a/sql/events.cc b/sql/events.cc
index df49b7db773..73c76688eb7 100644
--- a/sql/events.cc
+++ b/sql/events.cc
@@ -995,7 +995,12 @@ Events::deinit()
void
Events::init_mutexes()
{
- pthread_mutex_init(&LOCK_event_metadata, MY_MUTEX_INIT_FAST);
+ /*
+ Inconsisent usage between LOCK_event_metadata and LOCK_scheduler_state
+ and LOCK_open
+ */
+ my_pthread_mutex_init(&LOCK_event_metadata, MY_MUTEX_INIT_FAST,
+ "LOCK_event_metadata", MYF_NO_DEADLOCK_DETECTION);
}
diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc
index 4fd5ee1b402..1788fed26b1 100644
--- a/sql/ha_ndbcluster_binlog.cc
+++ b/sql/ha_ndbcluster_binlog.cc
@@ -1550,7 +1550,9 @@ end:
dict->forceGCPWait();
int max_timeout= opt_ndb_sync_timeout;
- (void) pthread_mutex_lock(&ndb_schema_object->mutex);
+ /* Inconsistent usage of ndb_schema_object->mutex and LOCK_open */
+ (void) my_pthread_mutex_lock(&ndb_schema_object->mutex,
+ MYF_NO_DEADLOCK_DETECTION);
if (have_lock_open)
{
safe_mutex_assert_owner(&LOCK_open);
diff --git a/sql/log.cc b/sql/log.cc
index b6fe1196835..4c5acf83bc4 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -2403,7 +2403,12 @@ void MYSQL_BIN_LOG::init_pthread_objects()
DBUG_ASSERT(inited == 0);
inited= 1;
(void) pthread_mutex_init(&LOCK_log, MY_MUTEX_INIT_SLOW);
- (void) pthread_mutex_init(&LOCK_index, MY_MUTEX_INIT_SLOW);
+ /*
+ LOCK_index and LOCK_log are taken in wrong order
+ Can be seen with 'mysql-test-run ndb.ndb_binlog_basic'
+ */
+ (void) my_pthread_mutex_init(&LOCK_index, MY_MUTEX_INIT_SLOW, "LOCK_index",
+ MYF_NO_DEADLOCK_DETECTION);
(void) pthread_cond_init(&update_cond, 0);
}
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index b1ec3eb1e3a..8415342b3df 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -757,6 +757,7 @@ static ulong find_bit_type_or_exit(const char *x, TYPELIB *bit_lib,
const char *option);
static void clean_up(bool print_message);
static int test_if_case_insensitive(const char *dir_name);
+static void register_mutex_order();
#ifndef EMBEDDED_LIBRARY
static void usage(void);
@@ -902,9 +903,19 @@ static void close_connections(void)
pthread_mutex_lock(&tmp->mysys_var->mutex);
if (tmp->mysys_var->current_cond)
{
- pthread_mutex_lock(tmp->mysys_var->current_mutex);
- pthread_cond_broadcast(tmp->mysys_var->current_cond);
- pthread_mutex_unlock(tmp->mysys_var->current_mutex);
+ uint i;
+ for (i=0; i < 2; i++)
+ {
+ int ret= pthread_mutex_trylock(tmp->mysys_var->current_mutex);
+ pthread_cond_broadcast(tmp->mysys_var->current_cond);
+ if (!ret)
+ {
+ /* Thread has surely got the signal, unlock and abort */
+ pthread_mutex_unlock(tmp->mysys_var->current_mutex);
+ break;
+ }
+ sleep(1);
+ }
}
pthread_mutex_unlock(&tmp->mysys_var->mutex);
}
@@ -1244,6 +1255,7 @@ void clean_up(bool print_message)
wt_end();
delete_elements(&key_caches, (void (*)(const char*, uchar*)) free_key_cache);
multi_keycache_free();
+ sp_cache_end();
free_status_vars();
end_thr_alarm(1); /* Free allocated memory */
my_free_open_file_info();
@@ -1336,6 +1348,7 @@ static void wait_for_signal_thread_to_end()
static void clean_up_mutexes()
{
+ DBUG_ENTER("clean_up_mutexes");
(void) pthread_mutex_destroy(&LOCK_mysql_create_db);
(void) pthread_mutex_destroy(&LOCK_lock_db);
(void) pthread_mutex_destroy(&LOCK_Acl);
@@ -1367,6 +1380,8 @@ static void clean_up_mutexes()
(void) pthread_mutex_destroy(&LOCK_rpl_status);
(void) pthread_cond_destroy(&COND_rpl_status);
#endif
+ (void) pthread_mutex_destroy(&LOCK_server_started);
+ (void) pthread_cond_destroy(&COND_server_started);
(void) pthread_mutex_destroy(&LOCK_active_mi);
(void) rwlock_destroy(&LOCK_sys_init_connect);
(void) rwlock_destroy(&LOCK_sys_init_slave);
@@ -1381,11 +1396,35 @@ static void clean_up_mutexes()
(void) pthread_cond_destroy(&COND_thread_cache);
(void) pthread_cond_destroy(&COND_flush_thread_cache);
(void) pthread_cond_destroy(&COND_manager);
+ DBUG_VOID_RETURN;
}
#endif /*EMBEDDED_LIBRARY*/
+/**
+ Register order of mutex for wrong mutex deadlock detector
+
+ By aquiring all mutex in order here, the mutex order detector in
+ mysys/thr_mutex.c, will give a warning on first wrong mutex usage!
+*/
+
+static void register_mutex_order()
+{
+#ifdef SAFE_MUTEX
+ /*
+ We must have LOCK_open before LOCK_global_system_variables because
+ LOCK_open is hold while sql_plugin.c::intern_sys_var_ptr() is called.
+ */
+ pthread_mutex_lock(&LOCK_open);
+ pthread_mutex_lock(&LOCK_global_system_variables);
+
+ pthread_mutex_unlock(&LOCK_global_system_variables);
+ pthread_mutex_unlock(&LOCK_open);
+#endif
+}
+
+
/****************************************************************************
** Init IP and UNIX socket
****************************************************************************/
@@ -3549,6 +3588,7 @@ static int init_thread_environment()
sql_print_error("Can't create thread-keys");
return 1;
}
+ register_mutex_order();
return 0;
}
@@ -5464,7 +5504,7 @@ enum options_mysqld
OPT_NDB_REPORT_THRESH_BINLOG_EPOCH_SLIP,
OPT_NDB_REPORT_THRESH_BINLOG_MEM_USAGE,
OPT_NDB_USE_COPYING_ALTER_TABLE,
- OPT_SKIP_SAFEMALLOC,
+ OPT_SKIP_SAFEMALLOC, OPT_MUTEX_DEADLOCK_DETECTOR,
OPT_TEMP_POOL, OPT_TX_ISOLATION, OPT_COMPLETION_TYPE,
OPT_SKIP_STACK_TRACE, OPT_SKIP_SYMLINKS,
OPT_MAX_BINLOG_DUMP_EVENTS, OPT_SPORADIC_BINLOG_DUMP_FAIL,
@@ -6002,6 +6042,13 @@ master-ssl",
#endif /* HAVE_REPLICATION */
{"memlock", OPT_MEMLOCK, "Lock mysqld in memory.", (uchar**) &locked_in_memory,
(uchar**) &locked_in_memory, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+#ifdef SAFE_MUTEX
+ {"mutex-deadlock-detector", OPT_MUTEX_DEADLOCK_DETECTOR,
+ "Enable checking of wrong mutex usage.",
+ (uchar**) &safe_mutex_deadlock_detector,
+ (uchar**) &safe_mutex_deadlock_detector,
+ 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
+#endif
{"myisam-recover", OPT_MYISAM_RECOVER,
"Syntax: myisam-recover[=option[,option...]], where option can be DEFAULT, BACKUP, FORCE or QUICK.",
(uchar**) &myisam_recover_options_str, (uchar**) &myisam_recover_options_str, 0,
diff --git a/sql/protocol.cc b/sql/protocol.cc
index 3eccc6632ce..bb69cdb36af 100644
--- a/sql/protocol.cc
+++ b/sql/protocol.cc
@@ -790,8 +790,8 @@ bool Protocol_text::store(const char *from, size_t length,
{
CHARSET_INFO *tocs= this->thd->variables.character_set_results;
#ifndef DBUG_OFF
- DBUG_PRINT("info", ("Protocol_text::store field %u (%u): %s", field_pos,
- field_count, from));
+ DBUG_PRINT("info", ("Protocol_text::store field %u (%u): %*s", field_pos,
+ field_count, (int) length, from));
DBUG_ASSERT(field_pos < field_count);
DBUG_ASSERT(field_types == 0 ||
field_types[field_pos] == MYSQL_TYPE_DECIMAL ||
diff --git a/sql/rpl_mi.cc b/sql/rpl_mi.cc
index 5e46837e948..cb8b0e02ef9 100644
--- a/sql/rpl_mi.cc
+++ b/sql/rpl_mi.cc
@@ -38,11 +38,26 @@ Master_info::Master_info()
ssl_cipher[0]= 0; ssl_key[0]= 0;
bzero((char*) &file, sizeof(file));
- pthread_mutex_init(&run_lock, MY_MUTEX_INIT_FAST);
- pthread_mutex_init(&data_lock, MY_MUTEX_INIT_FAST);
+ /*
+ We have to use MYF_NO_DEADLOCK_DETECTION because mysqld doesn't
+ lock run_lock and data_lock consistently.
+ Should be fixed as this can easily lead to deadlocks
+ */
+ my_pthread_mutex_init(&run_lock, MY_MUTEX_INIT_FAST,
+ "Master_info::run_lock", MYF_NO_DEADLOCK_DETECTION);
+ my_pthread_mutex_init(&data_lock, MY_MUTEX_INIT_FAST,
+ "Master_info::data_lock", MYF_NO_DEADLOCK_DETECTION);
pthread_cond_init(&data_cond, NULL);
pthread_cond_init(&start_cond, NULL);
pthread_cond_init(&stop_cond, NULL);
+
+#ifdef SAFE_MUTEX
+ /* Define mutex order for locks to find wrong lock usage */
+ pthread_mutex_lock(&data_lock);
+ pthread_mutex_lock(&run_lock);
+ pthread_mutex_unlock(&run_lock);
+ pthread_mutex_unlock(&data_lock);
+#endif
}
Master_info::~Master_info()
diff --git a/sql/set_var.cc b/sql/set_var.cc
index df1badebc50..074835107d9 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -2403,7 +2403,6 @@ end:
bool sys_var_log_state::update(THD *thd, set_var *var)
{
bool res;
- pthread_mutex_lock(&LOCK_global_system_variables);
if (!var->save_result.ulong_value)
{
logger.deactivate_log_handler(thd, log_type);
@@ -2411,15 +2410,12 @@ bool sys_var_log_state::update(THD *thd, set_var *var)
}
else
res= logger.activate_log_handler(thd, log_type);
- pthread_mutex_unlock(&LOCK_global_system_variables);
return res;
}
void sys_var_log_state::set_default(THD *thd, enum_var_type type)
{
- pthread_mutex_lock(&LOCK_global_system_variables);
logger.deactivate_log_handler(thd, log_type);
- pthread_mutex_unlock(&LOCK_global_system_variables);
}
@@ -2515,23 +2511,18 @@ bool update_sys_var_str_path(THD *thd, sys_var_str *var_str,
goto err;
}
- pthread_mutex_lock(&LOCK_global_system_variables);
logger.lock_exclusive();
if (file_log && log_state)
file_log->close(0);
- old_value= var_str->value;
- var_str->value= res;
- var_str->value_length= str_length;
- my_free(old_value, MYF(MY_ALLOW_ZERO_PTR));
if (file_log && log_state)
{
switch (log_type) {
case QUERY_LOG_SLOW:
- file_log->open_slow_log(sys_var_slow_log_path.value);
+ file_log->open_slow_log(res);
break;
case QUERY_LOG_GENERAL:
- file_log->open_query_log(sys_var_general_log_path.value);
+ file_log->open_query_log(res);
break;
default:
DBUG_ASSERT(0);
@@ -2539,6 +2530,13 @@ bool update_sys_var_str_path(THD *thd, sys_var_str *var_str,
}
logger.unlock();
+
+ /* update global variable */
+ pthread_mutex_lock(&LOCK_global_system_variables);
+ old_value= var_str->value;
+ var_str->value= res;
+ var_str->value_length= str_length;
+ my_free(old_value, MYF(MY_ALLOW_ZERO_PTR));
pthread_mutex_unlock(&LOCK_global_system_variables);
err:
@@ -2578,26 +2576,22 @@ static void sys_default_slow_log_path(THD *thd, enum_var_type type)
bool sys_var_log_output::update(THD *thd, set_var *var)
{
- pthread_mutex_lock(&LOCK_global_system_variables);
logger.lock_exclusive();
logger.init_slow_log(var->save_result.ulong_value);
logger.init_general_log(var->save_result.ulong_value);
*value= var->save_result.ulong_value;
logger.unlock();
- pthread_mutex_unlock(&LOCK_global_system_variables);
return 0;
}
void sys_var_log_output::set_default(THD *thd, enum_var_type type)
{
- pthread_mutex_lock(&LOCK_global_system_variables);
logger.lock_exclusive();
logger.init_slow_log(LOG_FILE);
logger.init_general_log(LOG_FILE);
*value= LOG_FILE;
logger.unlock();
- pthread_mutex_unlock(&LOCK_global_system_variables);
}
diff --git a/sql/sp_cache.cc b/sql/sp_cache.cc
index 64898915b7e..13b3e771a91 100644
--- a/sql/sp_cache.cc
+++ b/sql/sp_cache.cc
@@ -106,6 +106,12 @@ void sp_cache_clear(sp_cache **cp)
}
+void sp_cache_end()
+{
+ pthread_mutex_destroy(&Cversion_lock);
+}
+
+
/*
Insert a routine into the cache.
diff --git a/sql/sp_cache.h b/sql/sp_cache.h
index f4d44a1f29f..efb61d76719 100644
--- a/sql/sp_cache.h
+++ b/sql/sp_cache.h
@@ -53,6 +53,7 @@ class sp_cache;
*/
void sp_cache_init();
+void sp_cache_end();
void sp_cache_clear(sp_cache **cp);
void sp_cache_insert(sp_cache **cp, sp_head *sp);
sp_head *sp_cache_lookup(sp_cache **cp, sp_name *name);
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 9a450f245be..3504b4fdea5 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -978,6 +978,14 @@ void add_diff_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var,
*(to++)+= *(from++) - *(dec++);
}
+#define SECONDS_TO_WAIT_FOR_KILL 2
+#if !defined(__WIN__) && defined(HAVE_SELECT)
+/* my_sleep() can wait for sub second times */
+#define WAIT_FOR_KILL_TRY_TIMES 20
+#else
+#define WAIT_FOR_KILL_TRY_TIMES 2
+#endif
+
void THD::awake(THD::killed_state state_to_set)
{
@@ -1032,12 +1040,35 @@ void THD::awake(THD::killed_state state_to_set)
we issue a second KILL or the status it's waiting for happens).
It's true that we have set its thd->killed but it may not
see it immediately and so may have time to reach the cond_wait().
+
+ We have to do the loop with trylock, because if we would use
+ pthread_mutex_lock(), we can cause a deadlock as we are here locking
+ the mysys_var->mutex and mysys_var->current_mutex in a different order
+ than in the thread we are trying to kill.
+ We only sleep for 2 seconds as we don't want to have LOCK_delete
+ locked too long time.
+
+ There is a small change we may not succeed in aborting a thread that
+ is not yet waiting for a mutex, but as this happens only for a
+ thread that was doing something else when the kill was issued and
+ which should detect the kill flag before it starts to wait, this
+ should be good enough.
*/
if (mysys_var->current_cond && mysys_var->current_mutex)
{
- pthread_mutex_lock(mysys_var->current_mutex);
- pthread_cond_broadcast(mysys_var->current_cond);
- pthread_mutex_unlock(mysys_var->current_mutex);
+ uint i;
+ for (i= 0; i < WAIT_FOR_KILL_TRY_TIMES * SECONDS_TO_WAIT_FOR_KILL; i++)
+ {
+ int ret= pthread_mutex_trylock(mysys_var->current_mutex);
+ pthread_cond_broadcast(mysys_var->current_cond);
+ if (!ret)
+ {
+ /* Signal is sure to get through */
+ pthread_mutex_unlock(mysys_var->current_mutex);
+ break;
+ }
+ }
+ my_sleep(1000000L / WAIT_FOR_KILL_TRY_TIMES);
}
pthread_mutex_unlock(&mysys_var->mutex);
}
@@ -1073,6 +1104,15 @@ bool THD::store_globals()
created in another thread
*/
thr_lock_info_init(&lock_info);
+
+#ifdef SAFE_MUTEX
+ /* Register order of mutex for wrong mutex deadlock detector */
+ pthread_mutex_lock(&LOCK_delete);
+ pthread_mutex_lock(&mysys_var->mutex);
+
+ pthread_mutex_unlock(&mysys_var->mutex);
+ pthread_mutex_unlock(&LOCK_delete);
+#endif
return 0;
}
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index a275c680cb5..d8838b3b03d 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -1719,7 +1719,8 @@ public:
thd.system_thread= SYSTEM_THREAD_DELAYED_INSERT;
thd.security_ctx->host_or_ip= "";
bzero((char*) &info,sizeof(info));
- pthread_mutex_init(&mutex,MY_MUTEX_INIT_FAST);
+ my_pthread_mutex_init(&mutex, MY_MUTEX_INIT_FAST, "Delayed_insert::mutex",
+ 0);
pthread_cond_init(&cond,NULL);
pthread_cond_init(&cond_client,NULL);
VOID(pthread_mutex_lock(&LOCK_thread_count));
@@ -2224,7 +2225,8 @@ void kill_delayed_threads(void)
in handle_delayed_insert()
*/
if (&di->mutex != di->thd.mysys_var->current_mutex)
- pthread_mutex_lock(di->thd.mysys_var->current_mutex);
+ my_pthread_mutex_lock(di->thd.mysys_var->current_mutex,
+ MYF_NO_DEADLOCK_DETECTION);
pthread_cond_broadcast(di->thd.mysys_var->current_cond);
if (&di->mutex != di->thd.mysys_var->current_mutex)
pthread_mutex_unlock(di->thd.mysys_var->current_mutex);
@@ -2470,13 +2472,14 @@ end:
clients
*/
- close_thread_tables(thd); // Free the table
di->table=0;
di->dead= 1; // If error
thd->killed= THD::KILL_CONNECTION; // If error
- pthread_cond_broadcast(&di->cond_client); // Safety
pthread_mutex_unlock(&di->mutex);
+ close_thread_tables(thd); // Free the table
+ pthread_cond_broadcast(&di->cond_client); // Safety
+
pthread_mutex_lock(&LOCK_delayed_create); // Because of delayed_get_table
pthread_mutex_lock(&LOCK_delayed_insert);
delete di;
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index b5ab6484d12..6e321af9292 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -2102,7 +2102,6 @@ static bool show_status_array(THD *thd, const char *wild,
const char *pos, *end; // We assign a lot of const's
pthread_mutex_lock(&LOCK_global_system_variables);
-
if (show_type == SHOW_SYS)
{
show_type= ((sys_var*) value)->show_type();
@@ -2183,14 +2182,14 @@ static bool show_status_array(THD *thd, const char *wild,
DBUG_ASSERT(0);
break;
}
+ pthread_mutex_unlock(&LOCK_global_system_variables);
+
restore_record(table, s->default_values);
table->field[0]->store(name_buffer, strlen(name_buffer),
system_charset_info);
table->field[1]->store(pos, (uint32) (end - pos), system_charset_info);
table->field[1]->set_notnull();
- pthread_mutex_unlock(&LOCK_global_system_variables);
-
if (schema_table_store_record(thd, table))
DBUG_RETURN(TRUE);
}
diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc
index 887acacbd1f..9c2b71094b6 100644
--- a/storage/innobase/handler/ha_innodb.cc
+++ b/storage/innobase/handler/ha_innodb.cc
@@ -1659,8 +1659,15 @@ innobase_init(
srv_sizeof_trx_t_in_ha_innodb_cc = sizeof(trx_t);
+#ifdef SAFE_MUTEX
+ /* Disable deadlock detection as it's very slow for the buffer pool */
+ my_bool old_safe_mutex_deadlock_detector;
+ safe_mutex_deadlock_detector= 0;
+#endif
err = innobase_start_or_create_for_mysql();
-
+#ifdef SAFE_MUTEX
+ safe_mutex_deadlock_detector= old_safe_mutex_deadlock_detector;
+#endif
if (err != DB_SUCCESS) {
my_free(internal_innobase_data_file_path,
MYF(MY_ALLOW_ZERO_PTR));
diff --git a/storage/maria/ha_maria.cc b/storage/maria/ha_maria.cc
index fa2edb24791..3a32ef8cce2 100644
--- a/storage/maria/ha_maria.cc
+++ b/storage/maria/ha_maria.cc
@@ -1460,7 +1460,7 @@ int ha_maria::repair(THD *thd, HA_CHECK *param, bool do_optimize)
(local_testflag &
T_STATISTICS ? UPDATE_STAT : 0));
info(HA_STATUS_NO_LOCK | HA_STATUS_TIME | HA_STATUS_VARIABLE |
- HA_STATUS_CONST);
+ HA_STATUS_CONST, 0);
if (rows != file->state->records && !(param->testflag & T_VERY_SILENT))
{
char llbuff[22], llbuff2[22];
@@ -2148,6 +2148,11 @@ void ha_maria::position(const uchar *record)
int ha_maria::info(uint flag)
{
+ return info(flag, table->s->tmp_table == NO_TMP_TABLE);
+}
+
+int ha_maria::info(uint flag, my_bool lock_table_share)
+{
MARIA_INFO maria_info;
char name_buff[FN_REFLEN];
@@ -2173,7 +2178,7 @@ int ha_maria::info(uint flag)
stats.block_size= maria_block_size;
/* Update share */
- if (share->tmp_table == NO_TMP_TABLE)
+ if (lock_table_share)
pthread_mutex_lock(&share->mutex);
share->keys_in_use.set_prefix(share->keys);
share->keys_in_use.intersect_extended(maria_info.key_map);
@@ -2186,7 +2191,7 @@ int ha_maria::info(uint flag)
for (end= to+ share->key_parts ; to < end ; to++, from++)
*to= (ulong) (*from + 0.5);
}
- if (share->tmp_table == NO_TMP_TABLE)
+ if (lock_table_share)
pthread_mutex_unlock(&share->mutex);
/*
diff --git a/storage/maria/ha_maria.h b/storage/maria/ha_maria.h
index ba3ea39b92a..23fb74f1c27 100644
--- a/storage/maria/ha_maria.h
+++ b/storage/maria/ha_maria.h
@@ -112,6 +112,7 @@ public:
int restart_rnd_next(uchar * buf);
void position(const uchar * record);
int info(uint);
+ int info(uint, my_bool);
int extra(enum ha_extra_function operation);
int extra_opt(enum ha_extra_function operation, ulong cache_size);
int reset(void);
diff --git a/storage/maria/ma_close.c b/storage/maria/ma_close.c
index 9463ad8078d..aa4a2f28d9f 100644
--- a/storage/maria/ma_close.c
+++ b/storage/maria/ma_close.c
@@ -114,6 +114,7 @@ int maria_close(register MARIA_HA *info)
}
#ifdef THREAD
thr_lock_delete(&share->lock);
+ (void) pthread_mutex_destroy(&share->key_del_lock);
{
int i,keys;
keys = share->state.header.keys;
@@ -162,14 +163,10 @@ int maria_close(register MARIA_HA *info)
pthread_mutex_unlock(&share->intern_lock);
if (share_can_be_freed)
{
- VOID(pthread_mutex_destroy(&share->intern_lock));
+ (void) pthread_mutex_destroy(&share->intern_lock);
my_free((uchar *)share, MYF(0));
}
- if (info->ftparser_param)
- {
- my_free((uchar*)info->ftparser_param, MYF(0));
- info->ftparser_param= 0;
- }
+ my_free(info->ftparser_param, MYF(MY_ALLOW_ZERO_PTR));
if (info->dfile.file >= 0)
{
/*
diff --git a/storage/maria/ma_key.c b/storage/maria/ma_key.c
index 729d3cbc6de..08702a9109e 100644
--- a/storage/maria/ma_key.c
+++ b/storage/maria/ma_key.c
@@ -67,14 +67,16 @@ static int _ma_put_key_in_record(MARIA_HA *info,uint keynr,uchar *record);
Prefix bytes 244 to 249 are reserved for negative transid, that can be used
when we pack transid relative to each other on a key block.
- We have to store transid in high-byte-first order to be able to do a
- fast byte-per-byte comparision of them without packing them up.
+ We have to store transid in high-byte-first order so that we can compare
+ them unpacked byte per byte and as soon we find a difference we know
+ which is smaller.
For example, assuming we the following data:
key_data: 1 (4 byte integer)
pointer_to_row: 2 << 8 + 3 = 515 (page 2, row 3)
- table_create_transid 1000 Defined at create table time
+ table_create_transid 1000 Defined at create table time and
+ stored in table definition
transid 1010 Transaction that created row
delete_transid 2011 Transaction that deleted row
@@ -86,8 +88,9 @@ static int _ma_put_key_in_record(MARIA_HA *info,uint keynr,uchar *record);
00 00 00 01 Key data (1 stored high byte first)
00 00 00 47 (515 << 1) + 1 ; The last 1 is marker that key cont.
- 15 ((1000-1010) << 1) + 1 ; The last 1 is marker that key cont.
- FB 07 E6 length byte and ((2011 - 1000) << 1) = 07 E6
+ 15 ((1010-1000) << 1) + 1 ; The last 1 is marker that key cont.
+ FB 07 E6 Length byte (FE = 249 + 2 means 2 bytes) and
+ ((2011 - 1000) << 1) = 07 E6
*/
uint transid_store_packed(MARIA_HA *info, uchar *to, ulonglong trid)
diff --git a/storage/maria/ma_loghandler.c b/storage/maria/ma_loghandler.c
index e314363b1d2..cc8c7898d84 100644
--- a/storage/maria/ma_loghandler.c
+++ b/storage/maria/ma_loghandler.c
@@ -1431,7 +1431,9 @@ static my_bool translog_buffer_init(struct st_translog_buffer *buffer)
/* list of waiting buffer ready threads */
buffer->waiting_flush= 0;
/* lock for the buffer. Current buffer also lock the handler */
- if (pthread_mutex_init(&buffer->mutex, MY_MUTEX_INIT_FAST) ||
+ if (my_pthread_mutex_init(&buffer->mutex, MY_MUTEX_INIT_FAST,
+ "translog_buffer->mutex",
+ MYF_NO_DEADLOCK_DETECTION) ||
pthread_cond_init(&buffer->prev_sent_to_disk_cond, 0))
DBUG_RETURN(1);
buffer->is_closing_buffer= 0;
diff --git a/storage/maria/ma_state.c b/storage/maria/ma_state.c
index 785f1689a37..a4b002dec64 100644
--- a/storage/maria/ma_state.c
+++ b/storage/maria/ma_state.c
@@ -366,6 +366,15 @@ my_bool _ma_check_status(void *param)
/**
@brief write hook at end of trans to store status for all used table
+
+ @Notes
+ This function must be called under trnman_lock in trnman_end_trn()
+ because of the following reasons:
+ - After trnman_end_trn() is called, the current transaction will be
+ regarded as committed and all used tables state_history will be
+ visible to other transactions. To do this, we loop over all used
+ tables and create/update a history entries that contains the correct
+ state_history for them.
*/
my_bool _ma_trnman_end_trans_hook(TRN *trn, my_bool commit,
@@ -390,6 +399,13 @@ my_bool _ma_trnman_end_trans_hook(TRN *trn, my_bool commit,
trnman_exists_active_transactions(share->state_history->trid,
trn->commit_trid, 1))
{
+ /*
+ There exist transactions that are still using the current
+ share->state_history. Create a new history item for this
+ commit and add it first in the state_history list. This
+ ensures that all history items are stored in the list in
+ decresing trid order.
+ */
if (!(history= my_malloc(sizeof(*history), MYF(MY_WME))))
{
/* purecov: begin inspected */
diff --git a/storage/maria/ma_test1.c b/storage/maria/ma_test1.c
index b0993f45524..affa3a71634 100644
--- a/storage/maria/ma_test1.c
+++ b/storage/maria/ma_test1.c
@@ -70,6 +70,9 @@ extern int _ma_flush_table_files(MARIA_HA *info, uint flush_data_or_index,
int main(int argc,char *argv[])
{
+#if defined(SAFE_MUTEX) && defined(THREAD)
+ safe_mutex_deadlock_detector= 1;
+#endif
MY_INIT(argv[0]);
get_options(argc,argv);
maria_data_root= (char *)".";
diff --git a/storage/maria/ma_test2.c b/storage/maria/ma_test2.c
index 08d3ae486cf..07d67b4fce4 100644
--- a/storage/maria/ma_test2.c
+++ b/storage/maria/ma_test2.c
@@ -72,6 +72,10 @@ int main(int argc, char *argv[])
const char *filename;
char *blob_buffer;
MARIA_CREATE_INFO create_info;
+
+#if defined(SAFE_MUTEX) && defined(THREAD)
+ safe_mutex_deadlock_detector= 1;
+#endif
MY_INIT(argv[0]);
filename= "test2";