Commit 41dc2fc1 authored by Sergey Vojtovich's avatar Sergey Vojtovich

Move table cache private functions out of header

This is mostly needed to hide all references to free_tables, so that further
implementation of multi-instance list can be done completely inside
table_cache.cc
parent f7048e9a
...@@ -83,14 +83,14 @@ static int32 tc_count; /**< Number of TABLE objects in table cache. */ ...@@ -83,14 +83,14 @@ static int32 tc_count; /**< Number of TABLE objects in table cache. */
static mysql_mutex_t LOCK_unused_shares; static mysql_mutex_t LOCK_unused_shares;
#ifdef HAVE_PSI_INTERFACE #ifdef HAVE_PSI_INTERFACE
PSI_mutex_key key_LOCK_unused_shares, key_TABLE_SHARE_LOCK_table_share; static PSI_mutex_key key_LOCK_unused_shares, key_TABLE_SHARE_LOCK_table_share;
static PSI_mutex_info all_tc_mutexes[]= static PSI_mutex_info all_tc_mutexes[]=
{ {
{ &key_LOCK_unused_shares, "LOCK_unused_shares", PSI_FLAG_GLOBAL }, { &key_LOCK_unused_shares, "LOCK_unused_shares", PSI_FLAG_GLOBAL },
{ &key_TABLE_SHARE_LOCK_table_share, "TABLE_SHARE::tdc.LOCK_table_share", 0 } { &key_TABLE_SHARE_LOCK_table_share, "TABLE_SHARE::tdc.LOCK_table_share", 0 }
}; };
PSI_cond_key key_TABLE_SHARE_COND_release; static PSI_cond_key key_TABLE_SHARE_COND_release;
static PSI_cond_info all_tc_conds[]= static PSI_cond_info all_tc_conds[]=
{ {
{ &key_TABLE_SHARE_COND_release, "TABLE_SHARE::tdc.COND_release", 0 } { &key_TABLE_SHARE_COND_release, "TABLE_SHARE::tdc.COND_release", 0 }
...@@ -138,6 +138,19 @@ uint tc_records(void) ...@@ -138,6 +138,19 @@ uint tc_records(void)
} }
/**
Wait for MDL deadlock detector to complete traversing tdc.all_tables.
Must be called before updating TABLE_SHARE::tdc.all_tables.
*/
static void tc_wait_for_mdl_deadlock_detector(TDC_element *element)
{
while (element->all_tables_refs)
mysql_cond_wait(&element->COND_release, &element->LOCK_table_share);
}
/** /**
Remove TABLE object from table cache. Remove TABLE object from table cache.
...@@ -153,12 +166,12 @@ static void tc_remove_table(TABLE *table) ...@@ -153,12 +166,12 @@ static void tc_remove_table(TABLE *table)
static void tc_remove_all_unused_tables(TDC_element *element, static void tc_remove_all_unused_tables(TDC_element *element,
TABLE_list *purge_tables, TDC_element::TABLE_list *purge_tables,
bool mark_flushed) bool mark_flushed)
{ {
TABLE *table; TABLE *table;
element->wait_for_mdl_deadlock_detector(); tc_wait_for_mdl_deadlock_detector(element);
/* /*
Mark share flushed in order to ensure that it gets Mark share flushed in order to ensure that it gets
automatically deleted once it is no longer referenced. automatically deleted once it is no longer referenced.
...@@ -220,6 +233,20 @@ void tc_purge(bool mark_flushed) ...@@ -220,6 +233,20 @@ void tc_purge(bool mark_flushed)
} }
/**
Get last element of free_tables.
*/
static TABLE *tc_free_tables_back(TDC_element *element)
{
TDC_element::TABLE_list::Iterator it(element->free_tables);
TABLE *entry, *last= 0;
while ((entry= it++))
last= entry;
return last;
}
/** /**
Add new TABLE object to table cache. Add new TABLE object to table cache.
...@@ -244,12 +271,12 @@ struct tc_add_table_arg ...@@ -244,12 +271,12 @@ struct tc_add_table_arg
}; };
my_bool tc_add_table_callback(TDC_element *element, tc_add_table_arg *arg) static my_bool tc_add_table_callback(TDC_element *element, tc_add_table_arg *arg)
{ {
TABLE *table; TABLE *table;
mysql_mutex_lock(&element->LOCK_table_share); mysql_mutex_lock(&element->LOCK_table_share);
if ((table= element->free_tables_back()) && table->tc_time < arg->purge_time) if ((table= tc_free_tables_back(element)) && table->tc_time < arg->purge_time)
{ {
memcpy(arg->key, element->m_key, element->m_key_length); memcpy(arg->key, element->m_key, element->m_key_length);
arg->key_length= element->m_key_length; arg->key_length= element->m_key_length;
...@@ -265,7 +292,7 @@ void tc_add_table(THD *thd, TABLE *table) ...@@ -265,7 +292,7 @@ void tc_add_table(THD *thd, TABLE *table)
bool need_purge; bool need_purge;
DBUG_ASSERT(table->in_use == thd); DBUG_ASSERT(table->in_use == thd);
mysql_mutex_lock(&table->s->tdc->LOCK_table_share); mysql_mutex_lock(&table->s->tdc->LOCK_table_share);
table->s->tdc->wait_for_mdl_deadlock_detector(); tc_wait_for_mdl_deadlock_detector(table->s->tdc);
table->s->tdc->all_tables.push_front(table); table->s->tdc->all_tables.push_front(table);
mysql_mutex_unlock(&table->s->tdc->LOCK_table_share); mysql_mutex_unlock(&table->s->tdc->LOCK_table_share);
...@@ -290,14 +317,14 @@ void tc_add_table(THD *thd, TABLE *table) ...@@ -290,14 +317,14 @@ void tc_add_table(THD *thd, TABLE *table)
TABLE *entry; TABLE *entry;
mysql_mutex_lock(&element->LOCK_table_share); mysql_mutex_lock(&element->LOCK_table_share);
lf_hash_search_unpin(thd->tdc_hash_pins); lf_hash_search_unpin(thd->tdc_hash_pins);
element->wait_for_mdl_deadlock_detector(); tc_wait_for_mdl_deadlock_detector(element);
/* /*
It may happen that oldest table was acquired meanwhile. In this case It may happen that oldest table was acquired meanwhile. In this case
just go ahead, number of objects in table cache will normalize just go ahead, number of objects in table cache will normalize
eventually. eventually.
*/ */
if ((entry= element->free_tables_back()) && if ((entry= tc_free_tables_back(element)) &&
entry->tc_time == argument.purge_time) entry->tc_time == argument.purge_time)
{ {
element->free_tables.remove(entry); element->free_tables.remove(entry);
...@@ -313,6 +340,36 @@ void tc_add_table(THD *thd, TABLE *table) ...@@ -313,6 +340,36 @@ void tc_add_table(THD *thd, TABLE *table)
} }
/**
Acquire TABLE object from table cache.
@pre share must be protected against removal.
Acquired object cannot be evicted or acquired again.
@return TABLE object, or NULL if no unused objects.
*/
static TABLE *tc_acquire_table(THD *thd, TDC_element *element)
{
TABLE *table;
mysql_mutex_lock(&element->LOCK_table_share);
table= element->free_tables.pop_front();
if (table)
{
DBUG_ASSERT(!table->in_use);
table->in_use= thd;
/* The ex-unused table must be fully functional. */
DBUG_ASSERT(table->db_stat && table->file);
/* The children must be detached from the table. */
DBUG_ASSERT(!table->file->extra(HA_EXTRA_IS_ATTACHED_CHILDREN));
}
mysql_mutex_unlock(&element->LOCK_table_share);
return table;
}
/** /**
Release TABLE object to table cache. Release TABLE object to table cache.
...@@ -368,7 +425,7 @@ bool tc_release_table(TABLE *table) ...@@ -368,7 +425,7 @@ bool tc_release_table(TABLE *table)
return false; return false;
purge: purge:
table->s->tdc->wait_for_mdl_deadlock_detector(); tc_wait_for_mdl_deadlock_detector(table->s->tdc);
tc_remove_table(table); tc_remove_table(table);
mysql_mutex_unlock(&table->s->tdc->LOCK_table_share); mysql_mutex_unlock(&table->s->tdc->LOCK_table_share);
table->in_use= 0; table->in_use= 0;
...@@ -377,6 +434,19 @@ bool tc_release_table(TABLE *table) ...@@ -377,6 +434,19 @@ bool tc_release_table(TABLE *table)
} }
static void tdc_assert_clean_share(TDC_element *element)
{
DBUG_ASSERT(element->share == 0);
DBUG_ASSERT(element->ref_count == 0);
DBUG_ASSERT(element->m_flush_tickets.is_empty());
DBUG_ASSERT(element->all_tables.is_empty());
DBUG_ASSERT(element->free_tables.is_empty());
DBUG_ASSERT(element->all_tables_refs == 0);
DBUG_ASSERT(element->next == 0);
DBUG_ASSERT(element->prev == 0);
}
/** /**
Delete share from hash and free share object. Delete share from hash and free share object.
*/ */
...@@ -419,7 +489,7 @@ static void tdc_delete_share_from_hash(TDC_element *element) ...@@ -419,7 +489,7 @@ static void tdc_delete_share_from_hash(TDC_element *element)
pins= lf_hash_get_pins(&tdc_hash); pins= lf_hash_get_pins(&tdc_hash);
DBUG_ASSERT(pins); // What can we do about it? DBUG_ASSERT(pins); // What can we do about it?
element->assert_clean_share(); tdc_assert_clean_share(element);
lf_hash_delete(&tdc_hash, pins, element->m_key, element->m_key_length); lf_hash_delete(&tdc_hash, pins, element->m_key, element->m_key_length);
if (!thd) if (!thd)
lf_hash_put_pins(pins); lf_hash_put_pins(pins);
...@@ -428,6 +498,61 @@ static void tdc_delete_share_from_hash(TDC_element *element) ...@@ -428,6 +498,61 @@ static void tdc_delete_share_from_hash(TDC_element *element)
} }
/**
Prepeare table share for use with table definition cache.
*/
static void lf_alloc_constructor(uchar *arg)
{
TDC_element *element= (TDC_element*) (arg + LF_HASH_OVERHEAD);
DBUG_ENTER("lf_alloc_constructor");
mysql_mutex_init(key_TABLE_SHARE_LOCK_table_share,
&element->LOCK_table_share, MY_MUTEX_INIT_FAST);
mysql_cond_init(key_TABLE_SHARE_COND_release, &element->COND_release, 0);
element->m_flush_tickets.empty();
element->all_tables.empty();
element->free_tables.empty();
element->all_tables_refs= 0;
element->share= 0;
element->ref_count= 0;
element->next= 0;
element->prev= 0;
DBUG_VOID_RETURN;
}
/**
Release table definition cache specific resources of table share.
*/
static void lf_alloc_destructor(uchar *arg)
{
TDC_element *element= (TDC_element*) (arg + LF_HASH_OVERHEAD);
DBUG_ENTER("lf_alloc_destructor");
tdc_assert_clean_share(element);
mysql_cond_destroy(&element->COND_release);
mysql_mutex_destroy(&element->LOCK_table_share);
DBUG_VOID_RETURN;
}
static void tdc_hash_initializer(LF_HASH *hash __attribute__((unused)),
TDC_element *element, LEX_STRING *key)
{
memcpy(element->m_key, key->str, key->length);
element->m_key_length= key->length;
tdc_assert_clean_share(element);
}
static uchar *tdc_hash_key(const TDC_element *element, size_t *length,
my_bool not_used __attribute__((unused)))
{
*length= element->m_key_length;
return (uchar*) element->m_key;
}
/** /**
Initialize table definition cache. Initialize table definition cache.
*/ */
...@@ -443,11 +568,11 @@ void tdc_init(void) ...@@ -443,11 +568,11 @@ void tdc_init(void)
MY_MUTEX_INIT_FAST); MY_MUTEX_INIT_FAST);
tdc_version= 1L; /* Increments on each reload */ tdc_version= 1L; /* Increments on each reload */
lf_hash_init(&tdc_hash, sizeof(TDC_element), LF_HASH_UNIQUE, 0, 0, lf_hash_init(&tdc_hash, sizeof(TDC_element), LF_HASH_UNIQUE, 0, 0,
(my_hash_get_key) TDC_element::key, (my_hash_get_key) tdc_hash_key,
&my_charset_bin); &my_charset_bin);
tdc_hash.alloc.constructor= TDC_element::lf_alloc_constructor; tdc_hash.alloc.constructor= lf_alloc_constructor;
tdc_hash.alloc.destructor= TDC_element::lf_alloc_destructor; tdc_hash.alloc.destructor= lf_alloc_destructor;
tdc_hash.initializer= (lf_hash_initializer) TDC_element::lf_hash_initializer; tdc_hash.initializer= (lf_hash_initializer) tdc_hash_initializer;
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
...@@ -681,7 +806,7 @@ TABLE_SHARE *tdc_acquire_share(THD *thd, TABLE_LIST *tl, uint flags, ...@@ -681,7 +806,7 @@ TABLE_SHARE *tdc_acquire_share(THD *thd, TABLE_LIST *tl, uint flags,
if (out_table && (flags & GTS_TABLE)) if (out_table && (flags & GTS_TABLE))
{ {
if ((*out_table= element->acquire_table(thd))) if ((*out_table= tc_acquire_table(thd, element)))
{ {
lf_hash_search_unpin(thd->tdc_hash_pins); lf_hash_search_unpin(thd->tdc_hash_pins);
DBUG_ASSERT(!(flags & GTS_NOLOCK)); DBUG_ASSERT(!(flags & GTS_NOLOCK));
......
...@@ -16,14 +16,8 @@ ...@@ -16,14 +16,8 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#ifdef HAVE_PSI_INTERFACE struct TDC_element
extern PSI_mutex_key key_TABLE_SHARE_LOCK_table_share;
extern PSI_cond_key key_TABLE_SHARE_COND_release;
#endif
class TDC_element
{ {
public:
uchar m_key[NAME_LEN + 1 + NAME_LEN + 1]; uchar m_key[NAME_LEN + 1 + NAME_LEN + 1];
uint m_key_length; uint m_key_length;
ulong version; ulong version;
...@@ -53,138 +47,6 @@ class TDC_element ...@@ -53,138 +47,6 @@ class TDC_element
*/ */
All_share_tables_list all_tables; All_share_tables_list all_tables;
TABLE_list free_tables; TABLE_list free_tables;
TDC_element() {}
TDC_element(const char *key_arg, uint key_length) : m_key_length(key_length)
{
memcpy(m_key, key_arg, key_length);
}
void assert_clean_share()
{
DBUG_ASSERT(share == 0);
DBUG_ASSERT(ref_count == 0);
DBUG_ASSERT(m_flush_tickets.is_empty());
DBUG_ASSERT(all_tables.is_empty());
DBUG_ASSERT(free_tables.is_empty());
DBUG_ASSERT(all_tables_refs == 0);
DBUG_ASSERT(next == 0);
DBUG_ASSERT(prev == 0);
}
/**
Acquire TABLE object from table cache.
@pre share must be protected against removal.
Acquired object cannot be evicted or acquired again.
@return TABLE object, or NULL if no unused objects.
*/
TABLE *acquire_table(THD *thd)
{
TABLE *table;
mysql_mutex_lock(&LOCK_table_share);
table= free_tables.pop_front();
if (table)
{
DBUG_ASSERT(!table->in_use);
table->in_use= thd;
/* The ex-unused table must be fully functional. */
DBUG_ASSERT(table->db_stat && table->file);
/* The children must be detached from the table. */
DBUG_ASSERT(!table->file->extra(HA_EXTRA_IS_ATTACHED_CHILDREN));
}
mysql_mutex_unlock(&LOCK_table_share);
return table;
}
/**
Get last element of free_tables.
*/
TABLE *free_tables_back()
{
TABLE_list::Iterator it(free_tables);
TABLE *entry, *last= 0;
while ((entry= it++))
last= entry;
return last;
}
/**
Wait for MDL deadlock detector to complete traversing tdc.all_tables.
Must be called before updating TABLE_SHARE::tdc.all_tables.
*/
void wait_for_mdl_deadlock_detector()
{
while (all_tables_refs)
mysql_cond_wait(&COND_release, &LOCK_table_share);
}
/**
Prepeare table share for use with table definition cache.
*/
static void lf_alloc_constructor(uchar *arg)
{
TDC_element *element= (TDC_element*) (arg + LF_HASH_OVERHEAD);
DBUG_ENTER("lf_alloc_constructor");
mysql_mutex_init(key_TABLE_SHARE_LOCK_table_share,
&element->LOCK_table_share, MY_MUTEX_INIT_FAST);
mysql_cond_init(key_TABLE_SHARE_COND_release, &element->COND_release, 0);
element->m_flush_tickets.empty();
element->all_tables.empty();
element->free_tables.empty();
element->all_tables_refs= 0;
element->share= 0;
element->ref_count= 0;
element->next= 0;
element->prev= 0;
DBUG_VOID_RETURN;
}
/**
Release table definition cache specific resources of table share.
*/
static void lf_alloc_destructor(uchar *arg)
{
TDC_element *element= (TDC_element*) (arg + LF_HASH_OVERHEAD);
DBUG_ENTER("lf_alloc_destructor");
element->assert_clean_share();
mysql_cond_destroy(&element->COND_release);
mysql_mutex_destroy(&element->LOCK_table_share);
DBUG_VOID_RETURN;
}
static void lf_hash_initializer(LF_HASH *hash __attribute__((unused)),
TDC_element *element, LEX_STRING *key)
{
memcpy(element->m_key, key->str, key->length);
element->m_key_length= key->length;
element->assert_clean_share();
}
static uchar *key(const TDC_element *element, size_t *length,
my_bool not_used __attribute__((unused)))
{
*length= element->m_key_length;
return (uchar*) element->m_key;
}
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment