summaryrefslogtreecommitdiff
path: root/sql/lock.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/lock.cc')
-rw-r--r--sql/lock.cc314
1 files changed, 233 insertions, 81 deletions
diff --git a/sql/lock.cc b/sql/lock.cc
index ab4a81034ba..97a080c5634 100644
--- a/sql/lock.cc
+++ b/sql/lock.cc
@@ -81,7 +81,7 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table,uint count,
static void reset_lock_data(MYSQL_LOCK *sql_lock);
static int lock_external(THD *thd, TABLE **table,uint count);
static int unlock_external(THD *thd, TABLE **table,uint count);
-static void print_lock_error(int error);
+static void print_lock_error(int error, const char *);
/*
Lock tables.
@@ -94,18 +94,33 @@ static void print_lock_error(int error);
flags Options:
MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK Ignore a global read lock
MYSQL_LOCK_IGNORE_FLUSH Ignore a flush tables.
+ MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN Instead of reopening altered
+ or dropped tables by itself,
+ mysql_lock_tables() should
+ notify upper level and rely
+ on caller doing this.
+ need_reopen Out parameter, TRUE if some tables were altered
+ or deleted and should be reopened by caller.
RETURN
A lock structure pointer on success.
- NULL on error.
+ NULL on error or if some tables should be reopen.
*/
-MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, uint flags)
+/* Map the return value of thr_lock to an error from errmsg.txt */
+static int thr_lock_errno_to_mysql[]=
+{ 0, 1, ER_LOCK_WAIT_TIMEOUT, ER_LOCK_DEADLOCK };
+
+MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count,
+ uint flags, bool *need_reopen)
{
MYSQL_LOCK *sql_lock;
TABLE *write_lock_used;
+ int rc;
DBUG_ENTER("mysql_lock_tables");
+ *need_reopen= FALSE;
+
for (;;)
{
if (! (sql_lock= get_lock_data(thd, tables, count, GET_LOCK_STORE_LOCKS,
@@ -143,7 +158,6 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, uint flags)
reset_lock_data(sql_lock);
my_free((gptr) sql_lock,MYF(0));
sql_lock=0;
- thd->proc_info=0;
break;
}
thd->proc_info="Table lock";
@@ -152,11 +166,21 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, uint flags)
memcpy(sql_lock->locks + sql_lock->lock_count, sql_lock->locks,
sql_lock->lock_count * sizeof(*sql_lock->locks));
/* Lock on the copied half of the lock data array. */
- if (thr_multi_lock(sql_lock->locks + sql_lock->lock_count,
- sql_lock->lock_count))
+ rc= thr_lock_errno_to_mysql[(int) thr_multi_lock(sql_lock->locks +
+ sql_lock->lock_count,
+ sql_lock->lock_count,
+ thd->lock_id)];
+ if (rc > 1) /* a timeout or a deadlock */
+ {
+ my_error(rc, MYF(0));
+ my_free((gptr) sql_lock,MYF(0));
+ sql_lock= 0;
+ break;
+ }
+ else if (rc == 1) /* aborted */
{
thd->some_tables_deleted=1; // Try again
- sql_lock->lock_count=0; // Locks are alread freed
+ sql_lock->lock_count= 0; // Locks are already freed
}
else if (!thd->some_tables_deleted || (flags & MYSQL_LOCK_IGNORE_FLUSH))
{
@@ -177,13 +201,18 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, uint flags)
thd->locked=0;
retry:
sql_lock=0;
+ if (flags & MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN)
+ {
+ *need_reopen= TRUE;
+ break;
+ }
if (wait_for_tables(thd))
break; // Couldn't open tables
}
thd->proc_info=0;
if (thd->killed)
{
- my_error(ER_SERVER_SHUTDOWN,MYF(0));
+ thd->send_kill_message();
if (sql_lock)
{
mysql_unlock_tables(thd,sql_lock);
@@ -213,12 +242,12 @@ static int lock_external(THD *thd, TABLE **tables, uint count)
if ((error=(*tables)->file->external_lock(thd,lock_type)))
{
+ print_lock_error(error, (*tables)->file->table_type());
for (; i-- ; tables--)
{
(*tables)->file->external_lock(thd, F_UNLCK);
(*tables)->current_lock=F_UNLCK;
}
- print_lock_error(error);
DBUG_RETURN(error);
}
else
@@ -476,8 +505,8 @@ MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b)
SYNOPSIS
mysql_lock_have_duplicate()
thd The current thread.
- table The table to check for duplicate lock.
- tables The list of tables to search for the dup lock.
+ needle The table to check for duplicate lock.
+ haystack The list of tables to search for the dup lock.
NOTE
This is mainly meant for MERGE tables in INSERT ... SELECT
@@ -490,28 +519,38 @@ MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b)
both functions should be checked.
RETURN
- 1 A table from 'tables' matches a lock on 'table'.
- 0 No duplicate lock found.
+ NULL No duplicate lock found.
+ ! NULL First table from 'haystack' that matches a lock on 'needle'.
*/
-int mysql_lock_have_duplicate(THD *thd, TABLE *table, TABLE_LIST *tables)
+TABLE_LIST *mysql_lock_have_duplicate(THD *thd, TABLE_LIST *needle,
+ TABLE_LIST *haystack)
{
MYSQL_LOCK *mylock;
TABLE **lock_tables;
+ TABLE *table;
TABLE *table2;
- THR_LOCK_DATA **lock_locks, **table_lock_data;
+ THR_LOCK_DATA **lock_locks;
+ THR_LOCK_DATA **table_lock_data;
THR_LOCK_DATA **end_data;
THR_LOCK_DATA **lock_data2;
THR_LOCK_DATA **end_data2;
DBUG_ENTER("mysql_lock_have_duplicate");
+ /*
+ Table may not be defined for derived or view tables.
+ Table may not be part of a lock for delayed operations.
+ */
+ if (! (table= needle->table) || ! table->lock_count)
+ goto end;
+
/* A temporary table does not have locks. */
- if (table->tmp_table == TMP_TABLE)
+ if (table->s->tmp_table == TMP_TABLE)
goto end;
- /* Get command lock or LOCK TABLES lock. */
- mylock= thd->lock ? thd->lock : thd->locked_tables;
- DBUG_ASSERT(mylock);
+ /* Get command lock or LOCK TABLES lock. Maybe empty for INSERT DELAYED. */
+ if (! (mylock= thd->lock ? thd->lock : thd->locked_tables))
+ goto end;
/* If we have less than two tables, we cannot have duplicates. */
if (mylock->table_count < 2)
@@ -521,18 +560,22 @@ int mysql_lock_have_duplicate(THD *thd, TABLE *table, TABLE_LIST *tables)
lock_tables= mylock->table;
/* Prepare table related variables that don't change in loop. */
- DBUG_ASSERT(table == lock_tables[table->lock_position]);
+ DBUG_ASSERT((table->lock_position < mylock->table_count) &&
+ (table == lock_tables[table->lock_position]));
table_lock_data= lock_locks + table->lock_data_start;
end_data= table_lock_data + table->lock_count;
- for (; tables; tables= tables->next)
+ for (; haystack; haystack= haystack->next_global)
{
- table2= tables->table;
- if (table2->tmp_table == TMP_TABLE)
+ if (haystack->placeholder() || haystack->schema_table)
+ continue;
+ table2= haystack->table;
+ if (table2->s->tmp_table == TMP_TABLE)
continue;
/* All tables in list must be in lock. */
- DBUG_ASSERT(table2 == lock_tables[table2->lock_position]);
+ DBUG_ASSERT((table2->lock_position < mylock->table_count) &&
+ (table2 == lock_tables[table2->lock_position]));
for (lock_data2= lock_locks + table2->lock_data_start,
end_data2= lock_data2 + table2->lock_count;
@@ -547,13 +590,17 @@ int mysql_lock_have_duplicate(THD *thd, TABLE *table, TABLE_LIST *tables)
lock_data++)
{
if ((*lock_data)->lock == lock2)
- DBUG_RETURN(1);
+ {
+ DBUG_PRINT("info", ("haystack match: '%s'", haystack->table_name));
+ DBUG_RETURN(haystack);
+ }
}
}
}
end:
- DBUG_RETURN(0);
+ DBUG_PRINT("info", ("no duplicate found"));
+ DBUG_RETURN(NULL);
}
@@ -571,12 +618,13 @@ static int unlock_external(THD *thd, TABLE **table,uint count)
{
(*table)->current_lock = F_UNLCK;
if ((error=(*table)->file->external_lock(thd, F_UNLCK)))
+ {
error_code=error;
+ print_lock_error(error_code, (*table)->file->table_type());
+ }
}
table++;
} while (--count);
- if (error_code)
- print_lock_error(error_code);
DBUG_RETURN(error_code);
}
@@ -608,11 +656,24 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
*write_lock_used=0;
for (i=tables=lock_count=0 ; i < count ; i++)
{
- if (table_ptr[i]->tmp_table != TMP_TABLE)
+ if (table_ptr[i]->s->tmp_table != TMP_TABLE)
{
tables+=table_ptr[i]->file->lock_count();
lock_count++;
}
+ /*
+ To be able to open and lock for reading system tables like 'mysql.proc',
+ when we already have some tables opened and locked, and avoid deadlocks
+ we have to disallow write-locking of these tables with any other tables.
+ */
+ if (table_ptr[i]->s->system_table &&
+ table_ptr[i]->reginfo.lock_type >= TL_WRITE_ALLOW_WRITE &&
+ count != 1)
+ {
+ my_error(ER_WRONG_LOCK_OF_SYSTEM_TABLE, MYF(0), table_ptr[i]->s->db,
+ table_ptr[i]->s->table_name);
+ return 0;
+ }
}
/*
@@ -637,7 +698,7 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
TABLE *table;
enum thr_lock_type lock_type;
- if ((table=table_ptr[i])->tmp_table == TMP_TABLE)
+ if ((table=table_ptr[i])->s->tmp_table == TMP_TABLE)
continue;
lock_type= table->reginfo.lock_type;
if (lock_type >= TL_WRITE_ALLOW_WRITE)
@@ -645,7 +706,7 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
*write_lock_used=table;
if (table->db_stat & HA_READ_ONLY)
{
- my_error(ER_OPEN_AS_READONLY,MYF(0),table->table_name);
+ my_error(ER_OPEN_AS_READONLY,MYF(0),table->alias);
/* Clear the lock type of the lock data that are stored already. */
sql_lock->lock_count= locks - sql_lock->locks;
reset_lock_data(sql_lock);
@@ -796,11 +857,11 @@ int lock_table_name(THD *thd, TABLE_LIST *table_list)
uint key_length;
HASH_SEARCH_STATE state;
DBUG_ENTER("lock_table_name");
- DBUG_PRINT("enter",("db: %s name: %s", db, table_list->real_name));
+ DBUG_PRINT("enter",("db: %s name: %s", db, table_list->table_name));
safe_mutex_assert_owner(&LOCK_open);
- key_length=(uint) (strmov(strmov(key,db)+1,table_list->real_name)
+ key_length=(uint) (strmov(strmov(key,db)+1,table_list->table_name)
-key)+ 1;
@@ -819,8 +880,10 @@ int lock_table_name(THD *thd, TABLE_LIST *table_list)
if (!(table= (TABLE*) my_malloc(sizeof(*table)+key_length,
MYF(MY_WME | MY_ZEROFILL))))
DBUG_RETURN(-1);
- memcpy((table->table_cache_key= (char*) (table+1)), key, key_length);
- table->key_length=key_length;
+ table->s= &table->share_not_to_be_used;
+ memcpy((table->s->table_cache_key= (char*) (table+1)), key, key_length);
+ table->s->db= table->s->table_cache_key;
+ table->s->key_length=key_length;
table->in_use=thd;
table->locked_by_name=1;
table_list->table=table;
@@ -831,11 +894,9 @@ int lock_table_name(THD *thd, TABLE_LIST *table_list)
DBUG_RETURN(-1);
}
- if (remove_table_from_cache(thd, db, table_list->real_name, RTFC_NO_FLAG))
- {
- DBUG_RETURN(1); // Table is in use
- }
- DBUG_RETURN(0);
+ /* Return 1 if table is in use */
+ DBUG_RETURN(test(remove_table_from_cache(thd, db, table_list->table_name,
+ RTFC_NO_FLAG)));
}
@@ -844,14 +905,14 @@ void unlock_table_name(THD *thd, TABLE_LIST *table_list)
if (table_list->table)
{
hash_delete(&open_cache, (byte*) table_list->table);
- (void) pthread_cond_broadcast(&COND_refresh);
+ broadcast_refresh();
}
}
static bool locked_named_table(THD *thd, TABLE_LIST *table_list)
{
- for (; table_list ; table_list=table_list->next)
+ for (; table_list ; table_list=table_list->next_local)
{
if (table_list->table && table_is_used(table_list->table,0))
return 1;
@@ -905,7 +966,7 @@ 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)
+ for (lock_table= table_list; lock_table; lock_table= lock_table->next_local)
{
int got_lock;
if ((got_lock=lock_table_name(thd,lock_table)) < 0)
@@ -936,9 +997,9 @@ end:
(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
+ One must have a lock on LOCK_open when calling this.
+ This function will broadcast refresh signals to inform other threads
+ that the name locks are removed.
RETURN
0 ok
@@ -948,13 +1009,15 @@ end:
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)
+ for (TABLE_LIST *table= table_list;
+ table != last_table;
+ table= table->next_local)
unlock_table_name(thd,table);
- pthread_cond_broadcast(&COND_refresh);
+ broadcast_refresh();
}
-static void print_lock_error(int error)
+static void print_lock_error(int error, const char *table)
{
int textno;
DBUG_ENTER("print_lock_error");
@@ -966,11 +1029,22 @@ static void print_lock_error(int error)
case HA_ERR_READ_ONLY_TRANSACTION:
textno=ER_READ_ONLY_TRANSACTION;
break;
+ case HA_ERR_LOCK_DEADLOCK:
+ textno=ER_LOCK_DEADLOCK;
+ break;
+ case HA_ERR_WRONG_COMMAND:
+ textno=ER_ILLEGAL_HA;
+ break;
default:
textno=ER_CANT_LOCK;
break;
}
- my_error(textno,MYF(ME_BELL+ME_OLDWIN+ME_WAITTANG),error);
+
+ if ( textno == ER_ILLEGAL_HA )
+ my_error(textno, MYF(ME_BELL+ME_OLDWIN+ME_WAITTANG), table);
+ else
+ my_error(textno, MYF(ME_BELL+ME_OLDWIN+ME_WAITTANG), error);
+
DBUG_VOID_RETURN;
}
@@ -994,6 +1068,16 @@ static void print_lock_error(int error)
protect_against_global_read_lock
count of threads which have set protection against global read lock.
+ access to them is protected with a mutex LOCK_global_read_lock
+
+ (XXX: one should never take LOCK_open if LOCK_global_read_lock is
+ taken, otherwise a deadlock may occur. Other mutexes could be a
+ problem too - grep the code for global_read_lock if you want to use
+ any other mutex here) Also one must not hold LOCK_open when calling
+ wait_if_global_read_lock(). When the thread with the global read lock
+ tries to close its tables, it needs to take LOCK_open in
+ close_thread_table().
+
How blocking of threads by global read lock is achieved: that's
advisory. Any piece of code which should be blocked by global read lock must
be designed like this:
@@ -1031,17 +1115,13 @@ static void print_lock_error(int error)
table instance of thd2
thd1: COMMIT; # blocked by thd3.
thd1 blocks thd2 which blocks thd3 which blocks thd1: deadlock.
-
+
Note that we need to support that one thread does
FLUSH TABLES WITH READ LOCK; and then COMMIT;
(that's what innobackup does, for some good reason).
So in this exceptional case the COMMIT should not be blocked by the FLUSH
TABLES WITH READ LOCK.
- TODO in MySQL 5.x: make_global_read_lock_block_commit() should be
- killable. Normally CPU does not spend a long time in this function (COMMITs
- are quite fast), but it would still be nice.
-
****************************************************************************/
volatile uint global_read_lock=0;
@@ -1058,16 +1138,17 @@ bool lock_global_read_lock(THD *thd)
if (!thd->global_read_lock)
{
- (void) pthread_mutex_lock(&LOCK_open);
- const char *old_message=thd->enter_cond(&COND_refresh, &LOCK_open,
- "Waiting to get readlock");
+ const char *old_message;
+ (void) pthread_mutex_lock(&LOCK_global_read_lock);
+ old_message=thd->enter_cond(&COND_global_read_lock, &LOCK_global_read_lock,
+ "Waiting to get readlock");
DBUG_PRINT("info",
("waiting_for: %d protect_against: %d",
waiting_for_read_lock, protect_against_global_read_lock));
waiting_for_read_lock++;
while (protect_against_global_read_lock && !thd->killed)
- pthread_cond_wait(&COND_refresh, &LOCK_open);
+ pthread_cond_wait(&COND_global_read_lock, &LOCK_global_read_lock);
waiting_for_read_lock--;
if (thd->killed)
{
@@ -1076,7 +1157,7 @@ bool lock_global_read_lock(THD *thd)
}
thd->global_read_lock= GOT_GLOBAL_READ_LOCK;
global_read_lock++;
- thd->exit_cond(old_message);
+ thd->exit_cond(old_message); // this unlocks LOCK_global_read_lock
}
/*
We DON'T set global_read_lock_blocks_commit now, it will be set after
@@ -1089,18 +1170,29 @@ bool lock_global_read_lock(THD *thd)
DBUG_RETURN(0);
}
+
void unlock_global_read_lock(THD *thd)
{
uint tmp;
- pthread_mutex_lock(&LOCK_open);
+ DBUG_ENTER("unlock_global_read_lock");
+ DBUG_PRINT("info",
+ ("global_read_lock: %u global_read_lock_blocks_commit: %u",
+ global_read_lock, global_read_lock_blocks_commit));
+
+ pthread_mutex_lock(&LOCK_global_read_lock);
tmp= --global_read_lock;
if (thd->global_read_lock == MADE_GLOBAL_READ_LOCK_BLOCK_COMMIT)
--global_read_lock_blocks_commit;
- pthread_mutex_unlock(&LOCK_open);
+ pthread_mutex_unlock(&LOCK_global_read_lock);
/* Send the signal outside the mutex to avoid a context switch */
if (!tmp)
- pthread_cond_broadcast(&COND_refresh);
+ {
+ DBUG_PRINT("signal", ("Broadcasting COND_global_read_lock"));
+ pthread_cond_broadcast(&COND_global_read_lock);
+ }
thd->global_read_lock= 0;
+
+ DBUG_VOID_RETURN;
}
#define must_wait (global_read_lock && \
@@ -1115,14 +1207,22 @@ bool wait_if_global_read_lock(THD *thd, bool abort_on_refresh,
DBUG_ENTER("wait_if_global_read_lock");
LINT_INIT(old_message);
- (void) pthread_mutex_lock(&LOCK_open);
+ /*
+ Assert that we do not own LOCK_open. If we would own it, other
+ threads could not close their tables. This would make a pretty
+ deadlock.
+ */
+ safe_mutex_assert_not_owner(&LOCK_open);
+
+ (void) pthread_mutex_lock(&LOCK_global_read_lock);
if ((need_exit_cond= must_wait))
{
if (thd->global_read_lock) // This thread had the read locks
{
if (is_not_commit)
- my_error(ER_CANT_UPDATE_WITH_READLOCK,MYF(0));
- (void) pthread_mutex_unlock(&LOCK_open);
+ my_message(ER_CANT_UPDATE_WITH_READLOCK,
+ ER(ER_CANT_UPDATE_WITH_READLOCK), MYF(0));
+ (void) pthread_mutex_unlock(&LOCK_global_read_lock);
/*
We allow FLUSHer to COMMIT; we assume FLUSHer knows what it does.
This allowance is needed to not break existing versions of innobackup
@@ -1130,11 +1230,15 @@ bool wait_if_global_read_lock(THD *thd, bool abort_on_refresh,
*/
DBUG_RETURN(is_not_commit);
}
- old_message=thd->enter_cond(&COND_refresh, &LOCK_open,
+ old_message=thd->enter_cond(&COND_global_read_lock, &LOCK_global_read_lock,
"Waiting for release of readlock");
while (must_wait && ! thd->killed &&
(!abort_on_refresh || thd->version == refresh_version))
- (void) pthread_cond_wait(&COND_refresh,&LOCK_open);
+ {
+ DBUG_PRINT("signal", ("Waiting for COND_global_read_lock"));
+ (void) pthread_cond_wait(&COND_global_read_lock, &LOCK_global_read_lock);
+ DBUG_PRINT("signal", ("Got COND_global_read_lock"));
+ }
if (thd->killed)
result=1;
}
@@ -1144,10 +1248,10 @@ bool wait_if_global_read_lock(THD *thd, bool abort_on_refresh,
The following is only true in case of a global read locks (which is rare)
and if old_message is set
*/
- if (unlikely(need_exit_cond))
- thd->exit_cond(old_message);
+ if (unlikely(need_exit_cond))
+ thd->exit_cond(old_message); // this unlocks LOCK_global_read_lock
else
- pthread_mutex_unlock(&LOCK_open);
+ pthread_mutex_unlock(&LOCK_global_read_lock);
DBUG_RETURN(result);
}
@@ -1158,31 +1262,79 @@ void start_waiting_global_read_lock(THD *thd)
DBUG_ENTER("start_waiting_global_read_lock");
if (unlikely(thd->global_read_lock))
DBUG_VOID_RETURN;
- (void) pthread_mutex_lock(&LOCK_open);
+ (void) pthread_mutex_lock(&LOCK_global_read_lock);
tmp= (!--protect_against_global_read_lock &&
(waiting_for_read_lock || global_read_lock_blocks_commit));
- (void) pthread_mutex_unlock(&LOCK_open);
+ (void) pthread_mutex_unlock(&LOCK_global_read_lock);
if (tmp)
- pthread_cond_broadcast(&COND_refresh);
+ pthread_cond_broadcast(&COND_global_read_lock);
DBUG_VOID_RETURN;
}
-void make_global_read_lock_block_commit(THD *thd)
+bool make_global_read_lock_block_commit(THD *thd)
{
+ bool error;
+ const char *old_message;
+ DBUG_ENTER("make_global_read_lock_block_commit");
/*
If we didn't succeed lock_global_read_lock(), or if we already suceeded
make_global_read_lock_block_commit(), do nothing.
*/
if (thd->global_read_lock != GOT_GLOBAL_READ_LOCK)
- return;
- pthread_mutex_lock(&LOCK_open);
+ DBUG_RETURN(0);
+ pthread_mutex_lock(&LOCK_global_read_lock);
/* increment this BEFORE waiting on cond (otherwise race cond) */
global_read_lock_blocks_commit++;
- while (protect_against_global_read_lock)
- pthread_cond_wait(&COND_refresh, &LOCK_open);
- pthread_mutex_unlock(&LOCK_open);
- thd->global_read_lock= MADE_GLOBAL_READ_LOCK_BLOCK_COMMIT;
+ /* For testing we set up some blocking, to see if we can be killed */
+ DBUG_EXECUTE_IF("make_global_read_lock_block_commit_loop",
+ protect_against_global_read_lock++;);
+ old_message= thd->enter_cond(&COND_global_read_lock, &LOCK_global_read_lock,
+ "Waiting for all running commits to finish");
+ while (protect_against_global_read_lock && !thd->killed)
+ pthread_cond_wait(&COND_global_read_lock, &LOCK_global_read_lock);
+ DBUG_EXECUTE_IF("make_global_read_lock_block_commit_loop",
+ protect_against_global_read_lock--;);
+ if ((error= test(thd->killed)))
+ global_read_lock_blocks_commit--; // undo what we did
+ else
+ thd->global_read_lock= MADE_GLOBAL_READ_LOCK_BLOCK_COMMIT;
+ thd->exit_cond(old_message); // this unlocks LOCK_global_read_lock
+ DBUG_RETURN(error);
+}
+
+
+/*
+ Broadcast COND_refresh and COND_global_read_lock.
+
+ SYNOPSIS
+ broadcast_refresh()
+ void No parameters.
+
+ DESCRIPTION
+ Due to a bug in a threading library it could happen that a signal
+ did not reach its target. A condition for this was that the same
+ condition variable was used with different mutexes in
+ pthread_cond_wait(). Some time ago we changed LOCK_open to
+ LOCK_global_read_lock in global read lock handling. So COND_refresh
+ was used with LOCK_open and LOCK_global_read_lock.
+
+ We did now also change from COND_refresh to COND_global_read_lock
+ in global read lock handling. But now it is necessary to signal
+ both conditions at the same time.
+
+ NOTE
+ When signalling COND_global_read_lock within the global read lock
+ handling, it is not necessary to also signal COND_refresh.
+
+ RETURN
+ void
+*/
+
+void broadcast_refresh(void)
+{
+ VOID(pthread_cond_broadcast(&COND_refresh));
+ VOID(pthread_cond_broadcast(&COND_global_read_lock));
}