Commit b58e7956 authored by Konstantin Osipov's avatar Konstantin Osipov

Backport of:

------------------------------------------------------------
revno: 2630.4.13
committer: Dmitry Lenev <dlenev@mysql.com>
branch nick: mysql-6.0-3726-w
timestamp: Wed 2008-05-28 12:07:30 +0400
message:
  WL#3726 "DDL locking for all metadata objects".

  After review fixes in progress.

  Get rid of remove_table_from_cache() function since it was
  doing two things at once -- waiting while no one uses particular
  table (now job of metadata locking) and removing TABLE/TABLE_SHARE
  instances from table definition cache (now job of
  expel_table_from_cache()).



sql/mysql_priv.h:
  Got rid of remove_table_from_cache() function. Now one
          should use exclusive metadata lock for waiting until all
          other connections will stop using particular table and
          use expel_table_from_cache() for removing table instances
          and table share from table definition cache.
          Removed unused mysql_wait_completed_table() function.
sql/sql_base.cc:
  Got rid of remove_table_from_cache() function. Now one
  should use exclusive metadata lock for waiting until all
  other connections will stop using particular table and
  use expel_table_from_cache() for removing table instances
  and table share from table definition cache.
          Removed unused mysql_wait_completed_table() function.
sql/sql_table.cc:
  Get rid of two last places where we use remove_table_from_cache()
  by using wait_while_table_is_used() (which uses metadata locks)
  and close_cached_tables() instead.
parent cd055f0e
......@@ -1542,13 +1542,6 @@ char *generate_partition_syntax(partition_info *part_info,
Alter_info *alter_info);
#endif
/* bits for last argument to remove_table_from_cache() */
#define RTFC_NO_FLAG 0x0000
#define RTFC_OWNED_BY_THD_FLAG 0x0001
#define RTFC_WAIT_OTHER_THREAD_FLAG 0x0002
#define RTFC_CHECK_KILLED_FLAG 0x0004
bool remove_table_from_cache(THD *thd, const char *db, const char *table,
uint flags);
bool notify_thread_having_shared_lock(THD *thd, THD *in_use);
void expel_table_from_cache(THD *leave_thd, const char *db,
const char *table_name);
......@@ -1670,7 +1663,6 @@ extern pthread_mutex_t LOCK_gdl;
bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags);
int abort_and_upgrade_lock(ALTER_PARTITION_PARAM_TYPE *lpt);
void close_open_tables_and_downgrade(ALTER_PARTITION_PARAM_TYPE *lpt);
void mysql_wait_completed_table(ALTER_PARTITION_PARAM_TYPE *lpt, TABLE *my_table);
/* Functions to work with system tables. */
bool open_system_tables_for_read(THD *thd, TABLE_LIST *table_list,
......
......@@ -1482,7 +1482,7 @@ void close_thread_tables(THD *thd,
/*
Note that we need to hold LOCK_open while changing the
open_tables list. Another thread may work on it.
(See: remove_table_from_cache(), mysql_wait_completed_table())
(See: notify_thread_having_shared_lock())
Closing a MERGE child before the parent would be fatal if the
other thread tries to abort the MERGE lock in between.
*/
......@@ -2228,7 +2228,7 @@ void unlink_open_table(THD *thd, TABLE *find, bool unlock)
/*
Note that we need to hold LOCK_open while changing the
open_tables list. Another thread may work on it.
(See: remove_table_from_cache(), mysql_wait_completed_table())
(See: notify_thread_having_shared_lock())
Closing a MERGE child before the parent would be fatal if the
other thread tries to abort the MERGE lock in between.
*/
......@@ -8444,154 +8444,6 @@ void flush_tables()
}
/*
Mark all entries with the table as deleted to force an reopen of the table
The table will be closed (not stored in cache) by the current thread when
close_thread_tables() is called.
PREREQUISITES
Lock on LOCK_open()
RETURN
0 This thread now have exclusive access to this table and no other thread
can access the table until close_thread_tables() is called.
1 Table is in use by another thread
*/
bool remove_table_from_cache(THD *thd, const char *db, const char *table_name,
uint flags)
{
char key[MAX_DBKEY_LENGTH];
uint key_length;
TABLE *table;
TABLE_SHARE *share;
bool result= 0, signalled= 0;
DBUG_ENTER("remove_table_from_cache");
DBUG_PRINT("enter", ("table: '%s'.'%s' flags: %u", db, table_name, flags));
key_length=(uint) (strmov(strmov(key,db)+1,table_name)-key)+1;
for (;;)
{
result= signalled= 0;
if ((share= (TABLE_SHARE*) my_hash_search(&table_def_cache, (uchar*) key,
key_length)))
{
I_P_List_iterator<TABLE, TABLE_share> it(share->free_tables);
share->version= 0;
while ((table= it++))
relink_unused(table);
it.init(share->used_tables);
while ((table= it++))
{
THD *in_use= table->in_use;
DBUG_ASSERT(in_use);
if (in_use != thd)
{
DBUG_PRINT("info", ("Table was in use by other thread"));
/*
Mark that table is going to be deleted from cache. This will
force threads that are in mysql_lock_tables() (but not yet
in thr_multi_lock()) to abort it's locks, close all tables and retry
*/
in_use->some_tables_deleted= 1;
if (table->is_name_opened())
{
DBUG_PRINT("info", ("Found another active instance of the table"));
result=1;
}
/* Kill delayed insert threads */
if ((in_use->system_thread & SYSTEM_THREAD_DELAYED_INSERT) &&
! in_use->killed)
{
in_use->killed= THD::KILL_CONNECTION;
pthread_mutex_lock(&in_use->mysys_var->mutex);
if (in_use->mysys_var->current_cond)
{
pthread_mutex_lock(in_use->mysys_var->current_mutex);
signalled= 1;
pthread_cond_broadcast(in_use->mysys_var->current_cond);
pthread_mutex_unlock(in_use->mysys_var->current_mutex);
}
pthread_mutex_unlock(&in_use->mysys_var->mutex);
}
/*
Now we must abort all tables locks used by this thread
as the thread may be waiting to get a lock for another table.
Note that we need to hold LOCK_open while going through the
list. So that the other thread cannot change it. The other
thread must also hold LOCK_open whenever changing the
open_tables list. Aborting the MERGE lock after a child was
closed and before the parent is closed would be fatal.
*/
for (TABLE *thd_table= in_use->open_tables;
thd_table ;
thd_table= thd_table->next)
{
/* Do not handle locks of MERGE children. */
if (thd_table->db_stat && !thd_table->parent) // If table is open
signalled|= mysql_lock_abort_for_thread(thd, thd_table);
}
}
else
{
DBUG_PRINT("info", ("Table was in use by current thread. db_stat: %u",
table->db_stat));
result= result || (flags & RTFC_OWNED_BY_THD_FLAG);
}
}
while (unused_tables && !unused_tables->s->version)
free_cache_entry(unused_tables);
DBUG_PRINT("info", ("share version: %lu ref_count: %u",
share->version, share->ref_count));
if (share->ref_count == 0)
my_hash_delete(&table_def_cache, (uchar*) share);
}
if (result && (flags & RTFC_WAIT_OTHER_THREAD_FLAG))
{
/*
Signal any thread waiting for tables to be freed to
reopen their tables
*/
broadcast_refresh();
DBUG_PRINT("info", ("Waiting for refresh signal"));
if (!(flags & RTFC_CHECK_KILLED_FLAG) || !thd->killed)
{
dropping_tables++;
if (likely(signalled))
(void) pthread_cond_wait(&COND_refresh, &LOCK_open);
else
{
struct timespec abstime;
/*
It can happen that another thread has opened the
table but has not yet locked any table at all. Since
it can be locked waiting for a table that our thread
has done LOCK TABLE x WRITE on previously, we need to
ensure that the thread actually hears our signal
before we go to sleep. Thus we wait for a short time
and then we retry another loop in the
remove_table_from_cache routine.
*/
set_timespec(abstime, 10);
pthread_cond_timedwait(&COND_refresh, &LOCK_open, &abstime);
}
dropping_tables--;
continue;
}
}
break;
}
DBUG_RETURN(result);
}
/**
A callback to the server internals that is used to address
special cases of the locking protocol.
......@@ -8894,34 +8746,6 @@ int abort_and_upgrade_lock(ALTER_PARTITION_PARAM_TYPE *lpt)
}
/*
SYNOPSIS
close_open_tables_and_downgrade()
RESULT VALUES
NONE
DESCRIPTION
We need to ensure that any thread that has managed to open the table
but not yet encountered our lock on the table is also thrown out to
ensure that no threads see our frm changes premature to the final
version. The intermediate versions are only meant for use after a
crash and later REPAIR TABLE.
We also downgrade locks after the upgrade to WRITE_ONLY
*/
/* purecov: begin deadcode */
void close_open_tables_and_downgrade(ALTER_PARTITION_PARAM_TYPE *lpt)
{
pthread_mutex_lock(&LOCK_open);
remove_table_from_cache(lpt->thd, lpt->db, lpt->table_name,
RTFC_WAIT_OTHER_THREAD_FLAG);
pthread_mutex_unlock(&LOCK_open);
/* If MERGE child, forward lock handling to parent. */
mysql_lock_downgrade_write(lpt->thd, lpt->table->parent ? lpt->table->parent :
lpt->table, lpt->old_lock_type);
}
/* purecov: end */
/*
Tells if two (or more) tables have auto_increment columns and we want to
lock those tables with a write lock.
......@@ -9165,7 +8989,7 @@ void close_performance_schema_table(THD *thd, Open_tables_state *backup)
/*
Note that we need to hold LOCK_open while changing the
open_tables list. Another thread may work on it.
(See: remove_table_from_cache(), mysql_wait_completed_table())
(See: notify_thread_having_shared_lock())
Closing a MERGE child before the parent would be fatal if the
other thread tries to abort the MERGE lock in between.
*/
......
......@@ -4800,19 +4800,13 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
/* Close all instances of the table to allow repair to rename files */
if (lock_type == TL_WRITE && table->table->s->version)
{
DBUG_PRINT("admin", ("removing table from cache"));
pthread_mutex_lock(&LOCK_open);
const char *old_message=thd->enter_cond(&COND_refresh, &LOCK_open,
"Waiting to get writelock");
mysql_lock_abort(thd,table->table, TRUE);
remove_table_from_cache(thd, table->table->s->db.str,
table->table->s->table_name.str,
RTFC_WAIT_OTHER_THREAD_FLAG |
RTFC_CHECK_KILLED_FLAG);
thd->exit_cond(old_message);
DBUG_EXECUTE_IF("wait_in_mysql_admin_table", wait_for_kill_signal(thd););
if (thd->killed)
if (wait_while_table_is_used(thd, table->table,
HA_EXTRA_PREPARE_FOR_RENAME))
goto err;
DBUG_EXECUTE_IF("wait_in_mysql_admin_table",
wait_for_kill_signal(thd);
if (thd->killed)
goto err;);
/* Flush entries in the query cache involving this table. */
query_cache_invalidate3(thd, table->table, 0);
open_for_modify= 0;
......@@ -5064,10 +5058,10 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
table->table->file->info(HA_STATUS_CONST);
else
{
pthread_mutex_lock(&LOCK_open);
remove_table_from_cache(thd, table->table->s->db.str,
table->table->s->table_name.str, RTFC_NO_FLAG);
pthread_mutex_unlock(&LOCK_open);
TABLE_LIST *save_next_global= table->next_global;
table->next_global= 0;
close_cached_tables(thd, table, FALSE, FALSE);
table->next_global= save_next_global;
}
/* May be something modified consequently we have to invalidate cache */
query_cache_invalidate3(thd, table->table, 0);
......@@ -5133,6 +5127,7 @@ bool mysql_repair_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
bool mysql_optimize_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
{
DBUG_ENTER("mysql_optimize_table");
set_all_mdl_upgradable(tables);
DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
"optimize", TL_WRITE, 1,0,0,0,
&handler::ha_optimize, 0));
......
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