MySQL Lists are EOL. Please join:

List:Commits« Previous MessageNext Message »
From:monty Date:May 24 2006 2:21pm
Subject:bk commit into 5.0 tree (monty:1.2140) BUG#20048
View as plain text  
Below is the list of changes that have just been committed into a local
5.0 repository of monty. When monty does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html

ChangeSet
  1.2140 06/05/24 17:21:35 monty@stripped +8 -0
  More DBUG statements
  Replaced COND_refresh with COND_global_read_lock becasue of a bug in NTPL threads when using different mutexes as arguments to pthread_cond_wait()
  The original code caused a hang in FLUSH TABLES WITH READ LOCK in some circumstances because pthread_cond_broadcast() was not delivered to other threads.
  This fixes:
  Bug#16986: Deadlock condition with MyISAM tables
  Bug#20048: FLUSH TABLES WITH READ LOCK causes a deadlock

  sql/sql_base.cc
    1.337 06/05/24 17:21:32 monty@stripped +17 -1
    More DBUG statements
    Added a broadcast in remove_table_from_cache() to release any threads waiting in open

  sql/mysqld.cc
    1.550 06/05/24 17:21:32 monty@stripped +4 -4
    Added COND_global_read_lock

  sql/mysql_priv.h
    1.392 06/05/24 17:21:32 monty@stripped +1 -0
    Added COND_global_read_lock

  sql/lock.cc
    1.88 06/05/24 17:21:32 monty@stripped +25 -9
    Replaced COND_refresh with COND_global_read_lock becasue of a bug in NTPL threads when using different mutexes as arguments to pthread_cond_wait()
    The original code caused a hang in FLUSH TABLES WITH READ LOCK in some circumstances because pthread_cond_broadcast() was not delivered to other threads

  mysql-test/t/lock_multi.test
    1.14 06/05/24 17:21:32 monty@stripped +32 -0
    Test for bug in LOCK TABLE + optimize table

  mysql-test/t/flush.test
    1.20 06/05/24 17:21:32 monty@stripped +40 -0
    Added test case for deadlock with FLUSH TABLES WITH READ LOCK

  mysql-test/r/lock_multi.result
    1.16 06/05/24 17:21:32 monty@stripped +16 -0
    Test for bug in LOCK TABLE + optimize table

  mysql-test/r/flush.result
    1.14 06/05/24 17:21:32 monty@stripped +7 -0
    Added test case for deadlock with FLUSH TABLES WITH READ LOCK

# This is a BitKeeper patch.  What follows are the unified diffs for the
# set of deltas contained in the patch.  The rest of the patch, the part
# that BitKeeper cares about, is below these diffs.
# User:	monty
# Host:	narttu.mysql.fi
# Root:	/home/my/mysql-5.0

--- 1.87/sql/lock.cc	2006-04-20 04:08:10 +03:00
+++ 1.88/sql/lock.cc	2006-05-24 17:21:32 +03:00
@@ -1138,16 +1138,17 @@
 
   if (!thd->global_read_lock)
   {
+    const char *old_message;
     (void) pthread_mutex_lock(&LOCK_global_read_lock);
-    const char *old_message=thd->enter_cond(&COND_refresh, &LOCK_global_read_lock,
-					    "Waiting to get readlock");
+    old_message=thd->enter_cond(&COND_global_read_lock, &LOCK_global_read_lock,
+                                "Waiting to get readlock");
     DBUG_PRINT("info",
 	       ("waiting_for: %d  protect_against: %d",
 		waiting_for_read_lock, protect_against_global_read_lock));
 
     waiting_for_read_lock++;
     while (protect_against_global_read_lock && !thd->killed)
-      pthread_cond_wait(&COND_refresh, &LOCK_global_read_lock);
+      pthread_cond_wait(&COND_global_read_lock, &LOCK_global_read_lock);
     waiting_for_read_lock--;
     if (thd->killed)
     {
@@ -1169,9 +1170,15 @@
   DBUG_RETURN(0);
 }
 
+
 void unlock_global_read_lock(THD *thd)
 {
   uint tmp;
+  DBUG_ENTER("unlock_global_read_lock");
+  DBUG_PRINT("info",
+             ("global_read_lock: %u  global_read_lock_blocks_commit: %u",
+              global_read_lock, global_read_lock_blocks_commit));
+
   pthread_mutex_lock(&LOCK_global_read_lock);
   tmp= --global_read_lock;
   if (thd->global_read_lock == MADE_GLOBAL_READ_LOCK_BLOCK_COMMIT)
@@ -1179,8 +1186,13 @@
   pthread_mutex_unlock(&LOCK_global_read_lock);
   /* Send the signal outside the mutex to avoid a context switch */
   if (!tmp)
-    pthread_cond_broadcast(&COND_refresh);
+  {
+    DBUG_PRINT("signal", ("Broadcasting COND_global_read_lock"));
+    pthread_cond_broadcast(&COND_global_read_lock);
+  }
   thd->global_read_lock= 0;
+
+  DBUG_VOID_RETURN;
 }
 
 #define must_wait (global_read_lock &&                             \
@@ -1218,11 +1230,15 @@
       */
       DBUG_RETURN(is_not_commit);
     }
-    old_message=thd->enter_cond(&COND_refresh, &LOCK_global_read_lock,
+    old_message=thd->enter_cond(&COND_global_read_lock, &LOCK_global_read_lock,
 				"Waiting for release of readlock");
     while (must_wait && ! thd->killed &&
 	   (!abort_on_refresh || thd->version == refresh_version))
-      (void) pthread_cond_wait(&COND_refresh,&LOCK_global_read_lock);
+    {
+      DBUG_PRINT("signal", ("Waiting for COND_global_read_lock"));
+      (void) pthread_cond_wait(&COND_global_read_lock, &LOCK_global_read_lock);
+      DBUG_PRINT("signal", ("Got COND_global_read_lock"));
+    }
     if (thd->killed)
       result=1;
   }
@@ -1251,7 +1267,7 @@
         (waiting_for_read_lock || global_read_lock_blocks_commit));
   (void) pthread_mutex_unlock(&LOCK_global_read_lock);
   if (tmp)
-    pthread_cond_broadcast(&COND_refresh);
+    pthread_cond_broadcast(&COND_global_read_lock);
   DBUG_VOID_RETURN;
 }
 
@@ -1273,10 +1289,10 @@
   /* For testing we set up some blocking, to see if we can be killed */
   DBUG_EXECUTE_IF("make_global_read_lock_block_commit_loop",
                   protect_against_global_read_lock++;);
-  old_message= thd->enter_cond(&COND_refresh, &LOCK_global_read_lock,
+  old_message= thd->enter_cond(&COND_global_read_lock, &LOCK_global_read_lock,
                                "Waiting for all running commits to finish");
   while (protect_against_global_read_lock && !thd->killed)
-    pthread_cond_wait(&COND_refresh, &LOCK_global_read_lock);
+    pthread_cond_wait(&COND_global_read_lock, &LOCK_global_read_lock);
   DBUG_EXECUTE_IF("make_global_read_lock_block_commit_loop",
                   protect_against_global_read_lock--;);
   if ((error= test(thd->killed)))

--- 1.391/sql/mysql_priv.h	2006-05-24 11:56:55 +03:00
+++ 1.392/sql/mysql_priv.h	2006-05-24 17:21:32 +03:00
@@ -1220,6 +1220,7 @@
 #endif
 extern rw_lock_t LOCK_grant, LOCK_sys_init_connect, LOCK_sys_init_slave;
 extern pthread_cond_t COND_refresh, COND_thread_count, COND_manager;
+extern pthread_cond_t COND_global_read_lock;
 extern pthread_attr_t connection_attrib;
 extern I_List<THD> threads;
 extern I_List<NAMED_LIST> key_caches;

--- 1.549/sql/mysqld.cc	2006-05-20 02:13:50 +03:00
+++ 1.550/sql/mysqld.cc	2006-05-24 17:21:32 +03:00
@@ -516,7 +516,7 @@
 pthread_mutex_t LOCK_des_key_file;
 #endif
 rw_lock_t	LOCK_grant, LOCK_sys_init_connect, LOCK_sys_init_slave;
-pthread_cond_t COND_refresh,COND_thread_count;
+pthread_cond_t COND_refresh,COND_thread_count, COND_global_read_lock;
 pthread_t signal_thread;
 pthread_attr_t connection_attrib;
 
@@ -1235,6 +1235,7 @@
   (void) pthread_mutex_destroy(&LOCK_prepared_stmt_count);
   (void) pthread_cond_destroy(&COND_thread_count);
   (void) pthread_cond_destroy(&COND_refresh);
+  (void) pthread_cond_destroy(&COND_global_read_lock);
   (void) pthread_cond_destroy(&COND_thread_cache);
   (void) pthread_cond_destroy(&COND_flush_thread_cache);
   (void) pthread_cond_destroy(&COND_manager);
@@ -1657,13 +1658,11 @@
     }
   }
 
-  DBUG_PRINT("info", ("sending a broadcast"))
-
   /* Tell main we are ready */
   (void) pthread_mutex_unlock(&LOCK_thread_count);
   /* It's safe to broadcast outside a lock (COND... is not deleted here) */
+  DBUG_PRINT("signal", ("Broadcasting COND_thread_count"));
   (void) pthread_cond_broadcast(&COND_thread_count);
-  DBUG_PRINT("info", ("unlocked thread_count mutex"))
 #ifdef ONE_THREAD
   if (!(test_flags & TEST_NO_THREADS))	// For debugging under Linux
 #endif
@@ -2811,6 +2810,7 @@
   (void) my_rwlock_init(&LOCK_grant, NULL);
   (void) pthread_cond_init(&COND_thread_count,NULL);
   (void) pthread_cond_init(&COND_refresh,NULL);
+  (void) pthread_cond_init(&COND_global_read_lock,NULL);
   (void) pthread_cond_init(&COND_thread_cache,NULL);
   (void) pthread_cond_init(&COND_flush_thread_cache,NULL);
   (void) pthread_cond_init(&COND_manager,NULL);

--- 1.336/sql/sql_base.cc	2006-05-15 20:23:06 +03:00
+++ 1.337/sql/sql_base.cc	2006-05-24 17:21:32 +03:00
@@ -316,7 +316,7 @@
     bool found=1;
     /* Wait until all threads has closed all the tables we had locked */
     DBUG_PRINT("info",
-	       ("Waiting for others threads to close their open tables"));
+	       ("Waiting for other threads to close their open tables"));
     while (found && ! thd->killed)
     {
       found=0;
@@ -326,6 +326,7 @@
 	if ((table->s->version) < refresh_version && table->db_stat)
 	{
 	  found=1;
+          DBUG_PRINT("signal", ("Waiting for COND_refresh"));
 	  pthread_cond_wait(&COND_refresh,&LOCK_open);
 	  break;
 	}
@@ -1046,6 +1047,7 @@
 
 void wait_for_refresh(THD *thd)
 {
+  DBUG_ENTER("wait_for_refresh");
   safe_mutex_assert_owner(&LOCK_open);
 
   /* Wait until the current table is up to date */
@@ -1063,6 +1065,7 @@
   thd->mysys_var->current_cond= 0;
   thd->proc_info= proc_info;
   pthread_mutex_unlock(&thd->mysys_var->mutex);
+  DBUG_VOID_RETURN;
 }
 
 
@@ -1346,6 +1349,9 @@
   {
     if (table->s->version != refresh_version)
     {
+      DBUG_PRINT("note",
+                 ("Found table '%s.%s' with different refresh version",
+                  table_list->db, table_list->table_name));
       if (flags & MYSQL_LOCK_IGNORE_FLUSH)
       {
         /* Force close at once after usage */
@@ -5123,6 +5129,7 @@
   TABLE *table;
   bool result=0, signalled= 0;
   DBUG_ENTER("remove_table_from_cache");
+  DBUG_PRINT("enter", ("Table: '%s.%s'  flags: %u", db, table_name, flags));
 
   key_length=(uint) (strmov(strmov(key,db)+1,table_name)-key)+1;
   for (;;)
@@ -5147,7 +5154,10 @@
       {
         in_use->some_tables_deleted=1;
         if (table->db_stat)
+        {
+          DBUG_PRINT("info", ("Found another active instance of the table"));
   	  result=1;
+        }
         /* Kill delayed insert threads */
         if ((in_use->system_thread & SYSTEM_THREAD_DELAYED_INSERT) &&
             ! in_use->killed)
@@ -5182,6 +5192,12 @@
       VOID(hash_delete(&open_cache,(byte*) unused_tables));
     if (result && (flags & RTFC_WAIT_OTHER_THREAD_FLAG))
     {
+      /*
+        Signal any thread waiting for tables to be freed to
+        reopen their tables
+      */
+      (void) pthread_cond_broadcast(&COND_refresh);
+      DBUG_PRINT("info", ("Waiting for refresh signal"));
       if (!(flags & RTFC_CHECK_KILLED_FLAG) || !thd->killed)
       {
         dropping_tables++;

--- 1.15/mysql-test/r/lock_multi.result	2005-09-15 22:17:37 +03:00
+++ 1.16/mysql-test/r/lock_multi.result	2006-05-24 17:21:32 +03:00
@@ -43,3 +43,19 @@
 a	int(11)	YES		NULL	
 unlock tables;
 drop table t1;
+use mysql;
+LOCK TABLES columns_priv WRITE, db WRITE, host WRITE, user WRITE;
+FLUSH TABLES;
+use mysql;
+ SELECT user.Select_priv FROM user, db WHERE user.user = db.user LIMIT 1;
+OPTIMIZE TABLES columns_priv, db, host, user;
+Table	Op	Msg_type	Msg_text
+mysql.columns_priv	optimize	status	OK
+mysql.db	optimize	status	OK
+mysql.host	optimize	status	OK
+mysql.user	optimize	status	OK
+UNLOCK TABLES;
+Select_priv
+N
+use test;
+use test;

--- 1.13/mysql-test/t/lock_multi.test	2005-09-15 22:17:37 +03:00
+++ 1.14/mysql-test/t/lock_multi.test	2006-05-24 17:21:32 +03:00
@@ -107,3 +107,35 @@
 connection locker;
 unlock tables;
 drop table t1;
+
+#
+# Bug#16986 - Deadlock condition with MyISAM tables
+#
+connection locker;
+use mysql;
+LOCK TABLES columns_priv WRITE, db WRITE, host WRITE, user WRITE;
+FLUSH TABLES;
+--sleep 1
+#
+connection reader;
+use mysql;
+#NOTE:  This must be a multi-table select, otherwise the deadlock will not occur
+send SELECT user.Select_priv FROM user, db WHERE user.user = db.user LIMIT 1;
+--sleep 1
+#
+connection locker;
+# Make test case independent from earlier grants.
+--replace_result "Table is already up to date" "OK"
+OPTIMIZE TABLES columns_priv, db, host, user;
+UNLOCK TABLES;
+#
+connection reader;
+reap;
+use test;
+#
+connection locker;
+use test;
+#
+connection default;
+
+# End of 5.0 tests

--- 1.13/mysql-test/r/flush.result	2005-08-07 21:46:53 +03:00
+++ 1.14/mysql-test/r/flush.result	2006-05-24 17:21:32 +03:00
@@ -48,3 +48,10 @@
 flush tables with read lock;
 unlock tables;
 drop table t1, t2, t3;
+create table t1 (c1 int);
+create table t2 (c1 int);
+lock table t1 write;
+ flush tables with read lock;
+ insert into t2 values(1);
+unlock tables;
+drop table t1, t2;

--- 1.19/mysql-test/t/flush.test	2005-09-01 18:27:00 +03:00
+++ 1.20/mysql-test/t/flush.test	2006-05-24 17:21:32 +03:00
@@ -102,3 +102,43 @@
 drop table t1, t2, t3;
 
 # End of 4.1 tests
+
+#
+# Test of deadlock problem when doing FLUSH TABLE with read lock
+# (Bug was in NTPL threads in Linux when using different mutex while
+#  waiting for a condtion variable)
+
+create table t1 (c1 int);
+create table t2 (c1 int);
+
+connect (con1,localhost,root,,);
+connect (con3,localhost,root,,);
+
+connection con1;
+lock table t1 write;
+
+connection con2;
+send flush tables with read lock;
+--sleep 1
+
+connection con3;
+send insert into t2 values(1);
+--sleep 1
+
+connection con1;
+unlock tables;
+disconnect con1;
+
+connection con2;
+reap;
+disconnect con2;
+
+connection con3;
+# It hangs here (insert into t2 does not end).
+reap;
+disconnect con3;
+
+connection default;
+drop table t1, t2;
+
+# End of 5.0 tests
Thread
bk commit into 5.0 tree (monty:1.2140) BUG#20048monty24 May