Commit 374ea106 authored by unknown's avatar unknown
Browse files

Fixed a deadlock problem when using LOCK TABLE in one thread and DROP TABLE in another


sql/lock.cc:
  Added functions to handle list of table name locks
sql/mysql_priv.h:
  Added functions to handle list of named locks
sql/sql_rename.cc:
  Use new general table name lock functions
sql/sql_table.cc:
  Require table name locks when doing drop table.
  This fixed a deadlock problem when using LOCK TABLE in one thread and DROP TABLE in another
parent 391bc11a
Loading
Loading
Loading
Loading
+74 −2
Original line number Diff line number Diff line
@@ -416,10 +416,11 @@ int lock_table_name(THD *thd, TABLE_LIST *table_list)
{
  TABLE *table;
  char  key[MAX_DBKEY_LENGTH];
  char *db= table_list->db ? table_list->db : (thd->db ? thd->db : (char*) "");
  uint  key_length;
  DBUG_ENTER("lock_table_name");

  key_length=(uint) (strmov(strmov(key,table_list->db)+1,table_list->real_name)
  key_length=(uint) (strmov(strmov(key,db)+1,table_list->real_name)
		     -key)+ 1;

  /* Only insert the table if we haven't insert it already */
@@ -447,7 +448,7 @@ int lock_table_name(THD *thd, TABLE_LIST *table_list)
    my_free((gptr) table,MYF(0));
    DBUG_RETURN(-1);
  }
  if (remove_table_from_cache(thd, table_list->db, table_list->real_name))
  if (remove_table_from_cache(thd, db, table_list->real_name))
    DBUG_RETURN(1);					// Table is in use
  DBUG_RETURN(0);
}
@@ -490,6 +491,77 @@ bool wait_for_locked_table_names(THD *thd, TABLE_LIST *table_list)
  DBUG_RETURN(result);
}


/*
  Lock all tables in list with a name lock

  SYNOPSIS
    lock_table_names()
    thd			Thread handle
    table_list		Names of tables to lock

  NOTES
    One must have a lock on LOCK_open when calling this

  RETURN
    0	ok
    1	Fatal error (end of memory ?)
*/

bool lock_table_names(THD *thd, TABLE_LIST *table_list)
{
  bool got_all_locks=1;
  TABLE_LIST *lock_table;

  for (lock_table=table_list ; lock_table ; lock_table=lock_table->next)
  {
    int got_lock;
    if ((got_lock=lock_table_name(thd,lock_table)) < 0)
      goto end;					// Fatal error
    if (got_lock)
      got_all_locks=0;				// Someone is using table
  }

  /* If some table was in use, wait until we got the lock */
  if (!got_all_locks && wait_for_locked_table_names(thd, table_list))
    goto end;
  return 0;

end:
  unlock_table_names(thd, table_list, lock_table);
  return 1;
}


/*
  Unlock all tables in list with a name lock

  SYNOPSIS
    unlock_table_names()
    thd			Thread handle
    table_list		Names of tables to unlock
    last_table		Don't unlock any tables after this one.
			(default 0, which will unlock all tables)

  NOTES
    One must have a lock on LOCK_open when calling this
    This function will send a COND_refresh signal to inform other threads
    that the name locks are removed

  RETURN
    0	ok
    1	Fatal error (end of memory ?)
*/

void unlock_table_names(THD *thd, TABLE_LIST *table_list,
			TABLE_LIST *last_table)
{
  for (TABLE_LIST *table=table_list ; table != last_table ; table=table->next)
    unlock_table_name(thd,table);
  pthread_cond_broadcast(&COND_refresh);
}


static void print_lock_error(int error)
{
  int textno;
+3 −0
Original line number Diff line number Diff line
@@ -594,6 +594,9 @@ MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b);
int lock_table_name(THD *thd, TABLE_LIST *table_list);
void unlock_table_name(THD *thd, TABLE_LIST *table_list);
bool wait_for_locked_table_names(THD *thd, TABLE_LIST *table_list);
bool lock_table_names(THD *thd, TABLE_LIST *table_list);
void unlock_table_names(THD *thd, TABLE_LIST *table_list,
			TABLE_LIST *last_table= 0);


/* old unireg functions */
+10 −22
Original line number Diff line number Diff line
@@ -31,8 +31,8 @@ static TABLE_LIST *rename_tables(THD *thd, TABLE_LIST *table_list,

bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list)
{
  bool error=1,cerror,got_all_locks=1;
  TABLE_LIST *lock_table,*ren_table=0;
  bool error=1, cerror;
  TABLE_LIST *ren_table= 0;
  DBUG_ENTER("mysql_rename_tables");
  
  /* Avoid problems with a rename on a table that we have locked or
@@ -45,23 +45,11 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list)
  }
      
  VOID(pthread_mutex_lock(&LOCK_open));
  for (lock_table=table_list ; lock_table ; lock_table=lock_table->next)
  {
    int got_lock;
    if ((got_lock=lock_table_name(thd,lock_table)) < 0)
      goto end;
    if (got_lock)
      got_all_locks=0;
  }
  
  if (!got_all_locks && wait_for_locked_table_names(thd,table_list))
    goto end;
  if (lock_table_names(thd, table_list))
    goto err;

  if (!(ren_table=rename_tables(thd,table_list,0)))
  error= 0;
  
end:
  if (ren_table)
  if ((ren_table=rename_tables(thd,table_list,0)))
  {
    /* Rename didn't succeed;  rename back the tables in reverse order */
    TABLE_LIST *prev=0,*table;
@@ -83,7 +71,7 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list)
    table=table->next->next;			// Skipp error table
    /* Revert to old names */
    rename_tables(thd, table, 1);
    /* Note that lock_table == 0 here, so the unlock loop will work */
    error= 1;
  }

  /* Lets hope this doesn't fail as the result will be messy */ 
@@ -103,9 +91,9 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list)
    send_ok(&thd->net);
  }

  for (TABLE_LIST *table=table_list ; table != lock_table ; table=table->next)
    unlock_table_name(thd,table);
  pthread_cond_broadcast(&COND_refresh);
  unlock_table_names(thd,table_list);

err:
  pthread_mutex_unlock(&LOCK_open);
  DBUG_RETURN(error);
}
+7 −4
Original line number Diff line number Diff line
@@ -49,7 +49,7 @@ int mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists)
  char	path[FN_REFLEN];
  String wrong_tables;
  bool some_tables_deleted=0;
  uint error;
  uint error= 1;
  db_type table_type;
  TABLE_LIST *table;
  DBUG_ENTER("mysql_rm_table");
@@ -66,7 +66,6 @@ int mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists)
    {
      my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE,MYF(0),
	       tables->real_name);
      error = 1;
      goto err;
    }
    while (global_read_lock && ! thd->killed)
@@ -76,9 +75,12 @@ int mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists)

  }
  
  if (lock_table_names(thd, tables))
    goto err;

  for (table=tables ; table ; table=table->next)
  {
    char *db=table->db ? table->db : thd->db;
    char *db=table->db ? table->db : (thd->db ? thd->db : (char*) "");
    if (!close_temporary_table(thd, db, table->real_name))
    {
      some_tables_deleted=1;			// Log query
@@ -149,9 +151,10 @@ int mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists)
  }
  
  error = 0;
  unlock_table_names(thd, tables);

 err:  
  pthread_mutex_unlock(&LOCK_open);
  VOID(pthread_cond_broadcast(&COND_refresh)); // Signal to refresh

  pthread_mutex_lock(&thd->mysys_var->mutex);
  thd->mysys_var->current_mutex= 0;