[Commits] Rev 3951: MDEV-5492 - Reduce usage of LOCK_open: TABLE::in_use in lp:maria/10.0

Sergey Vojtovich svoj at mariadb.org
Fri Dec 27 19:05:57 EET 2013


At lp:maria/10.0

------------------------------------------------------------
revno: 3951
revision-id: svoj at mariadb.org-20131227170552-iun4ri4odnfnya46
parent: svoj at mariadb.org-20131219120928-3zjt3lwo1hnkryxf
committer: Sergey Vojtovich <svoj at mariadb.org>
branch nick: 10.0
timestamp: Fri 2013-12-27 21:05:52 +0400
message:
  MDEV-5492 - Reduce usage of LOCK_open: TABLE::in_use
  
  Move TABLE::in_use out of LOCK_open.
=== modified file 'sql/sql_base.cc'
--- a/sql/sql_base.cc	2013-12-12 17:49:14 +0000
+++ b/sql/sql_base.cc	2013-12-27 17:05:52 +0000
@@ -383,6 +383,7 @@ void kill_delayed_threads_for_table(TABL
 
   while ((tab= it++))
   {
+    mysql_rwlock_rdlock(&tab->LOCK_in_use);
     THD *in_use= tab->in_use;
 
     if (in_use && (in_use->system_thread & SYSTEM_THREAD_DELAYED_INSERT) &&
@@ -398,6 +399,7 @@ void kill_delayed_threads_for_table(TABL
       }
       mysql_mutex_unlock(&in_use->mysys_var->mutex);
     }
+    mysql_rwlock_unlock(&tab->LOCK_in_use);
   }
 }
 

=== modified file 'sql/sql_test.cc'
--- a/sql/sql_test.cc	2013-12-12 17:49:14 +0000
+++ b/sql/sql_test.cc	2013-12-27 17:05:52 +0000
@@ -94,12 +94,14 @@ static void print_cached_tables(void)
     TABLE_SHARE::All_share_tables_list::Iterator it(share->tdc.all_tables);
     while ((entry= it++))
     {
+      mysql_rwlock_rdlock(&entry->LOCK_in_use);
       printf("%-14.14s %-32s%6ld%8ld%6d  %s\n",
              entry->s->db.str, entry->s->table_name.str, entry->s->version,
              entry->in_use ? entry->in_use->thread_id : 0,
              entry->db_stat ? 1 : 0,
              entry->in_use ? lock_descriptions[(int)entry->reginfo.lock_type] :
                              "Not in use");
+      mysql_rwlock_unlock(&entry->LOCK_in_use);
     }
   }
   mysql_mutex_unlock(&LOCK_open);

=== modified file 'sql/table.cc'
--- a/sql/table.cc	2013-12-10 15:00:36 +0000
+++ b/sql/table.cc	2013-12-27 17:05:52 +0000
@@ -3808,7 +3808,7 @@ bool TABLE_SHARE::visit_subgraph(Wait_fo
   bool result= TRUE;
 
   /*
-    To protect used_tables list from being concurrently modified
+    To protect all_tables list from being concurrently modified
     while we are iterating through it we acquire LOCK_open.
     This does not introduce deadlocks in the deadlock detector
     because we won't try to acquire LOCK_open while
@@ -3835,19 +3835,25 @@ bool TABLE_SHARE::visit_subgraph(Wait_fo
 
   while ((table= tables_it++))
   {
+    mysql_rwlock_rdlock(&table->LOCK_in_use);
     if (table->in_use && gvisitor->inspect_edge(&table->in_use->mdl_context))
     {
+      mysql_rwlock_unlock(&table->LOCK_in_use);
       goto end_leave_node;
     }
+    mysql_rwlock_unlock(&table->LOCK_in_use);
   }
 
   tables_it.rewind();
   while ((table= tables_it++))
   {
+    mysql_rwlock_rdlock(&table->LOCK_in_use);
     if (table->in_use && table->in_use->mdl_context.visit_subgraph(gvisitor))
     {
+      mysql_rwlock_unlock(&table->LOCK_in_use);
       goto end_leave_node;
     }
+    mysql_rwlock_unlock(&table->LOCK_in_use);
   }
 
   result= FALSE;

=== modified file 'sql/table.h'
--- a/sql/table.h	2013-12-12 17:49:14 +0000
+++ b/sql/table.h	2013-12-27 17:05:52 +0000
@@ -1021,6 +1021,7 @@ struct TABLE
 public:
 
   THD	*in_use;                        /* Which thread uses this */
+  mysql_rwlock_t LOCK_in_use;
   /* Time when table was released to table cache. Valid for unused tables. */
   ulonglong tc_time;
   Field **field;			/* Pointer to fields */

=== modified file 'sql/table_cache.cc'
--- a/sql/table_cache.cc	2013-12-19 12:09:28 +0000
+++ b/sql/table_cache.cc	2013-12-27 17:05:52 +0000
@@ -44,6 +44,8 @@
 
   Table cache invariants:
   - TABLE_SHARE::free_tables shall not contain objects with TABLE::in_use != 0
+  - TABLE_SHARE::free_tables shall not receive new objects if table is marked
+    old (TABLE_SHARE::version != tdc_version)
 */
 
 #include "my_global.h"
@@ -68,8 +70,7 @@ static Atomic<uint> tc_count; /**< Numbe
 
 
 /**
-  Protects TABLE_SHARE::tdc.free_tables, TABLE_SHARE::tdc.all_tables,
-  TABLE::in_use.
+  Protects TABLE_SHARE::tdc.free_tables, TABLE_SHARE::tdc.all_tables.
 */
 
 mysql_mutex_t LOCK_open;
@@ -99,11 +100,13 @@ static PSI_mutex_info all_tc_mutexes[]=
   { &key_TABLE_SHARE_LOCK_table_share, "TABLE_SHARE::tdc.LOCK_table_share", 0 }
 };
 
-static PSI_rwlock_key key_rwlock_LOCK_tdc, key_rwlock_LOCK_flush;
+static PSI_rwlock_key key_rwlock_LOCK_tdc, key_rwlock_LOCK_flush,
+                      key_rwlock_LOCK_in_use;
 static PSI_rwlock_info all_tc_rwlocks[]=
 {
   { &key_rwlock_LOCK_tdc, "LOCK_tdc", PSI_FLAG_GLOBAL },
-  { &key_rwlock_LOCK_flush, "LOCK_flush", PSI_FLAG_GLOBAL }
+  { &key_rwlock_LOCK_flush, "LOCK_flush", PSI_FLAG_GLOBAL },
+  { &key_rwlock_LOCK_in_use, "TABLE::LOCK_in_use", PSI_FLAG_GLOBAL }
 };
 
 
@@ -142,6 +145,24 @@ uint tc_records(void)
 
 
 /**
+  Remove unused TABLE object from table cache.
+
+  @pre LOCK_open is locked, table is not used.
+
+  @note This is helper routine, supposed to be used by table cache
+  methods only.
+*/
+
+static void tc_remove_table(TABLE *table)
+{
+  mysql_mutex_assert_owner(&LOCK_open);
+  tc_count--;
+  table->s->tdc.all_tables.remove(table);
+  mysql_rwlock_destroy(&table->LOCK_in_use);
+}
+
+
+/**
   Free all unused TABLE objects.
 
   While locked:
@@ -169,9 +190,8 @@ void tc_purge(void)
   {
     while ((table= share->tdc.free_tables.pop_front()))
     {
-      share->tdc.all_tables.remove(table);
+      tc_remove_table(table);
       purge_tables.push_front(table);
-      tc_count--;
     }
   }
   tdc_it.deinit();
@@ -185,47 +205,6 @@ void tc_purge(void)
 
 
 /**
-  Verify consistency of used/unused lists (for debugging).
-*/
-
-#ifdef EXTRA_DEBUG
-static void check_unused(THD *thd)
-{
-  TABLE *entry;
-  TABLE_SHARE *share;
-  TDC_iterator tdc_it;
-
-  tdc_it.init();
-  mysql_mutex_lock(&LOCK_open);
-  while ((share= tdc_it.next()))
-  {
-    TABLE_SHARE::TABLE_list::Iterator it(share->tdc.free_tables);
-    while ((entry= it++))
-    {
-      /*
-        We must not have TABLEs in the free list that have their file closed.
-      */
-      DBUG_ASSERT(entry->db_stat && entry->file);
-      /* Merge children should be detached from a merge parent */
-      if (entry->in_use)
-      {
-        DBUG_PRINT("error",("Used table is in share's list of unused tables")); /* purecov: inspected */
-      }
-      /* extra() may assume that in_use is set */
-      entry->in_use= thd;
-      DBUG_ASSERT(!thd || !entry->file->extra(HA_EXTRA_IS_ATTACHED_CHILDREN));
-      entry->in_use= 0;
-    }
-  }
-  mysql_mutex_unlock(&LOCK_open);
-  tdc_it.deinit();
-}
-#else
-#define check_unused(A)
-#endif
-
-
-/**
   Add new TABLE object to table cache.
 
   @pre TABLE object is used by caller.
@@ -243,6 +222,8 @@ static void check_unused(THD *thd)
 
 void tc_add_table(THD *thd, TABLE *table)
 {
+  mysql_rwlock_init(key_rwlock_LOCK_in_use, &table->LOCK_in_use);
+
   DBUG_ASSERT(table->in_use == thd);
   mysql_mutex_lock(&LOCK_open);
   table->s->tdc.all_tables.push_front(table);
@@ -270,13 +251,11 @@ void tc_add_table(THD *thd, TABLE *table
     if (purge_table)
     {
       purge_table->s->tdc.free_tables.remove(purge_table);
-      purge_table->s->tdc.all_tables.remove(purge_table);
+      tc_remove_table(purge_table);
       mysql_rwlock_rdlock(&LOCK_flush);
       mysql_mutex_unlock(&LOCK_open);
       intern_close_table(purge_table);
       mysql_rwlock_unlock(&LOCK_flush);
-      tc_count--;
-      check_unused(thd);
     }
     else
       mysql_mutex_unlock(&LOCK_open);
@@ -292,7 +271,9 @@ void tc_add_table(THD *thd, TABLE *table
   Acquired object cannot be evicted or acquired again.
 
   While locked:
-  - pop object from TABLE_SHARE::tdc.free_tables()
+  - pop object from TABLE_SHARE::tdc.free_tables
+
+  While unlocked:
   - mark object used by thd
 
   @return TABLE object, or NULL if no unused objects.
@@ -308,10 +289,12 @@ static TABLE *tc_acquire_table(THD *thd,
     mysql_mutex_unlock(&LOCK_open);
     return 0;
   }
-  DBUG_ASSERT(!table->in_use);
-  table->in_use= thd;
   mysql_mutex_unlock(&LOCK_open);
 
+  DBUG_ASSERT(!table->in_use);
+  mysql_rwlock_wrlock(&table->LOCK_in_use);
+  table->in_use= thd;
+  mysql_rwlock_unlock(&table->LOCK_in_use);
   /* The ex-unused table must be fully functional. */
   DBUG_ASSERT(table->db_stat && table->file);
   /* The children must be detached from the table. */
@@ -328,12 +311,12 @@ static TABLE *tc_acquire_table(THD *thd,
   Released object may be evicted or acquired again.
 
   While locked:
-  - mark object not in use by any thread
   - if object is marked for purge, decrement tc_count
   - add object to TABLE_SHARE::tdc.free_tables
   - evict LRU object from table cache if we reached threshold
 
   While unlocked:
+  - mark object not in use by any thread
   - free evicted/purged object
 
   @note Another thread may mark share for purge any moment (even
@@ -352,28 +335,28 @@ bool tc_release_table(TABLE *table)
   DBUG_ASSERT(table->in_use);
   DBUG_ASSERT(table->file);
 
+  mysql_rwlock_wrlock(&table->LOCK_in_use);
+  table->in_use= 0;
+  mysql_rwlock_unlock(&table->LOCK_in_use);
+
   if (table->needs_reopen() || tc_count > tc_size)
   {
     mysql_mutex_lock(&LOCK_open);
-    table->in_use= 0;
     goto purge;
   }
 
   table->tc_time= my_interval_timer();
 
   mysql_mutex_lock(&LOCK_open);
-  table->in_use= 0;
   if (table->s->has_old_version())
     goto purge;
   /* Add table to the list of unused TABLE objects for this share. */
   table->s->tdc.free_tables.push_front(table);
   mysql_mutex_unlock(&LOCK_open);
-  check_unused(thd);
   return false;
 
 purge:
-  tc_count--;
-  table->s->tdc.all_tables.remove(table);
+  tc_remove_table(table);
   mysql_rwlock_rdlock(&LOCK_flush);
   mysql_mutex_unlock(&LOCK_open);
   intern_close_table(table);
@@ -743,7 +726,6 @@ TABLE_SHARE *tdc_acquire_share(THD *thd,
     if ((*out_table= tc_acquire_table(thd, share)))
     {
       mysql_rwlock_unlock(&LOCK_tdc);
-      check_unused(thd);
       DBUG_ASSERT(!(flags & GTS_NOLOCK));
       DBUG_ASSERT(!share->error);
       DBUG_ASSERT(!share->is_view);
@@ -988,8 +970,7 @@ bool tdc_remove_table(THD *thd, enum_tdc
 
     while ((table= share->tdc.free_tables.pop_front()))
     {
-      share->tdc.all_tables.remove(table);
-      tc_count--;
+      tc_remove_table(table);
       purge_tables.push_front(table);
     }
     mysql_rwlock_rdlock(&LOCK_flush);
@@ -999,7 +980,6 @@ bool tdc_remove_table(THD *thd, enum_tdc
       intern_close_table(table);
     mysql_rwlock_unlock(&LOCK_flush);
 
-    check_unused(thd);
     DBUG_ASSERT(share->tdc.all_tables.is_empty() || remove_type != TDC_RT_REMOVE_ALL);
     tdc_release_share(share);
 



More information about the commits mailing list