From: Dmitry Lenev Date: September 27 2010 7:46am Subject: bzr commit into mysql-5.5-runtime branch (Dmitry.Lenev:3142) Bug#56405 Bug#56715 List-Archive: http://lists.mysql.com/commits/119140 X-Bug: 56405,56715 Message-Id: <20100927074628.3718B1E5171@mockturtle> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="===============2120301147==" --===============2120301147== MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Content-Disposition: inline #At file:///home/dlenev/src/bzr/mysql-5.5-rt-56715/ based on revid:jon.hauglid@stripped 3142 Dmitry Lenev 2010-09-27 A better fix for bug #56405 "Deadlock in the MDL deadlock detector", which doesn't introduce bug #56715 "Concurrent transactions + FLUSH result in sporadical unwarranted deadlock errors". Deadlock could have occurred when workload containing mix of DML, DDL and FLUSH TABLES statements affecting same set of tables was executed in heavily concurrent environment. This deadlock occurred when several connections tried to perform deadlock detection in metadata locking subsystem. The first connection started traversing wait-for graph, encountered sub-graph representing wait for flush, acquired LOCK_open and dived into sub-graph inspection. When it has encountered sub-graph corresponding to wait for metadata lock and blocked while trying to acquire rd-lock on MDL_lock::m_rwlock protecting this subgraph, since some other thread had wr-lock on it. When this wr-lock was released it could have happened (if there was other pending wr-lock against this rwlock) that rd-lock from the first connection was left unsatisfied but at the same time new rd-lock request from the second connection sneaked in and was satisfied (for this to be possible second rd- request should come exactly after wr-lock is released but before pending wr-lock manages to grab rwlock, which is possible both on Linux and in our own rwlock implementation). If this second connection continued traversing wait-for graph and encountered sub-graph representing wait for flush it tried to acquire LOCK_open and thus deadlock was created. The previous patch tried to workaround this problem by not allowing deadlock detector to lock LOCK_open mutex if some other thread doing deadlock detection already owns it and current search depth is greater than 0. Instead deadlock was reported. As result it has introduced bug #56715. This patch solves this problem in a different way. It introduces a new rw_pr_lock_t implementation to be used by MDL subsystem instead of one based on Linux rwlocks or our own rwlock implementation. This new implementation never allows situation in which rwlock is rd-locked and there is a blocked pending rd-lock. Thus situation which has caused this bug becomes impossible with it. Due to fact that this implementation is optimized for wr-lock/unlock scenario which is most common in MDL subsystem it doesn't introduce noticiable performance regressions in sysbench tests. Moreover it significantly improves situation for POINT_SELECT test when many connections are used. No test case is provided as this bug is very hard to repeat in MTR environment but is repeatable with the help of RQG tests. This patch also doesn't include test for bug #56715 "Concurrent transactions + FLUSH result in sporadical unwarranted deadlock errors" as it takes too much time to be run as part of normal test-suite runs. QQ: Should we also remove support for preferring readers from my_rw_lock_t implementation? @ config.h.cmake We no longer need to check for presence of pthread_rwlockattr_setkind_np as we no longer use Linux-specific implementation of rw_pr_lock_t which uses this function. @ configure.cmake We no longer need to check for presence of pthread_rwlockattr_setkind_np as we no longer use Linux-specific implementation of rw_pr_lock_t which uses this function. @ configure.in We no longer need to check for presence of pthread_rwlockattr_setkind_np as we no longer use Linux-specific implementation of rw_pr_lock_t which uses this function. @ include/my_pthread.h Introduced new implementation of rw_pr_lock_t. Since it never allows situation in which rwlock is rd-locked and there is a blocked pending rd-lock it is not affected by bug #56405 "Deadlock in the MDL deadlock detector". This implementation is also optimized for wr-lock/unlock scenario which is most common in MDL subsystem. So it doesn't introduce noticiable performance regressions in sysbench tests (compared to old Linux-specific implementation). Moreover it significantly improves situation for POINT_SELECT test when many connections are used. As part of this change removed try-lock part of API for this type of lock. It is not used in our code and it would be hard to implement correctly within constraints of new implementation. @ include/mysql/psi/mysql_thread.h Removed try-lock part of prlock API. It is not used in our code and it would be hard to implement correctly within constraints of new prlock implementation. @ mysys/thr_rwlock.c Introduced new implementation of rw_pr_lock_t. Since it never allows situation in which rwlock is rd-locked and there is a blocked pending rd-lock it is not affected by bug #56405 "Deadlock in the MDL deadlock detector". This implementation is also optimized for wr-lock/unlock scenario which is most common in MDL subsystem. So it doesn't introduce noticiable performance regressions in sysbench tests (compared to old Linux-specific implementation). Moreover it significantly improves situation for POINT_SELECT test when many connections are used. modified: config.h.cmake configure.cmake configure.in include/my_pthread.h include/mysql/psi/mysql_thread.h mysys/thr_rwlock.c === modified file 'config.h.cmake' --- a/config.h.cmake 2010-07-29 09:42:55 +0000 +++ b/config.h.cmake 2010-09-27 07:46:07 +0000 @@ -220,7 +220,6 @@ #cmakedefine HAVE_PTHREAD_KEY_DELETE 1 #cmakedefine HAVE_PTHREAD_KILL 1 #cmakedefine HAVE_PTHREAD_RWLOCK_RDLOCK 1 -#cmakedefine HAVE_PTHREAD_RWLOCKATTR_SETKIND_NP 1 #cmakedefine HAVE_PTHREAD_SETPRIO_NP 1 #cmakedefine HAVE_PTHREAD_SETSCHEDPARAM 1 #cmakedefine HAVE_PTHREAD_SIGMASK 1 === modified file 'configure.cmake' --- a/configure.cmake 2010-07-29 09:42:55 +0000 +++ b/configure.cmake 2010-09-27 07:46:07 +0000 @@ -346,7 +346,6 @@ CHECK_FUNCTION_EXISTS (pthread_condattr_ CHECK_FUNCTION_EXISTS (pthread_init HAVE_PTHREAD_INIT) CHECK_FUNCTION_EXISTS (pthread_key_delete HAVE_PTHREAD_KEY_DELETE) CHECK_FUNCTION_EXISTS (pthread_rwlock_rdlock HAVE_PTHREAD_RWLOCK_RDLOCK) -CHECK_FUNCTION_EXISTS (pthread_rwlockattr_setkind_np HAVE_PTHREAD_RWLOCKATTR_SETKIND_NP) CHECK_FUNCTION_EXISTS (pthread_sigmask HAVE_PTHREAD_SIGMASK) CHECK_FUNCTION_EXISTS (pthread_threadmask HAVE_PTHREAD_THREADMASK) CHECK_FUNCTION_EXISTS (pthread_yield_np HAVE_PTHREAD_YIELD_NP) === modified file 'configure.in' --- a/configure.in 2010-08-25 14:05:33 +0000 +++ b/configure.in 2010-09-27 07:46:07 +0000 @@ -2169,7 +2169,7 @@ AC_CHECK_FUNCS(alarm bfill bmove bsearch mkstemp mlockall perror poll pread pthread_attr_create mmap mmap64 getpagesize \ pthread_attr_getstacksize pthread_attr_setstacksize pthread_condattr_create \ pthread_getsequence_np pthread_key_delete pthread_rwlock_rdlock \ - pthread_rwlockattr_setkind_np pthread_sigmask \ + pthread_sigmask \ readlink realpath rename rint rwlock_init setupterm \ shmget shmat shmdt shmctl sigaction sigemptyset sigaddset \ sighold sigset sigthreadmask port_create sleep thr_yield \ === modified file 'include/my_pthread.h' --- a/include/my_pthread.h 2010-08-10 21:12:01 +0000 +++ b/include/my_pthread.h 2010-09-27 07:46:07 +0000 @@ -606,49 +606,82 @@ int my_pthread_fastmutex_lock(my_pthread #endif /* USE_MUTEX_INSTEAD_OF_RW_LOCKS */ -/* - Portable read-write locks which prefer readers. +/** + Portable implementation of special type of read-write locks. - Required by some algorithms in order to provide correctness. -*/ + These locks have two properties which are unusual for rwlocks: + 1) They "prefer readers" in the sense that they do not allow + situations in which rwlock is rd-locked and there is a + pending rd-lock which is blocked (e.g. due to pending + request for wr-lock). + This is a stronger guarantee than one which is provided for + PTHREAD_RWLOCK_PREFER_READER_NP rwlocks in Linux. + MDL subsystem deadlock detector relies on this property for + its correctness. + 2) They are optimized for uncontended wr-lock/unlock case. + This is scenario in which they are most oftenly used + within MDL subsystem. Optimizing for it gives significant + performance improvements in some of tests involving many + connections. + + Another important requirement imposed on this type of rwlock + by the MDL subsystem is that it should be OK to destroy rwlock + object which is in unlocked state even though some threads might + have not yet fully left unlock operation for it (of course there + is an external guarantee that no thread will try to lock rwlock + which is destroyed). + Putting it another way the unlock operation should not access + rwlock data after changing its state to unlocked. -#if defined(HAVE_PTHREAD_RWLOCK_RDLOCK) && defined(HAVE_PTHREAD_RWLOCKATTR_SETKIND_NP) -/* - On systems which have a way to specify that readers should - be preferred through attribute mechanism (e.g. Linux) we use - system implementation of read/write locks. + TODO/FIXME: We should consider alleviating this requirement as + it blocks us from doing certain performance optimizations. */ -#define rw_pr_lock_t pthread_rwlock_t + +typedef struct st_rw_pr_lock_t { + /** + Lock which protects the structure. + Also held for the duration of wr-lock. + */ + pthread_mutex_t lock; + /** + Condition variable which is used to wake-up + writers waiting for readers to go away. + */ + pthread_cond_t no_active_readers; + /** Number of active readers. */ + uint active_readers; + /** Number of writers waiting for readers to go away. */ + uint writers_waiting_readers; + /** Indicates whether there is an active writer. */ + my_bool active_writer; +#ifdef SAFE_MUTEX + /** Thread holding wr-lock (for debug purposes only). */ + pthread_t writer_thread; +#endif +} rw_pr_lock_t; + extern int rw_pr_init(rw_pr_lock_t *); -#define rw_pr_rdlock(A) pthread_rwlock_rdlock(A) -#define rw_pr_wrlock(A) pthread_rwlock_wrlock(A) -#define rw_pr_tryrdlock(A) pthread_rwlock_tryrdlock(A) -#define rw_pr_trywrlock(A) pthread_rwlock_trywrlock(A) -#define rw_pr_unlock(A) pthread_rwlock_unlock(A) -#define rw_pr_destroy(A) pthread_rwlock_destroy(A) +extern int rw_pr_rdlock(rw_pr_lock_t *); +extern int rw_pr_wrlock(rw_pr_lock_t *); +extern int rw_pr_unlock(rw_pr_lock_t *); +extern int rw_pr_destroy(rw_pr_lock_t *); +#ifdef SAFE_MUTEX +#define rw_pr_lock_assert_write_owner(A) \ + DBUG_ASSERT((A)->active_writer && pthread_equal(pthread_self(), \ + (A)->writer_thread)) +#define rw_pr_lock_assert_not_write_owner(A) \ + DBUG_ASSERT(! (A)->active_writer || ! pthread_equal(pthread_self(), \ + (A)->writer_thread)) +#else #define rw_pr_lock_assert_write_owner(A) #define rw_pr_lock_assert_not_write_owner(A) -#else -/* Otherwise we have to use our own implementation of read/write locks. */ -#define NEED_MY_RW_LOCK 1 -struct st_my_rw_lock_t; -#define rw_pr_lock_t my_rw_lock_t -extern int rw_pr_init(struct st_my_rw_lock_t *); -#define rw_pr_rdlock(A) my_rw_rdlock((A)) -#define rw_pr_wrlock(A) my_rw_wrlock((A)) -#define rw_pr_tryrdlock(A) my_rw_tryrdlock((A)) -#define rw_pr_trywrlock(A) my_rw_trywrlock((A)) -#define rw_pr_unlock(A) my_rw_unlock((A)) -#define rw_pr_destroy(A) my_rw_destroy((A)) -#define rw_pr_lock_assert_write_owner(A) my_rw_lock_assert_write_owner((A)) -#define rw_pr_lock_assert_not_write_owner(A) my_rw_lock_assert_not_write_owner((A)) -#endif /* defined(HAVE_PTHREAD_RWLOCK_RDLOCK) && defined(HAVE_PTHREAD_RWLOCKATTR_SETKIND_NP) */ +#endif /* SAFE_MUTEX */ #ifdef NEED_MY_RW_LOCK /* - On systems which don't support native read/write locks, or don't support - read/write locks which prefer readers we have to use own implementation. + On systems which don't support native read/write locks we have + to use own implementation. */ typedef struct st_my_rw_lock_t { pthread_mutex_t lock; /* lock for structure */ === modified file 'include/mysql/psi/mysql_thread.h' --- a/include/mysql/psi/mysql_thread.h 2010-08-10 21:12:01 +0000 +++ b/include/mysql/psi/mysql_thread.h 2010-09-27 07:46:07 +0000 @@ -141,9 +141,7 @@ typedef struct st_mysql_rwlock mysql_rwl @c mysql_prlock_t is a drop-in replacement for @c rw_pr_lock_t. @sa mysql_prlock_init @sa mysql_prlock_rdlock - @sa mysql_prlock_tryrdlock @sa mysql_prlock_wrlock - @sa mysql_prlock_trywrlock @sa mysql_prlock_unlock @sa mysql_prlock_destroy */ @@ -421,20 +419,6 @@ typedef struct st_mysql_cond mysql_cond_ #endif /** - @def mysql_prlock_tryrdlock(RW) - Instrumented rw_pr_tryrdlock. - @c mysql_prlock_tryrdlock is a drop-in replacement - for @c rw_pr_tryrdlock. -*/ -#ifdef HAVE_PSI_INTERFACE - #define mysql_prlock_tryrdlock(RW) \ - inline_mysql_prlock_tryrdlock(RW, __FILE__, __LINE__) -#else - #define mysql_prlock_tryrdlock(RW) \ - inline_mysql_prlock_tryrdlock(RW) -#endif - -/** @def mysql_rwlock_trywrlock(RW) Instrumented rwlock_trywrlock. @c mysql_rwlock_trywrlock is a drop-in replacement @@ -449,20 +433,6 @@ typedef struct st_mysql_cond mysql_cond_ #endif /** - @def mysql_prlock_trywrlock(RW) - Instrumented rw_pr_trywrlock. - @c mysql_prlock_trywrlock is a drop-in replacement - for @c rw_pr_trywrlock. -*/ -#ifdef HAVE_PSI_INTERFACE - #define mysql_prlock_trywrlock(RW) \ - inline_mysql_prlock_trywrlock(RW, __FILE__, __LINE__) -#else - #define mysql_prlock_trywrlock(RW) \ - inline_mysql_prlock_trywrlock(RW) -#endif - -/** @def mysql_rwlock_unlock(RW) Instrumented rwlock_unlock. @c mysql_rwlock_unlock is a drop-in replacement @@ -905,35 +875,6 @@ static inline int inline_mysql_rwlock_tr return result; } -#ifndef DISABLE_MYSQL_PRLOCK_H -static inline int inline_mysql_prlock_tryrdlock( - mysql_prlock_t *that -#ifdef HAVE_PSI_INTERFACE - , const char *src_file, uint src_line -#endif - ) -{ - int result; -#ifdef HAVE_PSI_INTERFACE - struct PSI_rwlock_locker *locker= NULL; - PSI_rwlock_locker_state state; - if (likely(PSI_server && that->m_psi)) - { - locker= PSI_server->get_thread_rwlock_locker(&state, that->m_psi, - PSI_RWLOCK_TRYREADLOCK); - if (likely(locker != NULL)) - PSI_server->start_rwlock_rdwait(locker, src_file, src_line); - } -#endif - result= rw_pr_tryrdlock(&that->m_prlock); -#ifdef HAVE_PSI_INTERFACE - if (likely(locker != NULL)) - PSI_server->end_rwlock_rdwait(locker, result); -#endif - return result; -} -#endif - static inline int inline_mysql_rwlock_trywrlock( mysql_rwlock_t *that #ifdef HAVE_PSI_INTERFACE @@ -961,35 +902,6 @@ static inline int inline_mysql_rwlock_tr return result; } -#ifndef DISABLE_MYSQL_PRLOCK_H -static inline int inline_mysql_prlock_trywrlock( - mysql_prlock_t *that -#ifdef HAVE_PSI_INTERFACE - , const char *src_file, uint src_line -#endif - ) -{ - int result; -#ifdef HAVE_PSI_INTERFACE - struct PSI_rwlock_locker *locker= NULL; - PSI_rwlock_locker_state state; - if (likely(PSI_server && that->m_psi)) - { - locker= PSI_server->get_thread_rwlock_locker(&state, that->m_psi, - PSI_RWLOCK_TRYWRITELOCK); - if (likely(locker != NULL)) - PSI_server->start_rwlock_wrwait(locker, src_file, src_line); - } -#endif - result= rw_pr_trywrlock(&that->m_prlock); -#ifdef HAVE_PSI_INTERFACE - if (likely(locker != NULL)) - PSI_server->end_rwlock_wrwait(locker, result); -#endif - return result; -} -#endif - static inline int inline_mysql_rwlock_unlock( mysql_rwlock_t *that) { === modified file 'mysys/thr_rwlock.c' --- a/mysys/thr_rwlock.c 2010-08-12 13:50:23 +0000 +++ b/mysys/thr_rwlock.c 2010-09-27 07:46:07 +0000 @@ -192,30 +192,127 @@ int my_rw_unlock(my_rw_lock_t *rwp) return(0); } +#endif /* defined(NEED_MY_RW_LOCK) */ + -int rw_pr_init(struct st_my_rw_lock_t *rwlock) +int rw_pr_init(rw_pr_lock_t *rwlock) { - my_bool prefer_readers_attr= TRUE; - return my_rw_init(rwlock, &prefer_readers_attr); + pthread_mutex_init(&rwlock->lock, NULL); + pthread_cond_init(&rwlock->no_active_readers, NULL); + rwlock->active_readers= 0; + rwlock->writers_waiting_readers= 0; + rwlock->active_writer= FALSE; +#ifdef SAFE_MUTEX + rwlock->writer_thread= 0; +#endif + return 0; } -#else -/* - We are on system which has native read/write locks which support - preferring of readers. -*/ +int rw_pr_destroy(rw_pr_lock_t *rwlock) +{ + pthread_cond_destroy(&rwlock->no_active_readers); + pthread_mutex_destroy(&rwlock->lock); + return 0; +} -int rw_pr_init(rw_pr_lock_t *rwlock) + +int rw_pr_rdlock(rw_pr_lock_t *rwlock) { - pthread_rwlockattr_t rwlock_attr; + pthread_mutex_lock(&rwlock->lock); + /* + The fact that we were able to acquire 'lock' mutex means + that there are no active writers and we can acquire rd-lock. + Increment active readers counter to prevent requests for + wr-lock from succeeding and unlock mutex. + */ + rwlock->active_readers++; + pthread_mutex_unlock(&rwlock->lock); + return 0; +} - pthread_rwlockattr_init(&rwlock_attr); - pthread_rwlockattr_setkind_np(&rwlock_attr, PTHREAD_RWLOCK_PREFER_READER_NP); - pthread_rwlock_init(rwlock, NULL); - pthread_rwlockattr_destroy(&rwlock_attr); + +int rw_pr_wrlock(rw_pr_lock_t *rwlock) +{ + pthread_mutex_lock(&rwlock->lock); + + if (rwlock->active_readers != 0) + { + /* There are active readers. We have to wait until they are gone. */ + rwlock->writers_waiting_readers++; + + while (rwlock->active_readers != 0) + pthread_cond_wait(&rwlock->no_active_readers, &rwlock->lock); + + rwlock->writers_waiting_readers--; + } + + /* + We own 'lock' mutex so there is no active writers. + Also there are no active readers. + This means that we can grant wr-lock. + Not releasing 'lock' mutex until unlock will block + both requests for rd and wr-locks. + Set 'active_writer' flag to simplify unlock. + + Thanks to the fact wr-lock/unlock in the absence of + contention from readers is essentially mutex lock/unlock + with a few simple checks make this rwlock implementation + wr-lock optimized. + */ + rwlock->active_writer= TRUE; +#ifdef SAFE_MUTEX + rwlock->writer_thread= pthread_self(); +#endif return 0; } -#endif /* defined(NEED_MY_RW_LOCK) */ + +int rw_pr_unlock(rw_pr_lock_t *rwlock) +{ + if (rwlock->active_writer) + { + /* We are unlocking wr-lock. */ +#ifdef SAFE_MUTEX + rwlock->writer_thread= 0; +#endif + rwlock->active_writer= FALSE; + if (rwlock->writers_waiting_readers) + { + /* + Avoid expensive cond signal in case when there is no contention + or it is wr-only. + + Note that from view point of performance it would be better to + signal on the condition variable after unlocking mutex (as it + reduces number of contex switches). + + Unfortunately this would mean that such rwlock can't be safely + used by MDL subsystem, which relies on the fact that it is OK + to destroy rwlock once it is in unlocked state. + */ + pthread_cond_signal(&rwlock->no_active_readers); + } + pthread_mutex_unlock(&rwlock->lock); + } + else + { + /* We are unlocking rd-lock. */ + pthread_mutex_lock(&rwlock->lock); + rwlock->active_readers--; + if (rwlock->active_readers == 0 && + rwlock->writers_waiting_readers) + { + /* + If we are last reader and there are waiting + writers wake them up. + */ + pthread_cond_signal(&rwlock->no_active_readers); + } + pthread_mutex_unlock(&rwlock->lock); + } + return 0; +} + + #endif /* defined(THREAD) */ --===============2120301147== MIME-Version: 1.0 Content-Type: text/bzr-bundle; charset="us-ascii"; name="bzr/dmitry.lenev@stripped" Content-Transfer-Encoding: 7bit Content-Disposition: inline # Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: dmitry.lenev@stripped # target_branch: file:///home/dlenev/src/bzr/mysql-5.5-rt-56715/ # testament_sha1: 28e7d427c272df63cb59f51a24be2418f8d2559a # timestamp: 2010-09-27 11:46:27 +0400 # base_revision_id: jon.hauglid@stripped\ # mpy5ty4vmw0k5j19 # # Begin bundle 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 --===============2120301147==--