Commit 6d29b23b authored by unknown's avatar unknown
Browse files

Bug #10600

remove_table_from_cache fails to signal other thread and gets
blocked when other thread also gets blocked


include/thr_lock.h:
  Report if any threads was signalled
mysys/thr_lock.c:
  Report if any threads was signalled
sql/lock.cc:
  Report if any threads was signalled
  Use new interface for remove_table_from_cache
sql/mysql_priv.h:
  New interface for remove_table_from_cache
  + mysql_lock_abort_for_thread
sql/sql_base.cc:
  Use new interface of remove_table_cache
  Rewrote remove_table_from_cache to fix bug
sql/sql_table.cc:
  Use new interface of remove_table_from_cache
parent 5c27ff06
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -107,7 +107,7 @@ void thr_unlock(THR_LOCK_DATA *data);
int thr_multi_lock(THR_LOCK_DATA **data,uint count);
void thr_multi_unlock(THR_LOCK_DATA **data,uint count);
void thr_abort_locks(THR_LOCK *lock);
void thr_abort_locks_for_thread(THR_LOCK *lock, pthread_t thread);
bool thr_abort_locks_for_thread(THR_LOCK *lock, pthread_t thread);
void thr_print_locks(void);		/* For debugging */
my_bool thr_upgrade_write_delay_lock(THR_LOCK_DATA *data);
my_bool thr_reschedule_write_lock(THR_LOCK_DATA *data);
+5 −2
Original line number Diff line number Diff line
@@ -966,9 +966,10 @@ void thr_abort_locks(THR_LOCK *lock)
  This is used to abort all locks for a specific thread
*/

void thr_abort_locks_for_thread(THR_LOCK *lock, pthread_t thread)
bool thr_abort_locks_for_thread(THR_LOCK *lock, pthread_t thread)
{
  THR_LOCK_DATA *data;
  bool found= FALSE;
  DBUG_ENTER("thr_abort_locks_for_thread");

  pthread_mutex_lock(&lock->mutex);
@@ -978,6 +979,7 @@ void thr_abort_locks_for_thread(THR_LOCK *lock, pthread_t thread)
    {
      DBUG_PRINT("info",("Aborting read-wait lock"));
      data->type= TL_UNLOCK;			/* Mark killed */
      found= TRUE;
      pthread_cond_signal(data->cond);
      data->cond= 0;				/* Removed from list */

@@ -993,6 +995,7 @@ void thr_abort_locks_for_thread(THR_LOCK *lock, pthread_t thread)
    {
      DBUG_PRINT("info",("Aborting write-wait lock"));
      data->type= TL_UNLOCK;
      found= TRUE;
      pthread_cond_signal(data->cond);
      data->cond= 0;

@@ -1003,7 +1006,7 @@ void thr_abort_locks_for_thread(THR_LOCK *lock, pthread_t thread)
    }
  }
  pthread_mutex_unlock(&lock->mutex);
  DBUG_VOID_RETURN;
  DBUG_RETURN(found);
}


+18 −6
Original line number Diff line number Diff line
@@ -333,20 +333,25 @@ void mysql_lock_abort(THD *thd, TABLE *table)

/* Abort one thread / table combination */

void mysql_lock_abort_for_thread(THD *thd, TABLE *table)
bool mysql_lock_abort_for_thread(THD *thd, TABLE *table)
{
  MYSQL_LOCK *locked;
  TABLE *write_lock_used;
  bool result= FALSE;
  DBUG_ENTER("mysql_lock_abort_for_thread");

  if ((locked = get_lock_data(thd,&table,1,1,&write_lock_used)))
  {
    for (uint i=0; i < locked->lock_count; i++)
      thr_abort_locks_for_thread(locked->locks[i]->lock,
    {
      bool found;
      found= thr_abort_locks_for_thread(locked->locks[i]->lock,
	         			 table->in_use->real_id);
      result|= found;
    }
    my_free((gptr) locked,MYF(0));
  }
  DBUG_VOID_RETURN;
  DBUG_RETURN(result);
}


@@ -542,8 +547,15 @@ 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, db, table_list->real_name))
  
  {
    uint flags= 0;
    if (remove_table_from_cache(thd, db,
                                table_list->real_name, flags))
    {
      DBUG_RETURN(1);				// Table is in use
    }
  }
  DBUG_RETURN(0);
}

+5 −2
Original line number Diff line number Diff line
@@ -606,8 +606,11 @@ bool rename_temporary_table(THD* thd, TABLE *table, const char *new_db,
			    const char *table_name);
void remove_db_from_cache(const my_string db);
void flush_tables();
#define OWNED_BY_THD_FLAG 1
#define WAIT_OTHER_THREAD_FLAG 2
#define CHECK_KILLED_FLAG 4
bool remove_table_from_cache(THD *thd, const char *db, const char *table,
			     bool return_if_owned_by_thd=0);
                             uint flags);
bool close_cached_tables(THD *thd, bool wait_for_refresh, TABLE_LIST *tables);
void copy_field_from_tmp_record(Field *field,int offset);
int fill_record(List<Item> &fields,List<Item> &values, bool ignore_errors);
@@ -776,7 +779,7 @@ void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock);
void mysql_unlock_some_tables(THD *thd, TABLE **table,uint count);
void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table);
void mysql_lock_abort(THD *thd, TABLE *table);
void mysql_lock_abort_for_thread(THD *thd, TABLE *table);
bool mysql_lock_abort_for_thread(THD *thd, TABLE *table);
MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b);
bool lock_global_read_lock(THD *thd);
void unlock_global_read_lock(THD *thd);
+89 −43
Original line number Diff line number Diff line
@@ -370,7 +370,9 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh,
    bool found=0;
    for (TABLE_LIST *table=tables ; table ; table=table->next)
    {
      if (remove_table_from_cache(thd, table->db, table->real_name, 1))
      uint flags= OWNED_BY_THD_FLAG;
      if (remove_table_from_cache(thd, table->db, table->real_name,
                                  flags))
	found=1;
    }
    if (!found)
@@ -2407,15 +2409,22 @@ void flush_tables()
*/

bool remove_table_from_cache(THD *thd, const char *db, const char *table_name,
			     bool return_if_owned_by_thd)
                             uint flags)
{
  char key[MAX_DBKEY_LENGTH];
  uint key_length;
  TABLE *table;
  bool result=0;
  bool result=0, signalled= 0;
  bool return_if_owned_by_thd= flags & OWNED_BY_THD_FLAG;
  bool wait_for_other_thread= flags & WAIT_OTHER_THREAD_FLAG;
  bool check_killed_flag= flags & CHECK_KILLED_FLAG;
  DBUG_ENTER("remove_table_from_cache");

  key_length=(uint) (strmov(strmov(key,db)+1,table_name)-key)+1;
  do
  {
    result= signalled= 0;

    for (table=(TABLE*) hash_search(&open_cache,(byte*) key,key_length) ;
         table;
         table = (TABLE*) hash_next(&open_cache,(byte*) key,key_length))
@@ -2441,6 +2450,7 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name,
	  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);
	  }
@@ -2455,7 +2465,11 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name,
	     thd_table= thd_table->next)
        {
	  if (thd_table->db_stat)		// If table is open
	  mysql_lock_abort_for_thread(thd, thd_table);
          {
            bool found;
	    found= mysql_lock_abort_for_thread(thd, thd_table);
            signalled|= found;
          }
        }
      }
      else
@@ -2463,6 +2477,38 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name,
    }
    while (unused_tables && !unused_tables->version)
      VOID(hash_delete(&open_cache,(byte*) unused_tables));
    if (result && wait_for_other_thread)
    {
      if (!check_killed_flag || !thd->killed)
      {
        if (likely(signalled))
        {
          dropping_tables++;
          (void) pthread_cond_wait(&COND_refresh, &LOCK_open);
          dropping_tables--;
          continue;
        }
        else
        {
          /*
            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.
          */
          pthread_mutex_unlock(&LOCK_open);
          my_sleep(10);
          pthread_mutex_lock(&LOCK_open);
          continue;
        }
      }
    }
    break;
  } while (1);
  DBUG_RETURN(result);
}

Loading