List:Commits« Previous MessageNext Message »
From:Dmitry Lenev Date:September 15 2010 8:23pm
Subject:bzr push into mysql-5.5-runtime branch (dlenev:3137 to 3138)
View as plain text  
 3138 Dmitry Lenev	2010-09-16
      A new approach to rw_pr_lock_t implementation
      (based on Wlad's Windows-specific implementation).
      
      This is a draft patch which is committed mostly
      for purposes of benchmarking.

    modified:
      include/my_pthread.h
      mysys/thr_rwlock.c
 3137 Dmitry Lenev	2010-09-15
      Fix for bug #56251 "Deadlock with INSERT DELAYED and MERGE
      tables".
      
      Attempting to issue an INSERT DELAYED statement for a MERGE
      table might have caused a deadlock if it happened as part of
      a transaction or under LOCK TABLES, and there was a concurrent
      DDL or LOCK TABLES ... WRITE statement which tried to lock one
      of its underlying tables.
      
      The problem occurred when a delayed insert handler thread tried
      to open a MERGE table and discovered that to do this it had also
      to open all underlying tables and hence acquire metadata
      locks on them. Since metadata locks on the underlying tables were
      not pre-acquired by the connection thread executing INSERT DELAYED,
      attempts to do so might lead to waiting. In this case the
      connection thread had to wait for the delayed insert thread.
      If the thread which was preventing the lock on the underlying table
      from being acquired had to wait for the connection thread (due to
      this or other metadata locks), a deadlock occurred. 
      This deadlock was not detected by the MDL deadlock detector since 
      waiting for the handler thread by the connection thread is not
      represented in the wait-for graph.
      
      This patch solves the problem by ensuring that the delayed
      insert handler thread never tries to open underlying tables 
      of a MERGE table. Instead open_tables() is aborted right after
      the parent table is opened and a ER_DELAYED_NOT_SUPPORTED 
      error is emitted (which is passed to the connection thread and
      ultimately to the user).
     @ mysql-test/r/merge.result
        Added test for bug #56251 "Deadlock with INSERT DELAYED and
        MERGE tables".
     @ mysql-test/t/merge.test
        Added test for bug #56251 "Deadlock with INSERT DELAYED and
        MERGE tables".
     @ sql/sql_base.cc
        Changed open_n_lock_single_table() to take prelocking strategy
        as an argument instead of always using DML_prelocking_strategy.
     @ sql/sql_base.h
        Changed open_n_lock_single_table() to take prelocking strategy
        as an argument instead of always using DML_prelocking_strategy.
        Added a version of this function which is compatible with old
        signature.
     @ sql/sql_insert.cc
        When opening MERGE table in delayed insert thread stop and emit
        ER_DELAYED_NOT_SUPPORTED right after opening main table and
        before opening underlying tables. This ensures that we won't
        try to acquire metadata lock on underlying tables which might
        lead to a deadlock.
        This is achieved by using special prelocking strategy which
        abort open_tables() process as soon as we discover that we
        have opened table with engine which doesn't support delayed
        inserts.

    modified:
      mysql-test/r/merge.result
      mysql-test/t/merge.test
      sql/sql_base.cc
      sql/sql_base.h
      sql/sql_insert.cc
=== 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-15 20:22:43 +0000
@@ -612,37 +612,22 @@ int my_pthread_fastmutex_lock(my_pthread
   Required by some algorithms in order to provide correctness.
 */
 
-#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.
-*/
-#define rw_pr_lock_t pthread_rwlock_t
+typedef struct st_rw_pr_lock_t {
+  pthread_mutex_t lock;
+  pthread_cond_t no_active_readers;
+  int active_writer;
+  int active_readers;
+} 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_tryrdlock(rw_pr_lock_t *);
+extern int rw_pr_trywrlock(rw_pr_lock_t *);
+extern int rw_pr_unlock(rw_pr_lock_t *);
+extern int rw_pr_destroy(rw_pr_lock_t *);
 #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) */
 
 
 #ifdef NEED_MY_RW_LOCK

=== 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-15 20:22:43 +0000
@@ -192,30 +192,64 @@ 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_writer= rwlock->active_readers= 0;
+  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);
+  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);
+
+  while (rwlock->active_readers != 0)
+    pthread_cond_wait(&rwlock->no_active_readers, &rwlock->lock);
+
+  rwlock->active_writer= 1;
+  return 0;
+}
+
+
+int rw_pr_unlock(rw_pr_lock_t *rwlock)
+{
+  if (rwlock->active_writer)
+  {
+    rwlock->active_writer= 0;
+    pthread_cond_signal(&rwlock->no_active_readers);
+    pthread_mutex_unlock(&rwlock->lock);
+  }
+  else
+  {
+    pthread_mutex_lock(&rwlock->lock);
+    rwlock->active_readers--;
+    if (rwlock->active_readers == 0)
+      pthread_cond_signal(&rwlock->no_active_readers);
+    pthread_mutex_unlock(&rwlock->lock);
+  }
   return 0;
 }
 
-#endif /* defined(NEED_MY_RW_LOCK) */
 #endif /* defined(THREAD) */


Attachment: [text/bzr-bundle] bzr/dlenev@mysql.com-20100915202243-2zwy38gvju85a4a6.bundle
Thread
bzr push into mysql-5.5-runtime branch (dlenev:3137 to 3138)Dmitry Lenev15 Sep