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. */
static mysql_mutex_t LOCK_unused_shares;
#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[]=
{
{ &key_LOCK_unused_shares, "LOCK_unused_shares", PSI_FLAG_GLOBAL },
{ &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[]=
{
{ &key_TABLE_SHARE_COND_release, "TABLE_SHARE::tdc.COND_release", 0 }
......@@ -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.
......@@ -153,12 +166,12 @@ static void tc_remove_table(TABLE *table)
static void tc_remove_all_unused_tables(TDC_element *element,
TABLE_list *purge_tables,
TDC_element::TABLE_list *purge_tables,
bool mark_flushed)
{
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
automatically deleted once it is no longer referenced.
......@@ -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.
......@@ -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;
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);
arg->key_length= element->m_key_length;
......@@ -265,7 +292,7 @@ void tc_add_table(THD *thd, TABLE *table)
bool need_purge;
DBUG_ASSERT(table->in_use == thd);
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);
mysql_mutex_unlock(&table->s->tdc->LOCK_table_share);
......@@ -290,14 +317,14 @@ void tc_add_table(THD *thd, TABLE *table)
TABLE *entry;
mysql_mutex_lock(&element->LOCK_table_share);
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
just go ahead, number of objects in table cache will normalize
eventually.
*/
if ((entry= element->free_tables_back()) &&
if ((entry= tc_free_tables_back(element)) &&
entry->tc_time == argument.purge_time)
{
element->free_tables.remove(entry);
......@@ -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.
......@@ -368,7 +425,7 @@ bool tc_release_table(TABLE *table)
return false;
purge:
table->s->tdc->wait_for_mdl_deadlock_detector();
tc_wait_for_mdl_deadlock_detector(table->s->tdc);
tc_remove_table(table);
mysql_mutex_unlock(&table->s->tdc->LOCK_table_share);
table->in_use= 0;
......@@ -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.
*/
......@@ -419,7 +489,7 @@ static void tdc_delete_share_from_hash(TDC_element *element)
pins= lf_hash_get_pins(&tdc_hash);
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);
if (!thd)
lf_hash_put_pins(pins);
......@@ -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.
*/
......@@ -443,11 +568,11 @@ void tdc_init(void)
MY_MUTEX_INIT_FAST);
tdc_version= 1L; /* Increments on each reload */
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);
tdc_hash.alloc.constructor= TDC_element::lf_alloc_constructor;
tdc_hash.alloc.destructor= TDC_element::lf_alloc_destructor;
tdc_hash.initializer= (lf_hash_initializer) TDC_element::lf_hash_initializer;
tdc_hash.alloc.constructor= lf_alloc_constructor;
tdc_hash.alloc.destructor= lf_alloc_destructor;
tdc_hash.initializer= (lf_hash_initializer) tdc_hash_initializer;
DBUG_VOID_RETURN;
}
......@@ -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= element->acquire_table(thd)))
if ((*out_table= tc_acquire_table(thd, element)))
{
lf_hash_search_unpin(thd->tdc_hash_pins);
DBUG_ASSERT(!(flags & GTS_NOLOCK));
......
......@@ -16,14 +16,8 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#ifdef HAVE_PSI_INTERFACE
extern PSI_mutex_key key_TABLE_SHARE_LOCK_table_share;
extern PSI_cond_key key_TABLE_SHARE_COND_release;
#endif
class TDC_element
struct TDC_element
{
public:
uchar m_key[NAME_LEN + 1 + NAME_LEN + 1];
uint m_key_length;
ulong version;
......@@ -53,138 +47,6 @@ class TDC_element
*/
All_share_tables_list all_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