[Commits] Rev 3916: MDEV-5403 - Reduce usage of LOCK_open: tc_count in lp:maria/10.0

Sergey Vojtovich svoj at mariadb.org
Fri Dec 6 19:48:39 EET 2013


At lp:maria/10.0

------------------------------------------------------------
revno: 3916
revision-id: svoj at mariadb.org-20131206174845-qj4ddp4xdnxt86ff
parent: svoj at mariadb.org-20131205084430-7rt735tbanw0i6uq
committer: Sergey Vojtovich <svoj at mariadb.org>
branch nick: 10.0
timestamp: Fri 2013-12-06 21:48:45 +0400
message:
  MDEV-5403 - Reduce usage of LOCK_open: tc_count
  
  Lock-free tc_count.
=== modified file 'sql/table_cache.cc'
--- a/sql/table_cache.cc	2013-12-05 08:44:30 +0000
+++ b/sql/table_cache.cc	2013-12-06 17:48:45 +0000
@@ -64,11 +64,11 @@ static int64 tdc_version;  /* Increments
 static int64 last_table_id;
 static bool tdc_inited;
 
-static uint tc_count; /**< Number of TABLE objects in table cache. */
+static TDC_atomic32 tc_count; /**< Number of TABLE objects in table cache. */
 
 
 /**
-  Protects tc_count, TABLE_SHARE::tdc.free_tables, TABLE_SHARE::tdc.all_tables,
+  Protects TABLE_SHARE::tdc.free_tables, TABLE_SHARE::tdc.all_tables,
   TABLE::in_use.
 */
 
@@ -122,8 +122,8 @@ static void init_tc_psi_keys(void)
 
 
 /*
-  Auxiliary routines for manipulating with per-share used/unused and
-  global unused lists of TABLE objects and tc_count counter.
+  Auxiliary routines for manipulating with per-share all/unused and
+  and tc_count counter.
   Responsible for preserving invariants between those lists, counter
   and TABLE::in_use member.
   In fact those routines implement sort of implicit table cache as
@@ -133,13 +133,11 @@ static void init_tc_psi_keys(void)
 
 /**
   Get number of TABLE objects (used and unused) in table cache.
-
-  @todo Protect tc_count so it is read atomically.
 */
 
 uint tc_records(void)
 {
-  return tc_count;
+  return tc_count.load();
 }
 
 
@@ -248,44 +246,39 @@ void tc_add_table(THD *thd, TABLE *table
   DBUG_ASSERT(table->in_use == thd);
   mysql_mutex_lock(&LOCK_open);
   table->s->tdc.all_tables.push_front(table);
+  mysql_mutex_unlock(&LOCK_open);
+
   /* If we have too many TABLE instances around, try to get rid of them */
-  if (tc_count == tc_size)
+  if (tc_count++ >= tc_size)
   {
+    TABLE *purge_table= 0;
+    TABLE_SHARE *share;
     TDC_iterator tdc_it;
-    mysql_mutex_unlock(&LOCK_open);
 
     tdc_it.init();
     mysql_mutex_lock(&LOCK_open);
-    if (tc_count == tc_size)
+    while ((share= tdc_it.next()))
     {
-      TABLE *purge_table= 0;
-      TABLE_SHARE *share;
-      while ((share= tdc_it.next()))
-      {
-        TABLE_SHARE::TABLE_list::Iterator it(share->tdc.free_tables);
-        TABLE *entry;
-        while ((entry= it++))
-          if (!purge_table || entry->tc_time < purge_table->tc_time)
-            purge_table= entry;
-      }
-      if (purge_table)
-      {
-        tdc_it.deinit();
-        purge_table->s->tdc.free_tables.remove(purge_table);
-        purge_table->s->tdc.all_tables.remove(purge_table);
-        mysql_rwlock_rdlock(&LOCK_flush);
-        mysql_mutex_unlock(&LOCK_open);
-        intern_close_table(purge_table);
-        mysql_rwlock_unlock(&LOCK_flush);
-        check_unused(thd);
-        return;
-      }
+      TABLE_SHARE::TABLE_list::Iterator it(share->tdc.free_tables);
+      TABLE *entry;
+      while ((entry= it++))
+        if (!purge_table || entry->tc_time < purge_table->tc_time)
+          purge_table= entry;
     }
     tdc_it.deinit();
+
+    if (purge_table)
+    {
+      purge_table->s->tdc.free_tables.remove(purge_table);
+      purge_table->s->tdc.all_tables.remove(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);
+    }
   }
-  /* Nothing to evict, increment tc_count. */
-  tc_count++;
-  mysql_mutex_unlock(&LOCK_open);
 }
 
 
@@ -357,25 +350,33 @@ bool tc_release_table(TABLE *table)
   DBUG_ASSERT(table->in_use);
   DBUG_ASSERT(table->file);
 
+  if (table->needs_reopen() || tc_count.load() > 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() || table->needs_reopen() || tc_count > tc_size)
-  {
-    tc_count--;
-    table->s->tdc.all_tables.remove(table);
-    mysql_rwlock_rdlock(&LOCK_flush);
-    mysql_mutex_unlock(&LOCK_open);
-    intern_close_table(table);
-    mysql_rwlock_unlock(&LOCK_flush);
-    return true;
-  }
+  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);
+  mysql_rwlock_rdlock(&LOCK_flush);
+  mysql_mutex_unlock(&LOCK_open);
+  intern_close_table(table);
+  mysql_rwlock_unlock(&LOCK_flush);
+  return true;
 }
 
 

=== modified file 'sql/table_cache.h'
--- a/sql/table_cache.h	2013-12-05 08:44:30 +0000
+++ b/sql/table_cache.h	2013-12-06 17:48:45 +0000
@@ -24,6 +24,96 @@ enum enum_tdc_remove_table_type
   TDC_RT_REMOVE_NOT_OWN_KEEP_SHARE
 };
 
+
+template <typename T> class TDC_atomic
+{
+  my_atomic_rwlock_t m_rwlock;
+protected:
+  T m_value;
+public:
+  TDC_atomic(T value= 0)
+  {
+    m_value= value;
+    my_atomic_rwlock_init(&m_rwlock);
+  }
+  ~TDC_atomic()
+  {
+    my_atomic_rwlock_destroy(&m_rwlock);
+  }
+  void rdlock() { my_atomic_rwlock_rdlock(&m_rwlock); }
+  void wrlock() { my_atomic_rwlock_wrlock(&m_rwlock); }
+  void rdunlock() { my_atomic_rwlock_rdunlock(&m_rwlock); }
+  void wrunlock() { my_atomic_rwlock_wrunlock(&m_rwlock); }
+
+  virtual T add_unlocked(T value)= 0;
+  virtual bool cas_unlocked(T *old, T value)= 0;
+  virtual T fas_unlocked(T value)= 0;
+  virtual T load_unlocked()= 0;
+  virtual void store_unlocked(T value)= 0;
+
+  T add(T value)
+  {
+    T result;
+    wrlock();
+    result= add_unlocked(value);
+    wrunlock();
+    return result;
+  }
+  bool cas(T *old, T value)
+  {
+    bool result;
+    wrlock();
+    result= cas_unlocked(old, value);
+    wrunlock();
+    return result;
+  }
+  T fas(T value)
+  {
+    T result;
+    wrlock();
+    result= fas_unlocked(value);
+    wrunlock();
+    return result;
+  }
+  T load()
+  {
+    T result;
+    rdlock();
+    result= load_unlocked();
+    rdunlock();
+    return result;
+  }
+  void store(T value)
+  {
+    wrlock();
+    store_unlocked(value);
+    wrunlock();
+  }
+  T operator++(int) { return add(1); }
+  T operator--(int) { return add(-1); }
+};
+
+
+class TDC_atomic32 : public TDC_atomic<uint32>
+{
+  uint32 add_unlocked(uint32 value) { return my_atomic_add32((int32 *) &m_value, value); }
+  bool cas_unlocked(uint32 *old, uint32 value) { return my_atomic_cas32((int32 *) &m_value, (int32 *) old, value); }
+  uint32 fas_unlocked(uint32 value) { return my_atomic_fas32((int32 *) &m_value, value); }
+  uint32 load_unlocked() { return my_atomic_load32((int32 *) &m_value); }
+  void store_unlocked(uint32 value) { my_atomic_store32((int32 *) &m_value, value); }
+};
+
+
+class TDC_atomic64 : public TDC_atomic<uint64>
+{
+  uint64 add_unlocked(uint64 value) { return my_atomic_add64((int64 *) &m_value, value); }
+  bool cas_unlocked(uint64 *old, uint64 value) { return my_atomic_cas64((int64 *) &m_value, (int64 *) old, value); }
+  uint64 fas_unlocked(uint64 value) { return my_atomic_fas64((int64 *) &m_value, value); }
+  uint64 load_unlocked() { return my_atomic_load64((int64 *) &m_value); }
+  void store_unlocked(uint64 value) { my_atomic_store64((int64 *) &m_value, value); }
+};
+
+
 extern ulong tdc_size;
 extern ulong tc_size;
 extern mysql_mutex_t LOCK_open; /* FIXME: make private */



More information about the commits mailing list