Commit f3e9b392 authored by Jon Olav Hauglid's avatar Jon Olav Hauglid

Backport of revno: 2617.68.13

Introduce a counter for protection against global read lock on thread level.

The functions for protection against global read lock sometimes need a local
variable to signal when the protection is set, and hence need to be released.
It would be better to control this behaviour via a counter on the THD struct,
telling how many times the protection has been claimed by the current thread.
A side-effect of the fix is that if protection is claimed twice for a thread,
only a simple increment is required for the second claim, instead of a
mutex-protected increment of the global variable protect_against_global_read_lock.


sql/lock.cc:
  Count how many times that we have claimed protection against global read lock.
  Assert that we really have the protection when releasing it.
  Added comments to all functions operating on global_read_lock.
sql/sql_class.cc:
  Added the counter variable global_read_lock_protection.
sql/sql_class.h:
  Added the counter variable global_read_lock_protection.
sql/sql_parse.cc:
  Replaced test on local variable need_start_waiting with test on
  thd->global_read_lock_protection.
sql/sql_table.cc:
  Replaced test on local variable need_start_waiting with test on
  thd->global_read_lock_protection.
sql/sql_trigger.cc:
  Inserted test on thd->global_read_lock_protection.
parent 832ad466
...@@ -1098,6 +1098,19 @@ static volatile uint waiting_for_read_lock=0; ...@@ -1098,6 +1098,19 @@ static volatile uint waiting_for_read_lock=0;
#define GOT_GLOBAL_READ_LOCK 1 #define GOT_GLOBAL_READ_LOCK 1
#define MADE_GLOBAL_READ_LOCK_BLOCK_COMMIT 2 #define MADE_GLOBAL_READ_LOCK_BLOCK_COMMIT 2
/**
Take global read lock, wait if there is protection against lock.
If the global read lock is already taken by this thread, then nothing is done.
See also "Handling of global read locks" above.
@param thd Reference to thread.
@retval False Success, global read lock set, commits are NOT blocked.
@retval True Failure, thread was killed.
*/
bool lock_global_read_lock(THD *thd) bool lock_global_read_lock(THD *thd)
{ {
DBUG_ENTER("lock_global_read_lock"); DBUG_ENTER("lock_global_read_lock");
...@@ -1164,6 +1177,16 @@ bool lock_global_read_lock(THD *thd) ...@@ -1164,6 +1177,16 @@ bool lock_global_read_lock(THD *thd)
} }
/**
Unlock global read lock.
Commits may or may not be blocked when this function is called.
See also "Handling of global read locks" above.
@param thd Reference to thread.
*/
void unlock_global_read_lock(THD *thd) void unlock_global_read_lock(THD *thd)
{ {
uint tmp; uint tmp;
...@@ -1190,6 +1213,25 @@ void unlock_global_read_lock(THD *thd) ...@@ -1190,6 +1213,25 @@ void unlock_global_read_lock(THD *thd)
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
/**
Wait if the global read lock is set, and optionally seek protection against
global read lock.
See also "Handling of global read locks" above.
@param thd Reference to thread.
@param abort_on_refresh If True, abort waiting if a refresh occurs,
do NOT seek protection against GRL.
If False, wait until the GRL is released and seek
protection against GRL.
@param is_not_commit If False, called from a commit operation,
wait only if commit blocking is also enabled.
@retval False Success, protection against global read lock is set
(if !abort_on_refresh)
@retval True Failure, wait was aborted or thread was killed.
*/
#define must_wait (global_read_lock && \ #define must_wait (global_read_lock && \
(is_not_commit || \ (is_not_commit || \
global_read_lock_blocks_commit)) global_read_lock_blocks_commit))
...@@ -1201,6 +1243,16 @@ bool wait_if_global_read_lock(THD *thd, bool abort_on_refresh, ...@@ -1201,6 +1243,16 @@ bool wait_if_global_read_lock(THD *thd, bool abort_on_refresh,
bool result= 0, need_exit_cond; bool result= 0, need_exit_cond;
DBUG_ENTER("wait_if_global_read_lock"); DBUG_ENTER("wait_if_global_read_lock");
/*
If we already have protection against global read lock,
just increment the counter.
*/
if (unlikely(thd->global_read_lock_protection > 0))
{
if (!abort_on_refresh)
thd->global_read_lock_protection++;
DBUG_RETURN(FALSE);
}
/* /*
Assert that we do not own LOCK_open. If we would own it, other 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 threads could not close their tables. This would make a pretty
...@@ -1237,7 +1289,12 @@ bool wait_if_global_read_lock(THD *thd, bool abort_on_refresh, ...@@ -1237,7 +1289,12 @@ bool wait_if_global_read_lock(THD *thd, bool abort_on_refresh,
result=1; result=1;
} }
if (!abort_on_refresh && !result) if (!abort_on_refresh && !result)
{
thd->global_read_lock_protection++;
protect_against_global_read_lock++; protect_against_global_read_lock++;
DBUG_PRINT("sql_lock", ("protect_against_global_read_lock incr: %u",
protect_against_global_read_lock));
}
/* /*
The following is only true in case of a global read locks (which is rare) The following is only true in case of a global read locks (which is rare)
and if old_message is set and if old_message is set
...@@ -1250,10 +1307,31 @@ bool wait_if_global_read_lock(THD *thd, bool abort_on_refresh, ...@@ -1250,10 +1307,31 @@ bool wait_if_global_read_lock(THD *thd, bool abort_on_refresh,
} }
/**
Release protection against global read lock and restart
global read lock waiters.
Should only be called if we have protection against global read lock.
See also "Handling of global read locks" above.
@param thd Reference to thread.
*/
void start_waiting_global_read_lock(THD *thd) void start_waiting_global_read_lock(THD *thd)
{ {
bool tmp; bool tmp;
DBUG_ENTER("start_waiting_global_read_lock"); DBUG_ENTER("start_waiting_global_read_lock");
/*
Ignore request if we do not have protection against global read lock.
(Note that this is a violation of the interface contract, hence the assert).
*/
DBUG_ASSERT(thd->global_read_lock_protection > 0);
if (unlikely(thd->global_read_lock_protection == 0))
DBUG_VOID_RETURN;
/* Decrement local read lock protection counter, return if we still have it */
if (unlikely(--thd->global_read_lock_protection > 0))
DBUG_VOID_RETURN;
if (unlikely(thd->global_read_lock)) if (unlikely(thd->global_read_lock))
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
(void) pthread_mutex_lock(&LOCK_global_read_lock); (void) pthread_mutex_lock(&LOCK_global_read_lock);
...@@ -1267,6 +1345,21 @@ void start_waiting_global_read_lock(THD *thd) ...@@ -1267,6 +1345,21 @@ void start_waiting_global_read_lock(THD *thd)
} }
/**
Make global read lock also block commits.
The scenario is:
- This thread has the global read lock.
- Global read lock blocking of commits is not set.
See also "Handling of global read locks" above.
@param thd Reference to thread.
@retval False Success, global read lock set, commits are blocked.
@retval True Failure, thread was killed.
*/
bool make_global_read_lock_block_commit(THD *thd) bool make_global_read_lock_block_commit(THD *thd)
{ {
bool error; bool error;
......
...@@ -452,6 +452,7 @@ THD::THD() ...@@ -452,6 +452,7 @@ THD::THD()
examined_row_count(0), examined_row_count(0),
warning_info(&main_warning_info), warning_info(&main_warning_info),
stmt_da(&main_da), stmt_da(&main_da),
global_read_lock_protection(0),
global_read_lock(0), global_read_lock(0),
is_fatal_error(0), is_fatal_error(0),
transaction_rollback_request(0), transaction_rollback_request(0),
......
...@@ -1874,6 +1874,7 @@ public: ...@@ -1874,6 +1874,7 @@ public:
ulong rand_saved_seed1, rand_saved_seed2; ulong rand_saved_seed1, rand_saved_seed2;
pthread_t real_id; /* For debugging */ pthread_t real_id; /* For debugging */
my_thread_id thread_id; my_thread_id thread_id;
uint global_read_lock_protection;// GRL protection count
uint tmp_table, global_read_lock; uint tmp_table, global_read_lock;
uint server_status,open_options; uint server_status,open_options;
enum enum_thread_type system_thread; enum enum_thread_type system_thread;
......
...@@ -1762,7 +1762,6 @@ int ...@@ -1762,7 +1762,6 @@ int
mysql_execute_command(THD *thd) mysql_execute_command(THD *thd)
{ {
int res= FALSE; int res= FALSE;
bool need_start_waiting= FALSE; // have protection against global read lock
int up_result= 0; int up_result= 0;
LEX *lex= thd->lex; LEX *lex= thd->lex;
/* first SELECT_LEX (have special meaning for many of non-SELECTcommands) */ /* first SELECT_LEX (have special meaning for many of non-SELECTcommands) */
...@@ -2039,7 +2038,7 @@ mysql_execute_command(THD *thd) ...@@ -2039,7 +2038,7 @@ mysql_execute_command(THD *thd)
break; break;
if (!thd->locked_tables_mode && lex->protect_against_global_read_lock && if (!thd->locked_tables_mode && lex->protect_against_global_read_lock &&
!(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1))) wait_if_global_read_lock(thd, 0, 1))
break; break;
res= execute_sqlcom_select(thd, all_tables); res= execute_sqlcom_select(thd, all_tables);
...@@ -2309,10 +2308,9 @@ case SQLCOM_PREPARE: ...@@ -2309,10 +2308,9 @@ case SQLCOM_PREPARE:
read lock when it succeeds. This needs to be released by read lock when it succeeds. This needs to be released by
start_waiting_global_read_lock(). We protect the normal CREATE start_waiting_global_read_lock(). We protect the normal CREATE
TABLE in the same way. That way we avoid that a new table is TABLE in the same way. That way we avoid that a new table is
created during a gobal read lock. created during a global read lock.
*/ */
if (!thd->locked_tables_mode && if (!thd->locked_tables_mode && wait_if_global_read_lock(thd, 0, 1))
!(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
{ {
res= 1; res= 1;
goto end_with_restore_list; goto end_with_restore_list;
...@@ -2617,8 +2615,7 @@ end_with_restore_list: ...@@ -2617,8 +2615,7 @@ end_with_restore_list:
"INDEX DIRECTORY"); "INDEX DIRECTORY");
create_info.data_file_name= create_info.index_file_name= NULL; create_info.data_file_name= create_info.index_file_name= NULL;
if (!thd->locked_tables_mode && if (!thd->locked_tables_mode && wait_if_global_read_lock(thd, 0, 1))
!(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
{ {
res= 1; res= 1;
break; break;
...@@ -2852,8 +2849,7 @@ end_with_restore_list: ...@@ -2852,8 +2849,7 @@ end_with_restore_list:
DBUG_ASSERT(first_table == all_tables && first_table != 0); DBUG_ASSERT(first_table == all_tables && first_table != 0);
if (update_precheck(thd, all_tables)) if (update_precheck(thd, all_tables))
break; break;
if (!thd->locked_tables_mode && if (!thd->locked_tables_mode && wait_if_global_read_lock(thd, 0, 1))
!(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
goto error; goto error;
DBUG_ASSERT(select_lex->offset_limit == 0); DBUG_ASSERT(select_lex->offset_limit == 0);
unit->set_limit(select_lex); unit->set_limit(select_lex);
...@@ -2891,7 +2887,7 @@ end_with_restore_list: ...@@ -2891,7 +2887,7 @@ end_with_restore_list:
*/ */
if (!thd->locked_tables_mode && if (!thd->locked_tables_mode &&
lex->sql_command == SQLCOM_UPDATE_MULTI && lex->sql_command == SQLCOM_UPDATE_MULTI &&
!(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1))) wait_if_global_read_lock(thd, 0, 1))
goto error; goto error;
res= mysql_multi_update_prepare(thd); res= mysql_multi_update_prepare(thd);
...@@ -2993,8 +2989,7 @@ end_with_restore_list: ...@@ -2993,8 +2989,7 @@ end_with_restore_list:
if ((res= insert_precheck(thd, all_tables))) if ((res= insert_precheck(thd, all_tables)))
break; break;
if (!thd->locked_tables_mode && if (!thd->locked_tables_mode && wait_if_global_read_lock(thd, 0, 1))
!(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
{ {
res= 1; res= 1;
break; break;
...@@ -3033,8 +3028,7 @@ end_with_restore_list: ...@@ -3033,8 +3028,7 @@ end_with_restore_list:
unit->set_limit(select_lex); unit->set_limit(select_lex);
if (! thd->locked_tables_mode && if (!thd->locked_tables_mode && wait_if_global_read_lock(thd, 0, 1))
! (need_start_waiting= ! wait_if_global_read_lock(thd, 0, 1)))
{ {
res= 1; res= 1;
break; break;
...@@ -3104,7 +3098,7 @@ end_with_restore_list: ...@@ -3104,7 +3098,7 @@ end_with_restore_list:
ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0)); ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
goto error; goto error;
} }
if (!(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1))) if (wait_if_global_read_lock(thd, 0, 1))
goto error; goto error;
res= mysql_truncate(thd, first_table, 0); res= mysql_truncate(thd, first_table, 0);
break; break;
...@@ -3116,8 +3110,7 @@ end_with_restore_list: ...@@ -3116,8 +3110,7 @@ end_with_restore_list:
DBUG_ASSERT(select_lex->offset_limit == 0); DBUG_ASSERT(select_lex->offset_limit == 0);
unit->set_limit(select_lex); unit->set_limit(select_lex);
if (!thd->locked_tables_mode && if (!thd->locked_tables_mode && wait_if_global_read_lock(thd, 0, 1))
!(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
{ {
res= 1; res= 1;
break; break;
...@@ -3137,8 +3130,7 @@ end_with_restore_list: ...@@ -3137,8 +3130,7 @@ end_with_restore_list:
(TABLE_LIST *)thd->lex->auxiliary_table_list.first; (TABLE_LIST *)thd->lex->auxiliary_table_list.first;
multi_delete *del_result; multi_delete *del_result;
if (!thd->locked_tables_mode && if (!thd->locked_tables_mode && wait_if_global_read_lock(thd, 0, 1))
!(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
{ {
res= 1; res= 1;
break; break;
...@@ -3282,8 +3274,7 @@ end_with_restore_list: ...@@ -3282,8 +3274,7 @@ end_with_restore_list:
if (check_one_table_access(thd, privilege, all_tables)) if (check_one_table_access(thd, privilege, all_tables))
goto error; goto error;
if (!thd->locked_tables_mode && if (!thd->locked_tables_mode && wait_if_global_read_lock(thd, 0, 1))
!(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
goto error; goto error;
res= mysql_load(thd, lex->exchange, first_table, lex->field_list, res= mysql_load(thd, lex->exchange, first_table, lex->field_list,
...@@ -3357,7 +3348,7 @@ end_with_restore_list: ...@@ -3357,7 +3348,7 @@ end_with_restore_list:
FALSE, UINT_MAX, FALSE)) FALSE, UINT_MAX, FALSE))
goto error; goto error;
if (lex->protect_against_global_read_lock && if (lex->protect_against_global_read_lock &&
!(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1))) wait_if_global_read_lock(thd, 0, 1))
goto error; goto error;
init_mdl_requests(all_tables); init_mdl_requests(all_tables);
...@@ -4575,7 +4566,7 @@ error: ...@@ -4575,7 +4566,7 @@ error:
res= TRUE; res= TRUE;
finish: finish:
if (need_start_waiting) if (thd->global_read_lock_protection > 0)
{ {
/* /*
Release the protection against the global read lock and wake Release the protection against the global read lock and wake
......
...@@ -1780,16 +1780,16 @@ void write_bin_log(THD *thd, bool clear_error, ...@@ -1780,16 +1780,16 @@ void write_bin_log(THD *thd, bool clear_error,
bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists, bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
my_bool drop_temporary) my_bool drop_temporary)
{ {
bool error= FALSE, need_start_waiting= FALSE; bool error;
Drop_table_error_handler err_handler(thd->get_internal_handler()); Drop_table_error_handler err_handler(thd->get_internal_handler());
DBUG_ENTER("mysql_rm_table"); DBUG_ENTER("mysql_rm_table");
/* mark for close and remove all cached entries */ /* mark for close and remove all cached entries */
if (!drop_temporary) if (!drop_temporary)
{ {
if (!thd->locked_tables_mode && if (!thd->locked_tables_mode && wait_if_global_read_lock(thd, 0, 1))
!(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
} }
...@@ -1797,8 +1797,7 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists, ...@@ -1797,8 +1797,7 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
error= mysql_rm_table_part2(thd, tables, if_exists, drop_temporary, 0, 0); error= mysql_rm_table_part2(thd, tables, if_exists, drop_temporary, 0, 0);
thd->pop_internal_handler(); thd->pop_internal_handler();
if (thd->global_read_lock_protection > 0)
if (need_start_waiting)
start_waiting_global_read_lock(thd); start_waiting_global_read_lock(thd);
if (error) if (error)
......
...@@ -328,7 +328,6 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) ...@@ -328,7 +328,6 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
TABLE *table; TABLE *table;
bool result= TRUE; bool result= TRUE;
String stmt_query; String stmt_query;
bool need_start_waiting= FALSE;
bool lock_upgrade_done= FALSE; bool lock_upgrade_done= FALSE;
MDL_ticket *mdl_ticket= NULL; MDL_ticket *mdl_ticket= NULL;
...@@ -386,8 +385,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) ...@@ -386,8 +385,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
LOCK_open is not enough because global read lock is held without holding LOCK_open is not enough because global read lock is held without holding
LOCK_open). LOCK_open).
*/ */
if (!thd->locked_tables_mode && if (!thd->locked_tables_mode && wait_if_global_read_lock(thd, 0, 1))
!(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
if (!create) if (!create)
...@@ -521,7 +519,7 @@ end: ...@@ -521,7 +519,7 @@ end:
if (thd->locked_tables_mode && tables && lock_upgrade_done) if (thd->locked_tables_mode && tables && lock_upgrade_done)
mdl_ticket->downgrade_exclusive_lock(); mdl_ticket->downgrade_exclusive_lock();
if (need_start_waiting) if (thd->global_read_lock_protection > 0)
start_waiting_global_read_lock(thd); start_waiting_global_read_lock(thd);
if (!result) if (!result)
......
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