summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Vojtovich <svoj@mariadb.org>2014-12-28 19:42:17 +0400
committerSergey Vojtovich <svoj@mariadb.org>2014-12-28 19:46:18 +0400
commit6dbc48ca79e5fdd8d4022b00b862e08a4198155b (patch)
tree1904e477f09dd958af2b6696d4b7c8dbb0a1772d
parent8883c54ac08a555bc7d9b09395f49893ad4d80b5 (diff)
downloadmariadb-git-6dbc48ca79e5fdd8d4022b00b862e08a4198155b.tar.gz
MDEV-7324 - Lock-free hash for table definition cache
-rw-r--r--include/lf.h3
-rw-r--r--mysql-test/r/ps.result16
-rw-r--r--mysql-test/r/show_check.result2
-rw-r--r--mysql-test/suite/perfschema/r/dml_setup_instruments.result2
-rw-r--r--mysql-test/suite/perfschema/r/func_mutex.result14
-rw-r--r--mysql-test/suite/perfschema/t/func_mutex.test12
-rw-r--r--mysql-test/t/ps.test15
-rw-r--r--mysql-test/t/show_check.test3
-rw-r--r--mysys/lf_hash.c16
-rw-r--r--sql/handler.cc8
-rw-r--r--sql/mysqld.cc3
-rw-r--r--sql/sql_base.cc285
-rw-r--r--sql/sql_base.h2
-rw-r--r--sql/sql_class.cc5
-rw-r--r--sql/sql_class.h2
-rw-r--r--sql/sql_handler.cc2
-rw-r--r--sql/sql_insert.cc2
-rw-r--r--sql/sql_test.cc44
-rw-r--r--sql/table.cc52
-rw-r--r--sql/table.h28
-rw-r--r--sql/table_cache.cc944
-rw-r--r--sql/table_cache.h180
22 files changed, 889 insertions, 751 deletions
diff --git a/include/lf.h b/include/lf.h
index 5ca777dd680..b776c152ee5 100644
--- a/include/lf.h
+++ b/include/lf.h
@@ -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);
diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result
index e30fa9a1966..95a9a299bb5 100644
--- a/mysql-test/r/ps.result
+++ b/mysql-test/r/ps.result
@@ -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;
diff --git a/mysql-test/r/show_check.result b/mysql-test/r/show_check.result
index 371b2de1d80..a829cbd2aa3 100644
--- a/mysql-test/r/show_check.result
+++ b/mysql-test/r/show_check.result
@@ -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;
diff --git a/mysql-test/suite/perfschema/r/dml_setup_instruments.result b/mysql-test/suite/perfschema/r/dml_setup_instruments.result
index 08edb1c3b01..ce1cdf1884e 100644
--- a/mysql-test/suite/perfschema/r/dml_setup_instruments.result
+++ b/mysql-test/suite/perfschema/r/dml_setup_instruments.result
@@ -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 (
diff --git a/mysql-test/suite/perfschema/r/func_mutex.result b/mysql-test/suite/perfschema/r/func_mutex.result
index 86967a2a63f..1bd70b16811 100644
--- a/mysql-test/suite/perfschema/r/func_mutex.result
+++ b/mysql-test/suite/perfschema/r/func_mutex.result
@@ -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
diff --git a/mysql-test/suite/perfschema/t/func_mutex.test b/mysql-test/suite/perfschema/t/func_mutex.test
index ca1feb68091..66bcb68accb 100644
--- a/mysql-test/suite/perfschema/t/func_mutex.test
+++ b/mysql-test/suite/perfschema/t/func_mutex.test
@@ -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;
diff --git a/mysql-test/t/ps.test b/mysql-test/t/ps.test
index 491594a3045..84c9874260a 100644
--- a/mysql-test/t/ps.test
+++ b/mysql-test/t/ps.test
@@ -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;
diff --git a/mysql-test/t/show_check.test b/mysql-test/t/show_check.test
index 14d50709921..d2788ae3bad 100644
--- a/mysql-test/t/show_check.test
+++ b/mysql-test/t/show_check.test
@@ -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;
diff --git a/mysys/lf_hash.c b/mysys/lf_hash.c
index 782ebc00f24..53860bea49d 100644
--- a/mysys/lf_hash.c
+++ b/mysys/lf_hash.c
@@ -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*)"";
/*
diff --git a/sql/handler.cc b/sql/handler.cc
index 4bb8e0b4397..c512f2d0eb9 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -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);
}
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 9fd02e70767..9fcb382df70 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -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);
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 88b35f79b93..73a027772fa 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -270,58 +270,75 @@ uint get_table_def_key(const TABLE_LIST *table_list, const char **key)
# Pointer to list of names of open tables.
*/
-OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, const char *wild)
+struct list_open_tables_arg
{
- OPEN_TABLE_LIST **start_list, *open_list;
+ THD *thd;
+ const char *db;
+ const char *wild;
TABLE_LIST table_list;
- TABLE_SHARE *share;
- TDC_iterator tdc_it;
- DBUG_ENTER("list_open_tables");
+ OPEN_TABLE_LIST **start_list, *open_list;
+};
- bzero((char*) &table_list,sizeof(table_list));
- start_list= &open_list;
- open_list=0;
- tdc_it.init();
- while ((share= tdc_it.next()))
- {
- if (db && my_strcasecmp(system_charset_info, db, share->db.str))
- continue;
- if (wild && wild_compare(share->table_name.str, wild, 0))
- continue;
+static my_bool list_open_tables_callback(TDC_element *element,
+ list_open_tables_arg *arg)
+{
+ char *db= (char*) element->m_key;
+ char *table_name= (char*) element->m_key + strlen((char*) element->m_key) + 1;
- /* Check if user has SELECT privilege for any column in the table */
- table_list.db= share->db.str;
- table_list.table_name= share->table_name.str;
- table_list.grant.privilege=0;
+ if (arg->db && my_strcasecmp(system_charset_info, arg->db, db))
+ return FALSE;
+ if (arg->wild && wild_compare(table_name, arg->wild, 0))
+ return FALSE;
- if (check_table_access(thd,SELECT_ACL,&table_list, TRUE, 1, TRUE))
- continue;
+ /* Check if user has SELECT privilege for any column in the table */
+ arg->table_list.db= db;
+ arg->table_list.table_name= table_name;
+ arg->table_list.grant.privilege= 0;
- if (!(*start_list = (OPEN_TABLE_LIST *)
- sql_alloc(sizeof(**start_list)+share->table_cache_key.length)))
- {
- open_list=0; // Out of memory
- break;
- }
- strmov((*start_list)->table=
- strmov(((*start_list)->db= (char*) ((*start_list)+1)),
- share->db.str)+1,
- share->table_name.str);
- (*start_list)->in_use= 0;
- mysql_mutex_lock(&share->tdc.LOCK_table_share);
- TABLE_SHARE::All_share_tables_list::Iterator it(share->tdc.all_tables);
- TABLE *table;
- while ((table= it++))
- if (table->in_use)
- ++(*start_list)->in_use;
- mysql_mutex_unlock(&share->tdc.LOCK_table_share);
- (*start_list)->locked= 0; /* Obsolete. */
- start_list= &(*start_list)->next;
- *start_list=0;
- }
- tdc_it.deinit();
- DBUG_RETURN(open_list);
+ if (check_table_access(arg->thd, SELECT_ACL, &arg->table_list, TRUE, 1, TRUE))
+ return FALSE;
+
+ if (!(*arg->start_list= (OPEN_TABLE_LIST *) arg->thd->alloc(
+ sizeof(**arg->start_list) + element->m_key_length)))
+ return TRUE;
+
+ strmov((*arg->start_list)->table=
+ strmov(((*arg->start_list)->db= (char*) ((*arg->start_list) + 1)),
+ db) + 1, table_name);
+ (*arg->start_list)->in_use= 0;
+
+ mysql_mutex_lock(&element->LOCK_table_share);
+ TDC_element::All_share_tables_list::Iterator it(element->all_tables);
+ TABLE *table;
+ while ((table= it++))
+ if (table->in_use)
+ ++(*arg->start_list)->in_use;
+ mysql_mutex_unlock(&element->LOCK_table_share);
+ (*arg->start_list)->locked= 0; /* Obsolete. */
+ arg->start_list= &(*arg->start_list)->next;
+ *arg->start_list= 0;
+ return FALSE;
+}
+
+
+OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, const char *wild)
+{
+ list_open_tables_arg argument;
+ DBUG_ENTER("list_open_tables");
+
+ argument.thd= thd;
+ argument.db= db;
+ argument.wild= wild;
+ bzero((char*) &argument.table_list, sizeof(argument.table_list));
+ argument.start_list= &argument.open_list;
+ argument.open_list= 0;
+
+ if (tdc_iterate(thd, (my_hash_walk_action) list_open_tables_callback,
+ &argument, true))
+ DBUG_RETURN(0);
+
+ DBUG_RETURN(argument.open_list);
}
/*****************************************************************************
@@ -371,12 +388,12 @@ void free_io_cache(TABLE *table)
@pre Caller should have TABLE_SHARE::tdc.LOCK_table_share mutex.
*/
-void kill_delayed_threads_for_table(TABLE_SHARE *share)
+void kill_delayed_threads_for_table(TDC_element *element)
{
- TABLE_SHARE::All_share_tables_list::Iterator it(share->tdc.all_tables);
+ TDC_element::All_share_tables_list::Iterator it(element->all_tables);
TABLE *tab;
- mysql_mutex_assert_owner(&share->tdc.LOCK_table_share);
+ mysql_mutex_assert_owner(&element->LOCK_table_share);
if (!delayed_insert_threads)
return;
@@ -385,7 +402,7 @@ void kill_delayed_threads_for_table(TABLE_SHARE *share)
{
THD *in_use= tab->in_use;
- DBUG_ASSERT(in_use && tab->s->tdc.flushed);
+ DBUG_ASSERT(in_use && tab->s->tdc->flushed);
if ((in_use->system_thread & SYSTEM_THREAD_DELAYED_INSERT) &&
! in_use->killed)
{
@@ -422,6 +439,30 @@ void kill_delayed_threads_for_table(TABLE_SHARE *share)
lock taken by thread trying to obtain global read lock.
*/
+
+struct close_cached_tables_arg
+{
+ ulong refresh_version;
+ TDC_element *element;
+};
+
+
+static my_bool close_cached_tables_callback(TDC_element *element,
+ close_cached_tables_arg *arg)
+{
+ mysql_mutex_lock(&element->LOCK_table_share);
+ if (element->share && element->flushed &&
+ element->version < arg->refresh_version)
+ {
+ /* wait_for_old_version() will unlock mutex and free share */
+ arg->element= element;
+ return TRUE;
+ }
+ mysql_mutex_unlock(&element->LOCK_table_share);
+ return FALSE;
+}
+
+
bool close_cached_tables(THD *thd, TABLE_LIST *tables,
bool wait_for_refresh, ulong timeout)
{
@@ -517,38 +558,21 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables,
if (!tables)
{
- bool found= true;
+ int r= 0;
+ close_cached_tables_arg argument;
+ argument.refresh_version= refresh_version;
set_timespec(abstime, timeout);
- while (found && !thd->killed)
- {
- TABLE_SHARE *share;
- TDC_iterator tdc_it;
- found= false;
- tdc_it.init();
- while ((share= tdc_it.next()))
- {
- mysql_mutex_lock(&share->tdc.LOCK_table_share);
- if (share->tdc.flushed && share->tdc.version < refresh_version)
- {
- /* wait_for_old_version() will unlock mutex and free share */
- found= true;
- break;
- }
- mysql_mutex_unlock(&share->tdc.LOCK_table_share);
- }
- tdc_it.deinit();
+ while (!thd->killed &&
+ (r= tdc_iterate(thd,
+ (my_hash_walk_action) close_cached_tables_callback,
+ &argument)) == 1 &&
+ !argument.element->share->wait_for_old_version(thd, &abstime,
+ MDL_wait_for_subgraph::DEADLOCK_WEIGHT_DDL))
+ /* no-op */;
- if (found)
- {
- if (share->wait_for_old_version(thd, &abstime,
- MDL_wait_for_subgraph::DEADLOCK_WEIGHT_DDL))
- {
- result= TRUE;
- break;
- }
- }
- }
+ if (r)
+ result= TRUE;
}
else
{
@@ -592,53 +616,72 @@ err_with_reopen:
if specified string is NULL, then any table with a connection string.
*/
-bool close_cached_connection_tables(THD *thd, LEX_STRING *connection)
+struct close_cached_connection_tables_arg
{
- TABLE_LIST tmp, *tables= NULL;
- bool result= FALSE;
- TABLE_SHARE *share;
- TDC_iterator tdc_it;
- DBUG_ENTER("close_cached_connections");
- DBUG_ASSERT(thd);
+ THD *thd;
+ LEX_STRING *connection;
+ TABLE_LIST *tables;
+};
- bzero(&tmp, sizeof(TABLE_LIST));
- tdc_it.init();
- while ((share= tdc_it.next()))
- {
- mysql_mutex_lock(&share->tdc.LOCK_table_share);
- /* Ignore if table is not open or does not have a connect_string */
- if (!share->connect_string.length || !share->tdc.ref_count)
- {
- mysql_mutex_unlock(&share->tdc.LOCK_table_share);
- continue;
- }
- mysql_mutex_unlock(&share->tdc.LOCK_table_share);
+static my_bool close_cached_connection_tables_callback(
+ TDC_element *element, close_cached_connection_tables_arg *arg)
+{
+ TABLE_LIST *tmp;
- /* Compare the connection string */
- if (connection &&
- (connection->length > share->connect_string.length ||
- (connection->length < share->connect_string.length &&
- (share->connect_string.str[connection->length] != '/' &&
- share->connect_string.str[connection->length] != '\\')) ||
- strncasecmp(connection->str, share->connect_string.str,
- connection->length)))
- continue;
+ mysql_mutex_lock(&element->LOCK_table_share);
+ /* Ignore if table is not open or does not have a connect_string */
+ if (!element->share || !element->share->connect_string.length ||
+ !element->ref_count)
+ {
+ mysql_mutex_unlock(&element->LOCK_table_share);
+ return FALSE;
+ }
+
+ /* Compare the connection string */
+ if (arg->connection &&
+ (arg->connection->length > element->share->connect_string.length ||
+ (arg->connection->length < element->share->connect_string.length &&
+ (element->share->connect_string.str[arg->connection->length] != '/' &&
+ element->share->connect_string.str[arg->connection->length] != '\\')) ||
+ strncasecmp(arg->connection->str, element->share->connect_string.str,
+ arg->connection->length)))
+ return FALSE;
+
+ /* close_cached_tables() only uses these elements */
+ if (!(tmp= (TABLE_LIST*) alloc_root(arg->thd->mem_root, sizeof(TABLE_LIST))) ||
+ !(tmp->db= strdup_root(arg->thd->mem_root, element->share->db.str)) ||
+ !(tmp->table_name= strdup_root(arg->thd->mem_root,
+ element->share->table_name.str)))
+ return TRUE;
- /* close_cached_tables() only uses these elements */
- tmp.db= share->db.str;
- tmp.table_name= share->table_name.str;
- tmp.next_local= tables;
+ tmp->next_local= arg->tables;
+ arg->tables= tmp;
- tables= (TABLE_LIST *) memdup_root(thd->mem_root, (char*)&tmp,
- sizeof(TABLE_LIST));
- }
- tdc_it.deinit();
+ mysql_mutex_unlock(&element->LOCK_table_share);
+
+ return FALSE;
+}
- if (tables)
- result= close_cached_tables(thd, tables, FALSE, LONG_TIMEOUT);
- DBUG_RETURN(result);
+bool close_cached_connection_tables(THD *thd, LEX_STRING *connection)
+{
+ close_cached_connection_tables_arg argument;
+ DBUG_ENTER("close_cached_connections");
+ DBUG_ASSERT(thd);
+
+ argument.thd= thd;
+ argument.connection= connection;
+ argument.tables= NULL;
+
+ if (tdc_iterate(thd,
+ (my_hash_walk_action) close_cached_connection_tables_callback,
+ &argument))
+ DBUG_RETURN(true);
+
+ DBUG_RETURN(argument.tables ?
+ close_cached_tables(thd, argument.tables, FALSE, LONG_TIMEOUT) :
+ false);
}
@@ -1775,7 +1818,7 @@ bool wait_while_table_is_used(THD *thd, TABLE *table,
DBUG_ENTER("wait_while_table_is_used");
DBUG_PRINT("enter", ("table: '%s' share: 0x%lx db_stat: %u version: %lu",
table->s->table_name.str, (ulong) table->s,
- table->db_stat, table->s->tdc.version));
+ table->db_stat, table->s->tdc->version));
if (thd->mdl_context.upgrade_shared_lock(
table->mdl_ticket, MDL_EXCLUSIVE,
@@ -2388,10 +2431,10 @@ retry_share:
if (!(flags & MYSQL_OPEN_IGNORE_FLUSH))
{
- if (share->tdc.flushed)
+ if (share->tdc->flushed)
{
DBUG_PRINT("info", ("Found old share version: %lu current: %lu",
- share->tdc.version, tdc_refresh_version()));
+ share->tdc->version, tdc_refresh_version()));
/*
We already have an MDL lock. But we have encountered an old
version of table in the table definition cache which is possible
@@ -2422,7 +2465,7 @@ retry_share:
goto retry_share;
}
- if (thd->open_tables && thd->open_tables->s->tdc.flushed)
+ if (thd->open_tables && thd->open_tables->s->tdc->flushed)
{
/*
If the version changes while we're opening the tables,
diff --git a/sql/sql_base.h b/sql/sql_base.h
index 0041972f101..211aaee72b2 100644
--- a/sql/sql_base.h
+++ b/sql/sql_base.h
@@ -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,
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 246dab715ab..4fddb2158bc 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -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));
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 65210c8432f..60ed0793bef 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -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);
diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc
index 110bca96530..7dcc6fa0e95 100644
--- a/sql/sql_handler.cc
+++ b/sql/sql_handler.cc
@@ -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);
}
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index 429b51969a8..050e28f98b4 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -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
diff --git a/sql/sql_test.cc b/sql/sql_test.cc
index 8992ff24a1e..cad2bf4ba56 100644
--- a/sql/sql_test.cc
+++ b/sql/sql_test.cc
@@ -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 */
diff --git a/sql/table.cc b/sql/table.cc
index ebe058472ca..d0cffc8e78e 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -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,
diff --git a/sql/table.h b/sql/table.h
index 75c118f7374..3720d2e6b56 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -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;
diff --git a/sql/table_cache.cc b/sql/table_cache.cc
index 9e2246a5846..f12c031f91a 100644
--- a/sql/table_cache.cc
+++ b/sql/table_cache.cc
@@ -49,18 +49,22 @@
*/
#include "my_global.h"
-#include "hash.h"
+#include "lf.h"
#include "table.h"
#include "sql_base.h"
+
/** Configuration. */
ulong tdc_size; /**< Table definition cache threshold for LRU eviction. */
ulong tc_size; /**< Table cache threshold for LRU eviction. */
/** Data collections. */
-static HASH tdc_hash; /**< Collection of TABLE_SHARE objects. */
+static LF_HASH tdc_hash; /**< Collection of TABLE_SHARE objects. */
/** Collection of unused TABLE_SHARE objects. */
-static TABLE_SHARE *oldest_unused_share, end_of_unused_share;
+I_P_List <TDC_element,
+ I_P_List_adapter<TDC_element, &TDC_element::next, &TDC_element::prev>,
+ I_P_List_null_counter,
+ I_P_List_fast_push_back<TDC_element> > unused_shares;
static int64 tdc_version; /* Increments on each reload */
static int64 last_table_id;
@@ -72,32 +76,23 @@ static int32 tc_count; /**< Number of TABLE objects in table cache. */
/**
Protects unused shares list.
- TABLE_SHARE::tdc.prev
- TABLE_SHARE::tdc.next
- oldest_unused_share
- end_of_unused_share
+ TDC_element::prev
+ TDC_element::next
+ unused_shares
*/
static mysql_mutex_t LOCK_unused_shares;
-static mysql_rwlock_t LOCK_tdc; /**< Protects tdc_hash. */
my_atomic_rwlock_t LOCK_tdc_atomics; /**< Protects tdc_version. */
#ifdef HAVE_PSI_INTERFACE
-static PSI_mutex_key key_LOCK_unused_shares, key_TABLE_SHARE_LOCK_table_share;
+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 }
};
-static PSI_rwlock_key key_rwlock_LOCK_tdc;
-static PSI_rwlock_info all_tc_rwlocks[]=
-{
- { &key_rwlock_LOCK_tdc, "LOCK_tdc", PSI_FLAG_GLOBAL }
-};
-
-
-static PSI_cond_key key_TABLE_SHARE_COND_release;
+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 }
@@ -112,15 +107,19 @@ static void init_tc_psi_keys(void)
count= array_elements(all_tc_mutexes);
mysql_mutex_register(category, all_tc_mutexes, count);
- count= array_elements(all_tc_rwlocks);
- mysql_rwlock_register(category, all_tc_rwlocks, count);
-
count= array_elements(all_tc_conds);
mysql_cond_register(category, all_tc_conds, count);
}
#endif
+static int fix_thd_pins(THD *thd)
+{
+ return thd->tdc_hash_pins ? 0 :
+ (thd->tdc_hash_pins= lf_hash_get_pins(&tdc_hash)) == 0;
+}
+
+
/*
Auxiliary routines for manipulating with per-share all/unused lists
and tc_count counter.
@@ -157,34 +156,7 @@ static void tc_remove_table(TABLE *table)
my_atomic_rwlock_wrlock(&LOCK_tdc_atomics);
my_atomic_add32_explicit(&tc_count, -1, MY_MEMORY_ORDER_RELAXED);
my_atomic_rwlock_wrunlock(&LOCK_tdc_atomics);
- table->s->tdc.all_tables.remove(table);
-}
-
-
-/**
- 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(TABLE_SHARE *share)
-{
- while (share->tdc.all_tables_refs)
- mysql_cond_wait(&share->tdc.COND_release, &share->tdc.LOCK_table_share);
-}
-
-
-/**
- Get last element of tdc.free_tables.
-*/
-
-static TABLE *tc_free_tables_back(TABLE_SHARE *share)
-{
- TABLE_SHARE::TABLE_list::Iterator it(share->tdc.free_tables);
- TABLE *entry, *last= 0;
- while ((entry= it++))
- last= entry;
- return last;
+ table->s->tdc->all_tables.remove(table);
}
@@ -203,31 +175,39 @@ static TABLE *tc_free_tables_back(TABLE_SHARE *share)
periodicly flush all not used tables.
*/
-void tc_purge(bool mark_flushed)
+struct tc_purge_arg
+{
+ TDC_element::TABLE_list purge_tables;
+ bool mark_flushed;
+};
+
+
+static my_bool tc_purge_callback(TDC_element *element, tc_purge_arg *arg)
{
- TABLE_SHARE *share;
TABLE *table;
- TDC_iterator tdc_it;
- TABLE_SHARE::TABLE_list purge_tables;
- tdc_it.init();
- while ((share= tdc_it.next()))
+ mysql_mutex_lock(&element->LOCK_table_share);
+ element->wait_for_mdl_deadlock_detector();
+ if (arg->mark_flushed)
+ element->flushed= true;
+ while ((table= element->free_tables.pop_front()))
{
- mysql_mutex_lock(&share->tdc.LOCK_table_share);
- tc_wait_for_mdl_deadlock_detector(share);
-
- if (mark_flushed)
- share->tdc.flushed= true;
- while ((table= share->tdc.free_tables.pop_front()))
- {
- tc_remove_table(table);
- purge_tables.push_front(table);
- }
- mysql_mutex_unlock(&share->tdc.LOCK_table_share);
+ tc_remove_table(table);
+ arg->purge_tables.push_front(table);
}
- tdc_it.deinit();
+ mysql_mutex_unlock(&element->LOCK_table_share);
+ return FALSE;
+}
- while ((table= purge_tables.pop_front()))
+
+void tc_purge(bool mark_flushed)
+{
+ tc_purge_arg argument;
+ TABLE *table;
+
+ argument.mark_flushed= mark_flushed;
+ tdc_iterate(0, (my_hash_walk_action) tc_purge_callback, &argument);
+ while ((table= argument.purge_tables.pop_front()))
intern_close_table(table);
}
@@ -248,14 +228,38 @@ void tc_purge(bool mark_flushed)
- free evicted object
*/
+struct tc_add_table_arg
+{
+ char key[MAX_DBKEY_LENGTH];
+ uint key_length;
+ ulonglong purge_time;
+};
+
+
+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)
+ {
+ memcpy(arg->key, element->m_key, element->m_key_length);
+ arg->key_length= element->m_key_length;
+ arg->purge_time= table->tc_time;
+ }
+ mysql_mutex_unlock(&element->LOCK_table_share);
+ return FALSE;
+}
+
+
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);
- tc_wait_for_mdl_deadlock_detector(table->s);
- table->s->tdc.all_tables.push_front(table);
- mysql_mutex_unlock(&table->s->tdc.LOCK_table_share);
+ mysql_mutex_lock(&table->s->tdc->LOCK_table_share);
+ table->s->tdc->wait_for_mdl_deadlock_detector();
+ table->s->tdc->all_tables.push_front(table);
+ mysql_mutex_unlock(&table->s->tdc->LOCK_table_share);
/* If we have too many TABLE instances around, try to get rid of them */
my_atomic_rwlock_wrlock(&LOCK_tdc_atomics);
@@ -265,89 +269,45 @@ void tc_add_table(THD *thd, TABLE *table)
if (need_purge)
{
- TABLE_SHARE *purge_share= 0;
- TABLE_SHARE *share;
- TABLE *entry;
- ulonglong UNINIT_VAR(purge_time);
- TDC_iterator tdc_it;
-
- tdc_it.init();
- while ((share= tdc_it.next()))
- {
- mysql_mutex_lock(&share->tdc.LOCK_table_share);
- if ((entry= tc_free_tables_back(share)) &&
- (!purge_share || entry->tc_time < purge_time))
- {
- purge_share= share;
- purge_time= entry->tc_time;
- }
- mysql_mutex_unlock(&share->tdc.LOCK_table_share);
- }
+ tc_add_table_arg argument;
+ argument.purge_time= ULONGLONG_MAX;
+ tdc_iterate(thd, (my_hash_walk_action) tc_add_table_callback, &argument);
- if (purge_share)
+ if (argument.purge_time != ULONGLONG_MAX)
{
- mysql_mutex_lock(&purge_share->tdc.LOCK_table_share);
- tc_wait_for_mdl_deadlock_detector(purge_share);
- tdc_it.deinit();
- /*
- 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= tc_free_tables_back(purge_share)) &&
- entry->tc_time == purge_time)
+ TDC_element *element= (TDC_element*) lf_hash_search(&tdc_hash,
+ thd->tdc_hash_pins,
+ argument.key,
+ argument.key_length);
+ if (element)
{
- entry->s->tdc.free_tables.remove(entry);
- tc_remove_table(entry);
- mysql_mutex_unlock(&purge_share->tdc.LOCK_table_share);
- intern_close_table(entry);
+ TABLE *entry;
+ mysql_mutex_lock(&element->LOCK_table_share);
+ lf_hash_search_unpin(thd->tdc_hash_pins);
+ element->wait_for_mdl_deadlock_detector();
+
+ /*
+ 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()) &&
+ entry->tc_time == argument.purge_time)
+ {
+ element->free_tables.remove(entry);
+ tc_remove_table(entry);
+ mysql_mutex_unlock(&element->LOCK_table_share);
+ intern_close_table(entry);
+ }
+ else
+ mysql_mutex_unlock(&element->LOCK_table_share);
}
- else
- mysql_mutex_unlock(&purge_share->tdc.LOCK_table_share);
}
- else
- tdc_it.deinit();
}
}
/**
- Acquire TABLE object from table cache.
-
- @pre share must be protected against removal.
-
- Acquired object cannot be evicted or acquired again.
-
- While locked:
- - pop object from TABLE_SHARE::tdc.free_tables
-
- While unlocked:
- - mark object used by thd
-
- @return TABLE object, or NULL if no unused objects.
-*/
-
-static TABLE *tc_acquire_table(THD *thd, TABLE_SHARE *share)
-{
- TABLE *table;
-
- mysql_mutex_lock(&share->tdc.LOCK_table_share);
- table= share->tdc.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(&share->tdc.LOCK_table_share);
- return table;
-}
-
-
-/**
Release TABLE object to table cache.
@pre object is used by caller.
@@ -380,14 +340,14 @@ bool tc_release_table(TABLE *table)
if (table->needs_reopen() || tc_records() > tc_size)
{
- mysql_mutex_lock(&table->s->tdc.LOCK_table_share);
+ mysql_mutex_lock(&table->s->tdc->LOCK_table_share);
goto purge;
}
table->tc_time= my_interval_timer();
- mysql_mutex_lock(&table->s->tdc.LOCK_table_share);
- if (table->s->tdc.flushed)
+ mysql_mutex_lock(&table->s->tdc->LOCK_table_share);
+ if (table->s->tdc->flushed)
goto purge;
/*
in_use doesn't really need mutex protection, but must be reset after
@@ -397,88 +357,76 @@ bool tc_release_table(TABLE *table)
*/
table->in_use= 0;
/* Add table to the list of unused TABLE objects for this share. */
- table->s->tdc.free_tables.push_front(table);
- mysql_mutex_unlock(&table->s->tdc.LOCK_table_share);
+ table->s->tdc->free_tables.push_front(table);
+ mysql_mutex_unlock(&table->s->tdc->LOCK_table_share);
return false;
purge:
- tc_wait_for_mdl_deadlock_detector(table->s);
+ table->s->tdc->wait_for_mdl_deadlock_detector();
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;
intern_close_table(table);
return true;
}
-extern "C" uchar *tdc_key(const uchar *record, size_t *length,
- my_bool not_used __attribute__((unused)))
-{
- TABLE_SHARE *entry= (TABLE_SHARE*) record;
- *length= entry->table_cache_key.length;
- return (uchar*) entry->table_cache_key.str;
-}
-
-
/**
Delete share from hash and free share object.
-
- @return
- @retval 0 Success
- @retval 1 Share is referenced
*/
-static int tdc_delete_share_from_hash(TABLE_SHARE *share)
+static void tdc_delete_share_from_hash(TDC_element *element)
{
+ THD *thd= current_thd;
+ LF_PINS *pins;
+ TABLE_SHARE *share;
DBUG_ENTER("tdc_delete_share_from_hash");
- mysql_rwlock_wrlock(&LOCK_tdc);
- mysql_mutex_lock(&share->tdc.LOCK_table_share);
- if (--share->tdc.ref_count)
- {
- mysql_cond_broadcast(&share->tdc.COND_release);
- mysql_mutex_unlock(&share->tdc.LOCK_table_share);
- mysql_rwlock_unlock(&LOCK_tdc);
- DBUG_RETURN(1);
- }
- my_hash_delete(&tdc_hash, (uchar*) share);
- /* Notify PFS early, while still locked. */
+
+ mysql_mutex_assert_owner(&element->LOCK_table_share);
+ share= element->share;
+ DBUG_ASSERT(share);
+ element->share= 0;
PSI_CALL_release_table_share(share->m_psi);
share->m_psi= 0;
- mysql_rwlock_unlock(&LOCK_tdc);
- if (share->tdc.m_flush_tickets.is_empty())
- {
- /* No threads are waiting for this share to be flushed, destroy it. */
- mysql_mutex_unlock(&share->tdc.LOCK_table_share);
- free_table_share(share);
- }
- else
+ if (!element->m_flush_tickets.is_empty())
{
- Wait_for_flush_list::Iterator it(share->tdc.m_flush_tickets);
+ Wait_for_flush_list::Iterator it(element->m_flush_tickets);
Wait_for_flush *ticket;
while ((ticket= it++))
(void) ticket->get_ctx()->m_wait.set_status(MDL_wait::GRANTED);
- /*
- If there are threads waiting for this share to be flushed,
- the last one to receive the notification will destroy the
- share. At this point the share is removed from the table
- definition cache, so is OK to proceed here without waiting
- for this thread to do the work.
- */
- mysql_mutex_unlock(&share->tdc.LOCK_table_share);
+
+ do
+ {
+ mysql_cond_wait(&element->COND_release, &element->LOCK_table_share);
+ } while (!element->m_flush_tickets.is_empty());
}
- DBUG_RETURN(0);
+
+ mysql_mutex_unlock(&element->LOCK_table_share);
+
+ if (thd)
+ {
+ fix_thd_pins(thd);
+ pins= thd->tdc_hash_pins;
+ }
+ else
+ pins= lf_hash_get_pins(&tdc_hash);
+
+ DBUG_ASSERT(pins); // What can we do about it?
+ element->assert_clean_share();
+ lf_hash_delete(&tdc_hash, pins, element->m_key, element->m_key_length);
+ if (!thd)
+ lf_hash_put_pins(pins);
+ free_table_share(share);
+ DBUG_VOID_RETURN;
}
/**
Initialize table definition cache.
-
- @retval 0 Success
- @retval !0 Error
*/
-int tdc_init(void)
+void tdc_init(void)
{
DBUG_ENTER("tdc_init");
#ifdef HAVE_PSI_INTERFACE
@@ -487,13 +435,15 @@ int tdc_init(void)
tdc_inited= true;
mysql_mutex_init(key_LOCK_unused_shares, &LOCK_unused_shares,
MY_MUTEX_INIT_FAST);
- mysql_rwlock_init(key_rwlock_LOCK_tdc, &LOCK_tdc);
my_atomic_rwlock_init(&LOCK_tdc_atomics);
- oldest_unused_share= &end_of_unused_share;
- end_of_unused_share.tdc.prev= &oldest_unused_share;
tdc_version= 1L; /* Increments on each reload */
- DBUG_RETURN(my_hash_init(&tdc_hash, &my_charset_bin, tdc_size, 0, 0, tdc_key,
- 0, 0));
+ lf_hash_init(&tdc_hash, sizeof(TDC_element), LF_HASH_UNIQUE, 0, 0,
+ (my_hash_get_key) TDC_element::key,
+ &my_charset_bin);
+ tdc_hash.alloc.constructor= TDC_element::lf_alloc_constructor;
+ tdc_hash.alloc.destructor= TDC_element::lf_alloc_destructor;
+ tdc_hash.element_size= offsetof(TDC_element, version);
+ DBUG_VOID_RETURN;
}
@@ -533,9 +483,8 @@ void tdc_deinit(void)
if (tdc_inited)
{
tdc_inited= false;
- my_hash_free(&tdc_hash);
+ lf_hash_destroy(&tdc_hash);
my_atomic_rwlock_destroy(&LOCK_tdc_atomics);
- mysql_rwlock_destroy(&LOCK_tdc);
mysql_mutex_destroy(&LOCK_unused_shares);
}
DBUG_VOID_RETURN;
@@ -550,12 +499,7 @@ void tdc_deinit(void)
ulong tdc_records(void)
{
- ulong records;
- DBUG_ENTER("tdc_records");
- mysql_rwlock_rdlock(&LOCK_tdc);
- records= tdc_hash.records;
- mysql_rwlock_unlock(&LOCK_tdc);
- DBUG_RETURN(records);
+ return my_atomic_load32_explicit(&tdc_hash.count, MY_MEMORY_ORDER_RELAXED);
}
@@ -564,72 +508,34 @@ void tdc_purge(bool all)
DBUG_ENTER("tdc_purge");
while (all || tdc_records() > tdc_size)
{
- TABLE_SHARE *share;
+ TDC_element *element;
mysql_mutex_lock(&LOCK_unused_shares);
- if (!oldest_unused_share->tdc.next)
+ if (!(element= unused_shares.pop_front()))
{
mysql_mutex_unlock(&LOCK_unused_shares);
break;
}
- share= oldest_unused_share;
- *share->tdc.prev= share->tdc.next;
- share->tdc.next->tdc.prev= share->tdc.prev;
/* Concurrent thread may start using share again, reset prev and next. */
- share->tdc.prev= 0;
- share->tdc.next= 0;
- mysql_mutex_lock(&share->tdc.LOCK_table_share);
- share->tdc.ref_count++;
- mysql_mutex_unlock(&share->tdc.LOCK_table_share);
+ element->prev= 0;
+ element->next= 0;
+ mysql_mutex_lock(&element->LOCK_table_share);
+ if (element->ref_count)
+ {
+ mysql_mutex_unlock(&element->LOCK_table_share);
+ mysql_mutex_unlock(&LOCK_unused_shares);
+ continue;
+ }
mysql_mutex_unlock(&LOCK_unused_shares);
- tdc_delete_share_from_hash(share);
+ tdc_delete_share_from_hash(element);
}
DBUG_VOID_RETURN;
}
/**
- Prepeare table share for use with table definition cache.
-*/
-
-void tdc_init_share(TABLE_SHARE *share)
-{
- DBUG_ENTER("tdc_init_share");
- mysql_mutex_init(key_TABLE_SHARE_LOCK_table_share,
- &share->tdc.LOCK_table_share, MY_MUTEX_INIT_FAST);
- mysql_cond_init(key_TABLE_SHARE_COND_release, &share->tdc.COND_release, 0);
- share->tdc.m_flush_tickets.empty();
- share->tdc.all_tables.empty();
- share->tdc.free_tables.empty();
- tdc_assign_new_table_id(share);
- share->tdc.version= tdc_refresh_version();
- share->tdc.flushed= false;
- share->tdc.all_tables_refs= 0;
- DBUG_VOID_RETURN;
-}
-
-
-/**
- Release table definition cache specific resources of table share.
-*/
-
-void tdc_deinit_share(TABLE_SHARE *share)
-{
- DBUG_ENTER("tdc_deinit_share");
- DBUG_ASSERT(share->tdc.ref_count == 0);
- DBUG_ASSERT(share->tdc.m_flush_tickets.is_empty());
- DBUG_ASSERT(share->tdc.all_tables.is_empty());
- DBUG_ASSERT(share->tdc.free_tables.is_empty());
- DBUG_ASSERT(share->tdc.all_tables_refs == 0);
- mysql_cond_destroy(&share->tdc.COND_release);
- mysql_mutex_destroy(&share->tdc.LOCK_table_share);
- DBUG_VOID_RETURN;
-}
-
-
-/**
Lock table share.
Find table share with given db.table_name in table definition cache. Return
@@ -641,27 +547,35 @@ void tdc_deinit_share(TABLE_SHARE *share)
Caller is expected to unlock table share with tdc_unlock_share().
- @retval 0 Share not found
- @retval !0 Pointer to locked table share
+ @retval 0 Share not found
+ @retval MY_ERRPTR OOM
+ @retval ptr Pointer to locked table share
*/
-TABLE_SHARE *tdc_lock_share(const char *db, const char *table_name)
+TDC_element *tdc_lock_share(THD *thd, const char *db, const char *table_name)
{
+ TDC_element *element;
char key[MAX_DBKEY_LENGTH];
- uint key_length;
DBUG_ENTER("tdc_lock_share");
- key_length= tdc_create_key(key, db, table_name);
+ if (fix_thd_pins(thd))
+ DBUG_RETURN((TDC_element*) MY_ERRPTR);
- mysql_rwlock_rdlock(&LOCK_tdc);
- TABLE_SHARE* share= (TABLE_SHARE*) my_hash_search(&tdc_hash,
- (uchar*) key, key_length);
- if (share && !share->error)
- mysql_mutex_lock(&share->tdc.LOCK_table_share);
- else
- share= 0;
- mysql_rwlock_unlock(&LOCK_tdc);
- DBUG_RETURN(share);
+ element= (TDC_element *) lf_hash_search(&tdc_hash, thd->tdc_hash_pins,
+ (uchar*) key,
+ tdc_create_key(key, db, table_name));
+ if (element)
+ {
+ mysql_mutex_lock(&element->LOCK_table_share);
+ if (!element->share || element->share->error)
+ {
+ mysql_mutex_unlock(&element->LOCK_table_share);
+ element= 0;
+ }
+ lf_hash_search_unpin(thd->tdc_hash_pins);
+ }
+
+ DBUG_RETURN(element);
}
@@ -669,10 +583,10 @@ TABLE_SHARE *tdc_lock_share(const char *db, const char *table_name)
Unlock share locked by tdc_lock_share().
*/
-void tdc_unlock_share(TABLE_SHARE *share)
+void tdc_unlock_share(TDC_element *element)
{
DBUG_ENTER("tdc_unlock_share");
- mysql_mutex_unlock(&share->tdc.LOCK_table_share);
+ mysql_mutex_unlock(&element->LOCK_table_share);
DBUG_VOID_RETURN;
}
@@ -702,60 +616,60 @@ TABLE_SHARE *tdc_acquire_share(THD *thd, const char *db, const char *table_name,
TABLE **out_table)
{
TABLE_SHARE *share;
+ TDC_element *element;
bool was_unused;
DBUG_ENTER("tdc_acquire_share");
- mysql_rwlock_rdlock(&LOCK_tdc);
- share= (TABLE_SHARE*) my_hash_search_using_hash_value(&tdc_hash, hash_value,
- (uchar*) key,
- key_length);
- if (!share)
+ if (fix_thd_pins(thd))
+ DBUG_RETURN(0);
+
+retry:
+ while (!(element= (TDC_element*) lf_hash_search_using_hash_value(&tdc_hash,
+ thd->tdc_hash_pins, hash_value, (uchar*) key, key_length)))
{
- TABLE_SHARE *new_share;
- mysql_rwlock_unlock(&LOCK_tdc);
+ TDC_element tmp(key, key_length);
+ int res= lf_hash_insert(&tdc_hash, thd->tdc_hash_pins, (uchar*) &tmp);
- if (!(new_share= alloc_table_share(db, table_name, key, key_length)))
+ if (res == -1)
DBUG_RETURN(0);
- new_share->error= OPEN_FRM_OPEN_ERROR;
+ else if (res == 1)
+ continue;
- mysql_rwlock_wrlock(&LOCK_tdc);
- share= (TABLE_SHARE*) my_hash_search_using_hash_value(&tdc_hash, hash_value,
- (uchar*) key,
- key_length);
- if (!share)
- {
- bool need_purge;
+ element= (TDC_element*) lf_hash_search_using_hash_value(&tdc_hash,
+ thd->tdc_hash_pins, hash_value, (uchar*) key, key_length);
+ lf_hash_search_unpin(thd->tdc_hash_pins);
+ DBUG_ASSERT(element);
+ element->assert_clean_share();
- share= new_share;
- mysql_mutex_lock(&share->tdc.LOCK_table_share);
- if (my_hash_insert(&tdc_hash, (uchar*) share))
- {
- mysql_mutex_unlock(&share->tdc.LOCK_table_share);
- mysql_rwlock_unlock(&LOCK_tdc);
- free_table_share(share);
- DBUG_RETURN(0);
- }
- need_purge= tdc_hash.records > tdc_size;
- mysql_rwlock_unlock(&LOCK_tdc);
+ if (!(share= alloc_table_share(db, table_name, key, key_length)))
+ {
+ lf_hash_delete(&tdc_hash, thd->tdc_hash_pins, key, key_length);
+ DBUG_RETURN(0);
+ }
- /* note that tdc_acquire_share() *always* uses discovery */
- open_table_def(thd, share, flags | GTS_USE_DISCOVERY);
- share->tdc.ref_count++;
- mysql_mutex_unlock(&share->tdc.LOCK_table_share);
+ /* note that tdc_acquire_share() *always* uses discovery */
+ open_table_def(thd, share, flags | GTS_USE_DISCOVERY);
- if (share->error)
- {
- tdc_delete_share_from_hash(share);
- DBUG_RETURN(0);
- }
- else if (need_purge)
- tdc_purge(false);
- if (out_table)
- *out_table= 0;
- share->m_psi= PSI_CALL_get_table_share(false, share);
- goto end;
+ if (share->error)
+ {
+ free_table_share(share);
+ lf_hash_delete(&tdc_hash, thd->tdc_hash_pins, key, key_length);
+ DBUG_RETURN(0);
}
- free_table_share(new_share);
+
+ mysql_mutex_lock(&element->LOCK_table_share);
+ element->share= share;
+ share->tdc= element;
+ element->ref_count++;
+ element->version= tdc_refresh_version();
+ element->flushed= false;
+ mysql_mutex_unlock(&element->LOCK_table_share);
+
+ tdc_purge(false);
+ if (out_table)
+ *out_table= 0;
+ share->m_psi= PSI_CALL_get_table_share(false, share);
+ goto end;
}
/* cannot force discovery of a cached share */
@@ -763,18 +677,25 @@ TABLE_SHARE *tdc_acquire_share(THD *thd, const char *db, const char *table_name,
if (out_table && (flags & GTS_TABLE))
{
- if ((*out_table= tc_acquire_table(thd, share)))
+ if ((*out_table= element->acquire_table(thd)))
{
- mysql_rwlock_unlock(&LOCK_tdc);
+ lf_hash_search_unpin(thd->tdc_hash_pins);
DBUG_ASSERT(!(flags & GTS_NOLOCK));
- DBUG_ASSERT(!share->error);
- DBUG_ASSERT(!share->is_view);
- DBUG_RETURN(share);
+ DBUG_ASSERT(element->share);
+ DBUG_ASSERT(!element->share->error);
+ DBUG_ASSERT(!element->share->is_view);
+ DBUG_RETURN(element->share);
}
}
- mysql_mutex_lock(&share->tdc.LOCK_table_share);
- mysql_rwlock_unlock(&LOCK_tdc);
+ mysql_mutex_lock(&element->LOCK_table_share);
+ if (!(share= element->share))
+ {
+ mysql_mutex_unlock(&element->LOCK_table_share);
+ lf_hash_search_unpin(thd->tdc_hash_pins);
+ goto retry;
+ }
+ lf_hash_search_unpin(thd->tdc_hash_pins);
/*
We found an existing table definition. Return it if we didn't get
@@ -797,30 +718,29 @@ TABLE_SHARE *tdc_acquire_share(THD *thd, const char *db, const char *table_name,
goto err;
}
- was_unused= !share->tdc.ref_count;
- share->tdc.ref_count++;
- mysql_mutex_unlock(&share->tdc.LOCK_table_share);
+ was_unused= !element->ref_count;
+ element->ref_count++;
+ mysql_mutex_unlock(&element->LOCK_table_share);
if (was_unused)
{
mysql_mutex_lock(&LOCK_unused_shares);
- if (share->tdc.prev)
+ if (element->prev)
{
/*
Share was not used before and it was in the old_unused_share list
Unlink share from this list
*/
DBUG_PRINT("info", ("Unlinking from not used list"));
- *share->tdc.prev= share->tdc.next;
- share->tdc.next->tdc.prev= share->tdc.prev;
- share->tdc.next= 0;
- share->tdc.prev= 0;
+ unused_shares.remove(element);
+ element->next= 0;
+ element->prev= 0;
}
mysql_mutex_unlock(&LOCK_unused_shares);
}
end:
DBUG_PRINT("exit", ("share: 0x%lx ref_count: %u",
- (ulong) share, share->tdc.ref_count));
+ (ulong) share, share->tdc->ref_count));
if (flags & GTS_NOLOCK)
{
tdc_release_share(share);
@@ -836,7 +756,7 @@ end:
DBUG_RETURN(share);
err:
- mysql_mutex_unlock(&share->tdc.LOCK_table_share);
+ mysql_mutex_unlock(&element->LOCK_table_share);
DBUG_RETURN(0);
}
@@ -849,87 +769,47 @@ void tdc_release_share(TABLE_SHARE *share)
{
DBUG_ENTER("tdc_release_share");
- mysql_mutex_lock(&share->tdc.LOCK_table_share);
+ mysql_mutex_lock(&share->tdc->LOCK_table_share);
DBUG_PRINT("enter",
("share: 0x%lx table: %s.%s ref_count: %u version: %lu",
(ulong) share, share->db.str, share->table_name.str,
- share->tdc.ref_count, share->tdc.version));
- DBUG_ASSERT(share->tdc.ref_count);
+ share->tdc->ref_count, share->tdc->version));
+ DBUG_ASSERT(share->tdc->ref_count);
- if (share->tdc.ref_count > 1)
+ if (share->tdc->ref_count > 1)
{
- share->tdc.ref_count--;
+ share->tdc->ref_count--;
if (!share->is_view)
- mysql_cond_broadcast(&share->tdc.COND_release);
- mysql_mutex_unlock(&share->tdc.LOCK_table_share);
+ mysql_cond_broadcast(&share->tdc->COND_release);
+ mysql_mutex_unlock(&share->tdc->LOCK_table_share);
DBUG_VOID_RETURN;
}
- mysql_mutex_unlock(&share->tdc.LOCK_table_share);
+ mysql_mutex_unlock(&share->tdc->LOCK_table_share);
mysql_mutex_lock(&LOCK_unused_shares);
- mysql_mutex_lock(&share->tdc.LOCK_table_share);
- if (share->tdc.flushed)
+ mysql_mutex_lock(&share->tdc->LOCK_table_share);
+ if (--share->tdc->ref_count)
{
- mysql_mutex_unlock(&share->tdc.LOCK_table_share);
+ mysql_mutex_unlock(&share->tdc->LOCK_table_share);
mysql_mutex_unlock(&LOCK_unused_shares);
- tdc_delete_share_from_hash(share);
DBUG_VOID_RETURN;
}
- if (--share->tdc.ref_count)
+ if (share->tdc->flushed || tdc_records() > tdc_size)
{
- mysql_mutex_unlock(&share->tdc.LOCK_table_share);
mysql_mutex_unlock(&LOCK_unused_shares);
+ tdc_delete_share_from_hash(share->tdc);
DBUG_VOID_RETURN;
}
/* Link share last in used_table_share list */
DBUG_PRINT("info", ("moving share to unused list"));
- DBUG_ASSERT(share->tdc.next == 0);
- share->tdc.prev= end_of_unused_share.tdc.prev;
- *end_of_unused_share.tdc.prev= share;
- end_of_unused_share.tdc.prev= &share->tdc.next;
- share->tdc.next= &end_of_unused_share;
- mysql_mutex_unlock(&share->tdc.LOCK_table_share);
+ DBUG_ASSERT(share->tdc->next == 0);
+ unused_shares.push_back(share->tdc);
+ mysql_mutex_unlock(&share->tdc->LOCK_table_share);
mysql_mutex_unlock(&LOCK_unused_shares);
-
- /* Delete the least used share to preserve LRU order. */
- tdc_purge(false);
DBUG_VOID_RETURN;
}
-static TABLE_SHARE *tdc_delete_share(const char *db, const char *table_name)
-{
- TABLE_SHARE *share;
- DBUG_ENTER("tdc_delete_share");
-
- while ((share= tdc_lock_share(db, table_name)))
- {
- share->tdc.ref_count++;
- if (share->tdc.ref_count > 1)
- {
- tdc_unlock_share(share);
- DBUG_RETURN(share);
- }
- tdc_unlock_share(share);
-
- mysql_mutex_lock(&LOCK_unused_shares);
- if (share->tdc.prev)
- {
- *share->tdc.prev= share->tdc.next;
- share->tdc.next->tdc.prev= share->tdc.prev;
- /* Concurrent thread may start using share again, reset prev and next. */
- share->tdc.prev= 0;
- share->tdc.next= 0;
- }
- mysql_mutex_unlock(&LOCK_unused_shares);
-
- if (!tdc_delete_share_from_hash(share))
- break;
- }
- DBUG_RETURN(0);
-}
-
-
/**
Remove all or some (depending on parameter) instances of TABLE and
TABLE_SHARE from the table definition cache.
@@ -973,9 +853,10 @@ bool tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type,
const char *db, const char *table_name,
bool kill_delayed_threads)
{
+ I_P_List <TABLE, TABLE_share> purge_tables;
TABLE *table;
- TABLE_SHARE *share;
- bool found= false;
+ TDC_element *element;
+ uint my_refs= 1;
DBUG_ENTER("tdc_remove_table");
DBUG_PRINT("enter",("name: %s remove_type: %d", table_name, remove_type));
@@ -983,83 +864,102 @@ bool tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type,
thd->mdl_context.is_lock_owner(MDL_key::TABLE, db, table_name,
MDL_EXCLUSIVE));
- if ((share= tdc_delete_share(db, table_name)))
- {
- I_P_List <TABLE, TABLE_share> purge_tables;
- uint my_refs= 1;
- mysql_mutex_lock(&share->tdc.LOCK_table_share);
- tc_wait_for_mdl_deadlock_detector(share);
- /*
- Mark share flushed in order to ensure that it gets
- automatically deleted once it is no longer referenced.
+ mysql_mutex_lock(&LOCK_unused_shares);
+ if (!(element= tdc_lock_share(thd, db, table_name)))
+ {
+ mysql_mutex_unlock(&LOCK_unused_shares);
+ DBUG_ASSERT(remove_type != TDC_RT_REMOVE_NOT_OWN_KEEP_SHARE);
+ DBUG_RETURN(false);
+ }
- Note that code in TABLE_SHARE::wait_for_old_version() assumes that
- marking share flushed is followed by purge of unused table
- shares.
- */
- if (remove_type != TDC_RT_REMOVE_NOT_OWN_KEEP_SHARE)
- share->tdc.flushed= true;
+ DBUG_ASSERT(element != MY_ERRPTR); // What can we do about it?
- while ((table= share->tdc.free_tables.pop_front()))
+ if (!element->ref_count)
+ {
+ if (element->prev)
{
- tc_remove_table(table);
- purge_tables.push_front(table);
+ unused_shares.remove(element);
+ element->prev= 0;
+ element->next= 0;
}
- if (kill_delayed_threads)
- kill_delayed_threads_for_table(share);
+ mysql_mutex_unlock(&LOCK_unused_shares);
- if (remove_type == TDC_RT_REMOVE_NOT_OWN ||
- remove_type == TDC_RT_REMOVE_NOT_OWN_KEEP_SHARE)
- {
- TABLE_SHARE::All_share_tables_list::Iterator it(share->tdc.all_tables);
- while ((table= it++))
- {
- my_refs++;
- DBUG_ASSERT(table->in_use == thd);
- }
- }
- DBUG_ASSERT(share->tdc.all_tables.is_empty() || remove_type != TDC_RT_REMOVE_ALL);
- mysql_mutex_unlock(&share->tdc.LOCK_table_share);
+ tdc_delete_share_from_hash(element);
+ DBUG_RETURN(true);
+ }
+ mysql_mutex_unlock(&LOCK_unused_shares);
- while ((table= purge_tables.pop_front()))
- intern_close_table(table);
+ element->ref_count++;
- if (remove_type != TDC_RT_REMOVE_UNUSED)
+ element->wait_for_mdl_deadlock_detector();
+ /*
+ Mark share flushed in order to ensure that it gets
+ automatically deleted once it is no longer referenced.
+
+ Note that code in TABLE_SHARE::wait_for_old_version() assumes that
+ marking share flushed is followed by purge of unused table
+ shares.
+ */
+ if (remove_type != TDC_RT_REMOVE_NOT_OWN_KEEP_SHARE)
+ element->flushed= true;
+
+ while ((table= element->free_tables.pop_front()))
+ {
+ tc_remove_table(table);
+ purge_tables.push_front(table);
+ }
+ if (kill_delayed_threads)
+ kill_delayed_threads_for_table(element);
+
+ if (remove_type == TDC_RT_REMOVE_NOT_OWN ||
+ remove_type == TDC_RT_REMOVE_NOT_OWN_KEEP_SHARE)
+ {
+ TDC_element::All_share_tables_list::Iterator it(element->all_tables);
+ while ((table= it++))
{
- /*
- Even though current thread holds exclusive metadata lock on this share
- (asserted above), concurrent FLUSH TABLES threads may be in process of
- closing unused table instances belonging to this share. E.g.:
- thr1 (FLUSH TABLES): table= share->tdc.free_tables.pop_front();
- thr1 (FLUSH TABLES): share->tdc.all_tables.remove(table);
- thr2 (ALTER TABLE): tdc_remove_table();
- thr1 (FLUSH TABLES): intern_close_table(table);
-
- Current remove type assumes that all table instances (except for those
- that are owned by current thread) must be closed before
- thd_remove_table() returns. Wait for such tables now.
-
- intern_close_table() decrements ref_count and signals COND_release. When
- ref_count drops down to number of references owned by current thread
- waiting is completed.
-
- Unfortunately TABLE_SHARE::wait_for_old_version() cannot be used here
- because it waits for all table instances, whereas we have to wait only
- for those that are not owned by current thread.
- */
- mysql_mutex_lock(&share->tdc.LOCK_table_share);
- while (share->tdc.ref_count > my_refs)
- mysql_cond_wait(&share->tdc.COND_release, &share->tdc.LOCK_table_share);
- mysql_mutex_unlock(&share->tdc.LOCK_table_share);
+ my_refs++;
+ DBUG_ASSERT(table->in_use == thd);
}
+ }
+ DBUG_ASSERT(element->all_tables.is_empty() || remove_type != TDC_RT_REMOVE_ALL);
+ mysql_mutex_unlock(&element->LOCK_table_share);
- tdc_release_share(share);
+ while ((table= purge_tables.pop_front()))
+ intern_close_table(table);
- found= true;
+ if (remove_type != TDC_RT_REMOVE_UNUSED)
+ {
+ /*
+ Even though current thread holds exclusive metadata lock on this share
+ (asserted above), concurrent FLUSH TABLES threads may be in process of
+ closing unused table instances belonging to this share. E.g.:
+ thr1 (FLUSH TABLES): table= share->tdc.free_tables.pop_front();
+ thr1 (FLUSH TABLES): share->tdc.all_tables.remove(table);
+ thr2 (ALTER TABLE): tdc_remove_table();
+ thr1 (FLUSH TABLES): intern_close_table(table);
+
+ Current remove type assumes that all table instances (except for those
+ that are owned by current thread) must be closed before
+ thd_remove_table() returns. Wait for such tables now.
+
+ intern_close_table() decrements ref_count and signals COND_release. When
+ ref_count drops down to number of references owned by current thread
+ waiting is completed.
+
+ Unfortunately TABLE_SHARE::wait_for_old_version() cannot be used here
+ because it waits for all table instances, whereas we have to wait only
+ for those that are not owned by current thread.
+ */
+ mysql_mutex_lock(&element->LOCK_table_share);
+ while (element->ref_count > my_refs)
+ mysql_cond_wait(&element->COND_release, &element->LOCK_table_share);
+ mysql_mutex_unlock(&element->LOCK_table_share);
}
- DBUG_ASSERT(found || remove_type != TDC_RT_REMOVE_NOT_OWN_KEEP_SHARE);
- DBUG_RETURN(found);
+
+ tdc_release_share(element->share);
+
+ DBUG_RETURN(true);
}
@@ -1081,21 +981,20 @@ int tdc_wait_for_old_version(THD *thd, const char *db, const char *table_name,
ulong wait_timeout, uint deadlock_weight,
ulong refresh_version)
{
- TABLE_SHARE *share;
- int res= FALSE;
+ TDC_element *element;
- if ((share= tdc_lock_share(db, table_name)))
+ if (!(element= tdc_lock_share(thd, db, table_name)))
+ return FALSE;
+ else if (element == MY_ERRPTR)
+ return TRUE;
+ else if (element->flushed && refresh_version > element->version)
{
- if (share->tdc.flushed && refresh_version > share->tdc.version)
- {
- struct timespec abstime;
- set_timespec(abstime, wait_timeout);
- res= share->wait_for_old_version(thd, &abstime, deadlock_weight);
- }
- else
- tdc_unlock_share(share);
+ struct timespec abstime;
+ set_timespec(abstime, wait_timeout);
+ return element->share->wait_for_old_version(thd, &abstime, deadlock_weight);
}
- return res;
+ tdc_unlock_share(element);
+ return FALSE;
}
@@ -1119,50 +1018,95 @@ ulong tdc_increment_refresh_version(void)
/**
- Initialize table definition cache iterator.
+ Iterate table definition cache.
+
+ Object is protected against removal from table definition cache.
+
+ @note Returned TABLE_SHARE is not guaranteed to be fully initialized:
+ tdc_acquire_share() added new share, but didn't open it yet. If caller
+ needs fully initializer share, it must lock table share mutex.
*/
-void TDC_iterator::init(void)
+struct eliminate_duplicates_arg
{
- DBUG_ENTER("TDC_iterator::init");
- idx= 0;
- mysql_rwlock_rdlock(&LOCK_tdc);
- DBUG_VOID_RETURN;
-}
-
+ HASH hash;
+ MEM_ROOT root;
+ my_hash_walk_action action;
+ void *argument;
+};
-/**
- Deinitialize table definition cache iterator.
-*/
-void TDC_iterator::deinit(void)
+static uchar *eliminate_duplicates_get_key(const uchar *element, size_t *length,
+ my_bool not_used __attribute__((unused)))
{
- DBUG_ENTER("TDC_iterator::deinit");
- mysql_rwlock_unlock(&LOCK_tdc);
- DBUG_VOID_RETURN;
+ LEX_STRING *key= (LEX_STRING *) element;
+ *length= key->length;
+ return (uchar *) key->str;
}
-/**
- Get next TABLE_SHARE object from table definition cache.
+static my_bool eliminate_duplicates(TDC_element *element,
+ eliminate_duplicates_arg *arg)
+{
+ LEX_STRING *key= (LEX_STRING *) alloc_root(&arg->root, sizeof(LEX_STRING));
- Object is protected against removal from table definition cache.
+ if (!key || !(key->str= (char*) memdup_root(&arg->root, element->m_key,
+ element->m_key_length)))
+ return TRUE;
+
+ key->length= element->m_key_length;
+
+ if (my_hash_insert(&arg->hash, (uchar *) key))
+ return FALSE;
+
+ return arg->action(element, arg->argument);
+}
- @note Returned TABLE_SHARE is not guaranteed to be fully initialized:
- tdc_acquire_share() added new share, but didn't open it yet. If caller
- needs fully initializer share, it must lock table share mutex.
-*/
-TABLE_SHARE *TDC_iterator::next(void)
+int tdc_iterate(THD *thd, my_hash_walk_action action, void *argument,
+ bool no_dups)
{
- TABLE_SHARE *share= 0;
- DBUG_ENTER("TDC_iterator::next");
- if (idx < tdc_hash.records)
+ eliminate_duplicates_arg no_dups_argument;
+ LF_PINS *pins;
+ myf alloc_flags= 0;
+ uint hash_flags= HASH_UNIQUE;
+ int res;
+
+ if (thd)
{
- share= (TABLE_SHARE*) my_hash_element(&tdc_hash, idx);
- idx++;
+ fix_thd_pins(thd);
+ pins= thd->tdc_hash_pins;
+ alloc_flags= MY_THREAD_SPECIFIC;
+ hash_flags|= HASH_THREAD_SPECIFIC;
}
- DBUG_RETURN(share);
+ else
+ pins= lf_hash_get_pins(&tdc_hash);
+
+ if (!pins)
+ return ER_OUTOFMEMORY;
+
+ if (no_dups)
+ {
+ init_alloc_root(&no_dups_argument.root, 4096, 4096, MYF(alloc_flags));
+ my_hash_init(&no_dups_argument.hash, &my_charset_bin, tdc_records(), 0, 0,
+ eliminate_duplicates_get_key, 0, hash_flags);
+ no_dups_argument.action= action;
+ no_dups_argument.argument= argument;
+ action= (my_hash_walk_action) eliminate_duplicates;
+ argument= &no_dups_argument;
+ }
+
+ res= lf_hash_iterate(&tdc_hash, pins, action, argument);
+
+ if (!thd)
+ lf_hash_put_pins(pins);
+
+ if (no_dups)
+ {
+ my_hash_free(&no_dups_argument.hash);
+ free_root(&no_dups_argument.root, MYF(0));
+ }
+ return res;
}
diff --git a/sql/table_cache.h b/sql/table_cache.h
index ea3822f9f68..b829e4de752 100644
--- a/sql/table_cache.h
+++ b/sql/table_cache.h
@@ -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);
-};