Commit 6dbc48ca authored by Sergey Vojtovich's avatar Sergey Vojtovich

MDEV-7324 - Lock-free hash for table definition cache

parent 8883c54a
......@@ -231,6 +231,9 @@ void lf_hash_init(LF_HASH *hash, uint element_size, uint flags,
void lf_hash_destroy(LF_HASH *hash);
int lf_hash_insert(LF_HASH *hash, LF_PINS *pins, const void *data);
void *lf_hash_search(LF_HASH *hash, LF_PINS *pins, const void *key, uint keylen);
void *lf_hash_search_using_hash_value(LF_HASH *hash, LF_PINS *pins,
my_hash_value_type hash_value,
const void *key, uint keylen);
int lf_hash_delete(LF_HASH *hash, LF_PINS *pins, const void *key, uint keylen);
int lf_hash_iterate(LF_HASH *hash, LF_PINS *pins,
my_hash_walk_action action, void *argument);
......
......@@ -2119,9 +2119,9 @@ select Host, Db from mysql.host limit 0;
Host Db
show open tables from mysql;
Database Table In_use Name_locked
mysql user 0 0
mysql general_log 0 0
mysql host 0 0
mysql user 0 0
call proc_1();
show open tables from mysql;
Database Table In_use Name_locked
......@@ -2132,9 +2132,9 @@ select Host, Db from mysql.host limit 0;
Host Db
show open tables from mysql;
Database Table In_use Name_locked
mysql user 0 0
mysql general_log 0 0
mysql host 0 0
mysql user 0 0
call proc_1();
show open tables from mysql;
Database Table In_use Name_locked
......@@ -2145,9 +2145,9 @@ select Host, Db from mysql.host limit 0;
Host Db
show open tables from mysql;
Database Table In_use Name_locked
mysql user 0 0
mysql general_log 0 0
mysql host 0 0
mysql user 0 0
call proc_1();
show open tables from mysql;
Database Table In_use Name_locked
......@@ -2158,9 +2158,9 @@ select Host, Db from mysql.host limit 0;
Host Db
show open tables from mysql;
Database Table In_use Name_locked
mysql user 0 0
mysql general_log 0 0
mysql host 0 0
mysql user 0 0
flush tables;
create function func_1() returns int begin flush tables; return 1; end|
ERROR 0A000: FLUSH is not allowed in stored function or trigger
......@@ -2176,9 +2176,9 @@ select Host, Db from mysql.host limit 0;
Host Db
show open tables from mysql;
Database Table In_use Name_locked
mysql user 0 0
mysql general_log 0 0
mysql host 0 0
mysql user 0 0
prepare abc from "flush tables";
execute abc;
show open tables from mysql;
......@@ -2190,9 +2190,9 @@ select Host, Db from mysql.host limit 0;
Host Db
show open tables from mysql;
Database Table In_use Name_locked
mysql user 0 0
mysql general_log 0 0
mysql host 0 0
mysql user 0 0
execute abc;
show open tables from mysql;
Database Table In_use Name_locked
......@@ -2203,9 +2203,9 @@ select Host, Db from mysql.host limit 0;
Host Db
show open tables from mysql;
Database Table In_use Name_locked
mysql user 0 0
mysql general_log 0 0
mysql host 0 0
mysql user 0 0
execute abc;
show open tables from mysql;
Database Table In_use Name_locked
......@@ -2216,9 +2216,9 @@ select Host, Db from mysql.host limit 0;
Host Db
show open tables from mysql;
Database Table In_use Name_locked
mysql user 0 0
mysql general_log 0 0
mysql host 0 0
mysql user 0 0
flush tables;
deallocate prepare abc;
create procedure proc_1() flush logs;
......
......@@ -259,8 +259,8 @@ create table t1(n int);
insert into t1 values (1);
show open tables;
Database Table In_use Name_locked
test t1 0 0
mysql general_log 0 0
test t1 0 0
drop table t1;
create table t1 (a int not null, b VARCHAR(10), INDEX (b) ) AVG_ROW_LENGTH=10 CHECKSUM=1 COMMENT="test" ENGINE=MYISAM MIN_ROWS=10 MAX_ROWS=100 PACK_KEYS=1 DELAY_KEY_WRITE=1 ROW_FORMAT=fixed;
show create table t1;
......
......@@ -24,11 +24,11 @@ wait/synch/rwlock/sql/LOCK_grant YES YES
wait/synch/rwlock/sql/LOCK_system_variables_hash YES YES
wait/synch/rwlock/sql/LOCK_sys_init_connect YES YES
wait/synch/rwlock/sql/LOCK_sys_init_slave YES YES
wait/synch/rwlock/sql/LOCK_tdc YES YES
wait/synch/rwlock/sql/LOGGER::LOCK_logger YES YES
wait/synch/rwlock/sql/MDL_context::LOCK_waiting_for YES YES
wait/synch/rwlock/sql/MDL_lock::rwlock YES YES
wait/synch/rwlock/sql/Query_cache_query::lock YES YES
wait/synch/rwlock/sql/THR_LOCK_servers YES YES
select * from performance_schema.setup_instruments
where name like 'Wait/Synch/Cond/sql/%'
and name not in (
......
......@@ -5,9 +5,9 @@ WHERE name LIKE 'wait/synch/mutex/%'
truncate table performance_schema.events_statements_summary_by_digest;
flush status;
select NAME from performance_schema.mutex_instances
where NAME = 'wait/synch/mutex/sql/TABLE_SHARE::tdc.LOCK_table_share' GROUP BY NAME;
where NAME = 'wait/synch/mutex/mysys/THR_LOCK::mutex' GROUP BY NAME;
NAME
wait/synch/mutex/sql/TABLE_SHARE::tdc.LOCK_table_share
wait/synch/mutex/mysys/THR_LOCK::mutex
select NAME from performance_schema.rwlock_instances
where NAME = 'wait/synch/rwlock/sql/LOCK_grant';
NAME
......@@ -24,7 +24,7 @@ id b
1 initial value
SET @before_count = (SELECT SUM(TIMER_WAIT)
FROM performance_schema.events_waits_history_long
WHERE (EVENT_NAME = 'wait/synch/mutex/sql/TABLE_SHARE::tdc.LOCK_table_share'));
WHERE (EVENT_NAME = 'wait/synch/mutex/mysys/THR_LOCK::mutex'));
SELECT * FROM t1;
id b
1 initial value
......@@ -37,12 +37,12 @@ id b
8 initial value
SET @after_count = (SELECT SUM(TIMER_WAIT)
FROM performance_schema.events_waits_history_long
WHERE (EVENT_NAME = 'wait/synch/mutex/sql/TABLE_SHARE::tdc.LOCK_table_share'));
WHERE (EVENT_NAME = 'wait/synch/mutex/mysys/THR_LOCK::mutex'));
SELECT IF((@after_count - @before_count) > 0, 'Success', 'Failure') test_fm1_timed;
test_fm1_timed
Success
UPDATE performance_schema.setup_instruments SET enabled = 'NO'
WHERE NAME = 'wait/synch/mutex/sql/TABLE_SHARE::tdc.LOCK_table_share';
WHERE NAME = 'wait/synch/mutex/mysys/THR_LOCK::mutex';
TRUNCATE TABLE performance_schema.events_waits_history_long;
TRUNCATE TABLE performance_schema.events_waits_history;
TRUNCATE TABLE performance_schema.events_waits_current;
......@@ -51,7 +51,7 @@ id b
1 initial value
SET @before_count = (SELECT SUM(TIMER_WAIT)
FROM performance_schema.events_waits_history_long
WHERE (EVENT_NAME = 'wait/synch/mutex/sql/TABLE_SHARE::tdc.LOCK_table_share'));
WHERE (EVENT_NAME = 'wait/synch/mutex/mysys/THR_LOCK::mutex'));
SELECT * FROM t1;
id b
1 initial value
......@@ -64,7 +64,7 @@ id b
8 initial value
SET @after_count = (SELECT SUM(TIMER_WAIT)
FROM performance_schema.events_waits_history_long
WHERE (EVENT_NAME = 'wait/synch/mutex/sql/TABLE_SHARE::tdc.LOCK_table_share'));
WHERE (EVENT_NAME = 'wait/synch/mutex/mysys/THR_LOCK::mutex'));
SELECT IF((COALESCE(@after_count, 0) - COALESCE(@before_count, 0)) = 0, 'Success', 'Failure') test_fm2_timed;
test_fm2_timed
Success
......
......@@ -19,7 +19,7 @@ flush status;
# Make sure objects are instrumented
select NAME from performance_schema.mutex_instances
where NAME = 'wait/synch/mutex/sql/TABLE_SHARE::tdc.LOCK_table_share' GROUP BY NAME;
where NAME = 'wait/synch/mutex/mysys/THR_LOCK::mutex' GROUP BY NAME;
select NAME from performance_schema.rwlock_instances
where NAME = 'wait/synch/rwlock/sql/LOCK_grant';
......@@ -49,18 +49,18 @@ SELECT * FROM t1 WHERE id = 1;
SET @before_count = (SELECT SUM(TIMER_WAIT)
FROM performance_schema.events_waits_history_long
WHERE (EVENT_NAME = 'wait/synch/mutex/sql/TABLE_SHARE::tdc.LOCK_table_share'));
WHERE (EVENT_NAME = 'wait/synch/mutex/mysys/THR_LOCK::mutex'));
SELECT * FROM t1;
SET @after_count = (SELECT SUM(TIMER_WAIT)
FROM performance_schema.events_waits_history_long
WHERE (EVENT_NAME = 'wait/synch/mutex/sql/TABLE_SHARE::tdc.LOCK_table_share'));
WHERE (EVENT_NAME = 'wait/synch/mutex/mysys/THR_LOCK::mutex'));
SELECT IF((@after_count - @before_count) > 0, 'Success', 'Failure') test_fm1_timed;
UPDATE performance_schema.setup_instruments SET enabled = 'NO'
WHERE NAME = 'wait/synch/mutex/sql/TABLE_SHARE::tdc.LOCK_table_share';
WHERE NAME = 'wait/synch/mutex/mysys/THR_LOCK::mutex';
TRUNCATE TABLE performance_schema.events_waits_history_long;
TRUNCATE TABLE performance_schema.events_waits_history;
......@@ -70,13 +70,13 @@ SELECT * FROM t1 WHERE id = 1;
SET @before_count = (SELECT SUM(TIMER_WAIT)
FROM performance_schema.events_waits_history_long
WHERE (EVENT_NAME = 'wait/synch/mutex/sql/TABLE_SHARE::tdc.LOCK_table_share'));
WHERE (EVENT_NAME = 'wait/synch/mutex/mysys/THR_LOCK::mutex'));
SELECT * FROM t1;
SET @after_count = (SELECT SUM(TIMER_WAIT)
FROM performance_schema.events_waits_history_long
WHERE (EVENT_NAME = 'wait/synch/mutex/sql/TABLE_SHARE::tdc.LOCK_table_share'));
WHERE (EVENT_NAME = 'wait/synch/mutex/mysys/THR_LOCK::mutex'));
SELECT IF((COALESCE(@after_count, 0) - COALESCE(@before_count, 0)) = 0, 'Success', 'Failure') test_fm2_timed;
......
......@@ -2224,24 +2224,32 @@ deallocate prepare abc;
create procedure proc_1() flush tables;
flush tables;
--sorted_result
show open tables from mysql;
select Host, User from mysql.user limit 0;
select Host, Db from mysql.host limit 0;
--sorted_result
show open tables from mysql;
call proc_1();
--sorted_result
show open tables from mysql;
select Host, User from mysql.user limit 0;
select Host, Db from mysql.host limit 0;
--sorted_result
show open tables from mysql;
call proc_1();
--sorted_result
show open tables from mysql;
select Host, User from mysql.user limit 0;
select Host, Db from mysql.host limit 0;
--sorted_result
show open tables from mysql;
call proc_1();
--sorted_result
show open tables from mysql;
select Host, User from mysql.user limit 0;
select Host, Db from mysql.host limit 0;
--sorted_result
show open tables from mysql;
flush tables;
delimiter |;
......@@ -2261,24 +2269,31 @@ drop procedure proc_1;
flush tables;
select Host, User from mysql.user limit 0;
select Host, Db from mysql.host limit 0;
--sorted_result
show open tables from mysql;
--enable_ps_protocol
prepare abc from "flush tables";
execute abc;
--sorted_result
show open tables from mysql;
select Host, User from mysql.user limit 0;
select Host, Db from mysql.host limit 0;
--sorted_result
show open tables from mysql;
execute abc;
--sorted_result
show open tables from mysql;
select Host, User from mysql.user limit 0;
select Host, Db from mysql.host limit 0;
--sorted_result
show open tables from mysql;
execute abc;
--sorted_result
show open tables from mysql;
select Host, User from mysql.user limit 0;
select Host, Db from mysql.host limit 0;
--sorted_result
show open tables from mysql;
flush tables;
deallocate prepare abc;
......
......@@ -135,9 +135,11 @@ show create table t1;
drop table t1;
flush tables;
--sorted_result
show open tables;
create table t1(n int);
insert into t1 values (1);
--sorted_result
show open tables;
drop table t1;
......@@ -617,6 +619,7 @@ show databases;
show tables;
show events;
show table status;
--sorted_result
show open tables;
show plugins;
show columns in t1;
......
......@@ -122,7 +122,7 @@ retry:
{
if (unlikely(callback))
{
if (callback(cursor->curr + 1, (void*)key))
if (cur_hashnr & 1 && callback(cursor->curr + 1, (void*)key))
return 1;
}
else if (cur_hashnr >= hashnr)
......@@ -467,12 +467,13 @@ int lf_hash_delete(LF_HASH *hash, LF_PINS *pins, const void *key, uint keylen)
NOTE
see lsearch() for pin usage notes
*/
void *lf_hash_search(LF_HASH *hash, LF_PINS *pins, const void *key, uint keylen)
void *lf_hash_search_using_hash_value(LF_HASH *hash, LF_PINS *pins,
my_hash_value_type hashnr,
const void *key, uint keylen)
{
LF_SLIST * volatile *el, *found;
uint bucket, hashnr= calc_hash(hash, (uchar *)key, keylen);
uint bucket= hashnr % hash->size;
bucket= hashnr % hash->size;
lf_rwlock_by_pins(pins);
el= _lf_dynarray_lvalue(&hash->array, bucket);
if (unlikely(!el))
......@@ -521,6 +522,13 @@ int lf_hash_iterate(LF_HASH *hash, LF_PINS *pins,
return res;
}
void *lf_hash_search(LF_HASH *hash, LF_PINS *pins, const void *key, uint keylen)
{
return lf_hash_search_using_hash_value(hash, pins,
calc_hash(hash, (uchar*) key, keylen),
key, keylen);
}
static const uchar *dummy_key= (uchar*)"";
/*
......
......@@ -5001,12 +5001,12 @@ bool ha_table_exists(THD *thd, const char *db, const char *table_name,
else if (engines_with_discover)
hton= &dummy;
TABLE_SHARE *share= tdc_lock_share(db, table_name);
if (share)
TDC_element *element= tdc_lock_share(thd, db, table_name);
if (element && element != MY_ERRPTR)
{
if (hton)
*hton= share->db_type();
tdc_unlock_share(share);
*hton= element->share->db_type();
tdc_unlock_share(element);
DBUG_RETURN(TRUE);
}
......
......@@ -4789,7 +4789,8 @@ static int init_server_components()
all things are initialized so that unireg_abort() doesn't fail
*/
mdl_init();
if (tdc_init() | hostname_cache_init())
tdc_init();
if (hostname_cache_init())
unireg_abort(1);
query_cache_set_min_res_unit(query_cache_min_res_unit);
......
This diff is collapsed.
......@@ -271,7 +271,7 @@ bool lock_tables(THD *thd, TABLE_LIST *tables, uint counter, uint flags);
int decide_logging_format(THD *thd, TABLE_LIST *tables);
void free_io_cache(TABLE *entry);
void intern_close_table(TABLE *entry);
void kill_delayed_threads_for_table(TABLE_SHARE *share);
void kill_delayed_threads_for_table(TDC_element *element);
void close_thread_table(THD *thd, TABLE **table_ptr);
bool close_temporary_tables(THD *thd);
TABLE_LIST *unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list,
......
......@@ -912,7 +912,8 @@ THD::THD(bool is_wsrep_applier)
#endif /* defined(ENABLED_DEBUG_SYNC) */
wait_for_commit_ptr(0),
main_da(0, false, false),
m_stmt_da(&main_da)
m_stmt_da(&main_da),
tdc_hash_pins(0)
#ifdef WITH_WSREP
,
wsrep_applier(is_wsrep_applier),
......@@ -1701,6 +1702,8 @@ THD::~THD()
free_root(&main_mem_root, MYF(0));
main_da.free_memory();
if (tdc_hash_pins)
lf_hash_put_pins(tdc_hash_pins);
if (status_var.memory_used != 0)
{
DBUG_PRINT("error", ("memory_used: %lld", status_var.memory_used));
......
......@@ -3769,6 +3769,8 @@ public:
(rgi_slave && rgi_have_temporary_tables()));
}
LF_PINS *tdc_hash_pins;
inline ulong wsrep_binlog_format() const
{
return WSREP_FORMAT(variables.binlog_format);
......
......@@ -1134,7 +1134,7 @@ void mysql_ha_flush(THD *thd)
((hash_tables->table->mdl_ticket &&
hash_tables->table->mdl_ticket->has_pending_conflicting_lock()) ||
(!hash_tables->table->s->tmp_table &&
hash_tables->table->s->tdc.flushed)))
hash_tables->table->s->tdc->flushed)))
mysql_ha_close_table(hash_tables);
}
......
......@@ -3048,7 +3048,7 @@ bool Delayed_insert::handle_inserts(void)
THD_STAGE_INFO(&thd, stage_insert);
max_rows= delayed_insert_limit;
if (thd.killed || table->s->tdc.flushed)
if (thd.killed || table->s->tdc->flushed)
{
thd.killed= KILL_SYSTEM_THREAD;
max_rows= ULONG_MAX; // Do as much as possible
......
......@@ -76,35 +76,37 @@ print_where(COND *cond,const char *info, enum_query_type query_type)
/* This is for debugging purposes */
static void print_cached_tables(void)
static my_bool print_cached_tables_callback(TDC_element *element,
void *arg __attribute__((unused)))
{
TABLE_SHARE *share;
TABLE *entry;
TDC_iterator tdc_it;
mysql_mutex_lock(&element->LOCK_table_share);
TDC_element::All_share_tables_list::Iterator it(element->all_tables);
while ((entry= it++))
{
THD *in_use= entry->in_use;
printf("%-14.14s %-32s%6ld%8ld%6d %s\n",
entry->s->db.str, entry->s->table_name.str, element->version,
in_use ? in_use->thread_id : 0,
entry->db_stat ? 1 : 0,
in_use ? lock_descriptions[(int)entry->reginfo.lock_type] :
"Not in use");
}
mysql_mutex_unlock(&element->LOCK_table_share);
return FALSE;
}
static void print_cached_tables(void)
{
compile_time_assert(TL_WRITE_ONLY+1 == array_elements(lock_descriptions));
/* purecov: begin tested */
puts("DB Table Version Thread Open Lock");
tdc_it.init();
while ((share= tdc_it.next()))
{
mysql_mutex_lock(&share->tdc.LOCK_table_share);
TABLE_SHARE::All_share_tables_list::Iterator it(share->tdc.all_tables);
while ((entry= it++))
{
THD *in_use= entry->in_use;
printf("%-14.14s %-32s%6ld%8ld%6d %s\n",
entry->s->db.str, entry->s->table_name.str, entry->s->tdc.version,
in_use ? in_use->thread_id : 0,
entry->db_stat ? 1 : 0,
in_use ? lock_descriptions[(int)entry->reginfo.lock_type] :
"Not in use");
}
mysql_mutex_unlock(&share->tdc.LOCK_table_share);
}
tdc_it.deinit();
tdc_iterate(0, (my_hash_walk_action) print_cached_tables_callback, NULL, true);
printf("\nCurrent refresh version: %ld\n", tdc_refresh_version());
fflush(stdout);
/* purecov: end */
......
......@@ -325,7 +325,7 @@ TABLE_SHARE *alloc_table_share(const char *db, const char *table_name,
&share->LOCK_share, MY_MUTEX_INIT_SLOW);
mysql_mutex_init(key_TABLE_SHARE_LOCK_ha_data,
&share->LOCK_ha_data, MY_MUTEX_INIT_FAST);
tdc_init_share(share);
tdc_assign_new_table_id(share);
}
DBUG_RETURN(share);
}
......@@ -422,7 +422,6 @@ void TABLE_SHARE::destroy()
{
mysql_mutex_destroy(&LOCK_share);
mysql_mutex_destroy(&LOCK_ha_data);
tdc_deinit_share(this);
}
my_hash_free(&name_hash);
......@@ -3866,11 +3865,11 @@ bool TABLE_SHARE::visit_subgraph(Wait_for_flush *wait_for_flush,
because we won't try to acquire tdc.LOCK_table_share while
holding a write-lock on MDL_lock::m_rwlock.
*/
mysql_mutex_lock(&tdc.LOCK_table_share);
tdc.all_tables_refs++;
mysql_mutex_unlock(&tdc.LOCK_table_share);
mysql_mutex_lock(&tdc->LOCK_table_share);
tdc->all_tables_refs++;
mysql_mutex_unlock(&tdc->LOCK_table_share);
All_share_tables_list::Iterator tables_it(tdc.all_tables);
TDC_element::All_share_tables_list::Iterator tables_it(tdc->all_tables);
/*
In case of multiple searches running in parallel, avoid going
......@@ -3888,7 +3887,7 @@ bool TABLE_SHARE::visit_subgraph(Wait_for_flush *wait_for_flush,
while ((table= tables_it++))
{
DBUG_ASSERT(table->in_use && tdc.flushed);
DBUG_ASSERT(table->in_use && tdc->flushed);
if (gvisitor->inspect_edge(&table->in_use->mdl_context))
{
goto end_leave_node;
......@@ -3898,7 +3897,7 @@ bool TABLE_SHARE::visit_subgraph(Wait_for_flush *wait_for_flush,
tables_it.rewind();
while ((table= tables_it++))
{
DBUG_ASSERT(table->in_use && tdc.flushed);
DBUG_ASSERT(table->in_use && tdc->flushed);
if (table->in_use->mdl_context.visit_subgraph(gvisitor))
{
goto end_leave_node;
......@@ -3911,10 +3910,10 @@ end_leave_node:
gvisitor->leave_node(src_ctx);
end:
mysql_mutex_lock(&tdc.LOCK_table_share);
if (!--tdc.all_tables_refs)
mysql_cond_broadcast(&tdc.COND_release);
mysql_mutex_unlock(&tdc.LOCK_table_share);
mysql_mutex_lock(&tdc->LOCK_table_share);
if (!--tdc->all_tables_refs)
mysql_cond_broadcast(&tdc->COND_release);
mysql_mutex_unlock(&tdc->LOCK_table_share);
return result;
}
......@@ -3949,14 +3948,14 @@ bool TABLE_SHARE::wait_for_old_version(THD *thd, struct timespec *abstime,
Wait_for_flush ticket(mdl_context, this, deadlock_weight);
MDL_wait::enum_wait_status wait_status;
mysql_mutex_assert_owner(&tdc.LOCK_table_share);
DBUG_ASSERT(tdc.flushed);
mysql_mutex_assert_owner(&tdc->LOCK_table_share);
DBUG_ASSERT(tdc->flushed);
tdc.m_flush_tickets.push_front(&ticket);
tdc->m_flush_tickets.push_front(&ticket);
mdl_context->m_wait.reset_status();
mysql_mutex_unlock(&tdc.LOCK_table_share);
mysql_mutex_unlock(&tdc->LOCK_table_share);
mdl_context->will_wait_for(&ticket);
......@@ -3967,21 +3966,10 @@ bool TABLE_SHARE::wait_for_old_version(THD *thd, struct timespec *abstime,
mdl_context->done_waiting_for();
mysql_mutex_lock(&tdc.LOCK_table_share);
tdc.m_flush_tickets.remove(&ticket);
if (tdc.m_flush_tickets.is_empty() && tdc.ref_count == 0)
{
/*
If our thread was the last one using the share,
we must destroy it here.
*/
mysql_mutex_unlock(&tdc.LOCK_table_share);
destroy();
}
else
mysql_mutex_unlock(&tdc.LOCK_table_share);
mysql_mutex_lock(&tdc->LOCK_table_share);
tdc->m_flush_tickets.remove(&ticket);
mysql_cond_broadcast(&tdc->COND_release);
mysql_mutex_unlock(&tdc->LOCK_table_share);
/*
......@@ -4027,7 +4015,7 @@ bool TABLE_SHARE::wait_for_old_version(THD *thd, struct timespec *abstime,
void TABLE::init(THD *thd, TABLE_LIST *tl)
{
DBUG_ASSERT(s->tdc.ref_count > 0 || s->tmp_table != NO_TMP_TABLE);
DBUG_ASSERT(s->tmp_table != NO_TMP_TABLE || s->tdc->ref_count > 0);
if (thd->lex->need_correct_ident())
alias_name_used= my_strcasecmp(table_alias_charset,
......
......@@ -47,6 +47,7 @@ class ACL_internal_schema_access;
class ACL_internal_table_access;
class Field;
class Table_statistics;
class TDC_element;
/*
Used to identify NESTED_JOIN structures within a join (applicable only to
......@@ -611,32 +612,7 @@ struct TABLE_SHARE
mysql_mutex_t LOCK_ha_data; /* To protect access to ha_data */
mysql_mutex_t LOCK_share; /* To protect TABLE_SHARE */
typedef I_P_List <TABLE, TABLE_share> TABLE_list;
typedef I_P_List <TABLE, All_share_tables> All_share_tables_list;
struct
{
/**
Protects ref_count, m_flush_tickets, all_tables, free_tables, flushed,
all_tables_refs.
*/
mysql_mutex_t LOCK_table_share;
mysql_cond_t COND_release;
TABLE_SHARE *next, **prev; /* Link to unused shares */
uint ref_count; /* How many TABLE objects uses this */
uint all_tables_refs; /* Number of refs to all_tables */
/**
List of tickets representing threads waiting for the share to be flushed.
*/
Wait_for_flush_list m_flush_tickets;
/*
Doubly-linked (back-linked) lists of used and unused TABLE objects
for this share.
*/
All_share_tables_list all_tables;
TABLE_list free_tables;
ulong version;
bool flushed;
} tdc;
TDC_element *tdc;
LEX_CUSTRING tabledef_version;
......
This diff is collapsed.
......@@ -16,6 +16,165 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
extern PSI_mutex_key key_TABLE_SHARE_LOCK_table_share;
extern PSI_cond_key key_TABLE_SHARE_COND_release;
class TDC_element
{
public:
uchar m_key[NAME_LEN + 1 + NAME_LEN + 1];
uint m_key_length;
ulong version;
bool flushed;
TABLE_SHARE *share;
typedef I_P_List <TABLE, TABLE_share> TABLE_list;
typedef I_P_List <TABLE, All_share_tables> All_share_tables_list;
/**
Protects ref_count, m_flush_tickets, all_tables, free_tables, flushed,
all_tables_refs.
*/
mysql_mutex_t LOCK_table_share;
mysql_cond_t COND_release;
TDC_element *next, **prev; /* Link to unused shares */
uint ref_count; /* How many TABLE objects uses this */
uint all_tables_refs; /* Number of refs to all_tables */
/**
List of tickets representing threads waiting for the share to be flushed.
*/
Wait_for_flush_list m_flush_tickets;
/*
Doubly-linked (back-linked) lists of used and unused TABLE objects
for this share.
*/
All_share_tables_list all_tables;
TABLE_list free_tables;
TDC_element() {}
TDC_element(const char *key, uint key_length) : m_key_length(key_length)
{
memcpy(m_key, key, 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(share->tdc->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 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;
}
};
enum enum_tdc_remove_table_type
{
TDC_RT_REMOVE_ALL,
......@@ -27,15 +186,14 @@ enum enum_tdc_remove_table_type
extern ulong tdc_size;
extern ulong tc_size;
extern int tdc_init(void);
extern void tdc_init(void);
extern void tdc_start_shutdown(void);
extern void tdc_deinit(void);
extern ulong tdc_records(void);
extern void tdc_purge(bool all);
extern void tdc_init_share(TABLE_SHARE *share);
extern void tdc_deinit_share(TABLE_SHARE *share);
extern TABLE_SHARE *tdc_lock_share(const char *db, const char *table_name);
extern void tdc_unlock_share(TABLE_SHARE *share);
extern TDC_element *tdc_lock_share(THD *thd, const char *db,
const char *table_name);
extern void tdc_unlock_share(TDC_element *element);
extern TABLE_SHARE *tdc_acquire_share(THD *thd, const char *db,
const char *table_name,
const char *key, uint key_length,
......@@ -52,6 +210,8 @@ extern int tdc_wait_for_old_version(THD *thd, const char *db,
extern ulong tdc_refresh_version(void);
extern ulong tdc_increment_refresh_version(void);
extern void tdc_assign_new_table_id(TABLE_SHARE *share);
extern int tdc_iterate(THD *thd, my_hash_walk_action action, void *argument,
bool no_dups= false);
extern uint tc_records(void);
extern void tc_purge(bool mark_flushed= false);
......@@ -125,13 +285,3 @@ static inline TABLE_SHARE *tdc_acquire_share_shortlived(THD *thd, TABLE_LIST *tl
return tdc_acquire_share(thd, tl->db, tl->table_name, key, key_length,
tl->mdl_request.key.tc_hash_value(), flags, 0);
}
class TDC_iterator
{
ulong idx;
public:
void init(void);
void deinit(void);
TABLE_SHARE *next(void);
};
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