mirror of
https://github.com/MariaDB/server.git
synced 2025-08-01 03:47:19 +03:00
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.
This commit is contained in:
93
sql/lock.cc
93
sql/lock.cc
@ -1098,6 +1098,19 @@ static volatile uint waiting_for_read_lock=0;
|
||||
#define GOT_GLOBAL_READ_LOCK 1
|
||||
#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)
|
||||
{
|
||||
DBUG_ENTER("lock_global_read_lock");
|
||||
@ -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)
|
||||
{
|
||||
uint tmp;
|
||||
@ -1190,6 +1213,25 @@ void unlock_global_read_lock(THD *thd)
|
||||
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 && \
|
||||
(is_not_commit || \
|
||||
global_read_lock_blocks_commit))
|
||||
@ -1201,6 +1243,16 @@ bool wait_if_global_read_lock(THD *thd, bool abort_on_refresh,
|
||||
bool result= 0, need_exit_cond;
|
||||
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
|
||||
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,
|
||||
result=1;
|
||||
}
|
||||
if (!abort_on_refresh && !result)
|
||||
{
|
||||
thd->global_read_lock_protection++;
|
||||
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)
|
||||
and if old_message is set
|
||||
@ -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)
|
||||
{
|
||||
bool tmp;
|
||||
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))
|
||||
DBUG_VOID_RETURN;
|
||||
(void) pthread_mutex_lock(&LOCK_global_read_lock);
|
||||
@ -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 error;
|
||||
|
@ -452,6 +452,7 @@ THD::THD()
|
||||
examined_row_count(0),
|
||||
warning_info(&main_warning_info),
|
||||
stmt_da(&main_da),
|
||||
global_read_lock_protection(0),
|
||||
global_read_lock(0),
|
||||
is_fatal_error(0),
|
||||
transaction_rollback_request(0),
|
||||
|
@ -1874,6 +1874,7 @@ public:
|
||||
ulong rand_saved_seed1, rand_saved_seed2;
|
||||
pthread_t real_id; /* For debugging */
|
||||
my_thread_id thread_id;
|
||||
uint global_read_lock_protection;// GRL protection count
|
||||
uint tmp_table, global_read_lock;
|
||||
uint server_status,open_options;
|
||||
enum enum_thread_type system_thread;
|
||||
|
@ -1762,7 +1762,6 @@ int
|
||||
mysql_execute_command(THD *thd)
|
||||
{
|
||||
int res= FALSE;
|
||||
bool need_start_waiting= FALSE; // have protection against global read lock
|
||||
int up_result= 0;
|
||||
LEX *lex= thd->lex;
|
||||
/* first SELECT_LEX (have special meaning for many of non-SELECTcommands) */
|
||||
@ -2039,7 +2038,7 @@ mysql_execute_command(THD *thd)
|
||||
break;
|
||||
|
||||
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;
|
||||
|
||||
res= execute_sqlcom_select(thd, all_tables);
|
||||
@ -2309,10 +2308,9 @@ case SQLCOM_PREPARE:
|
||||
read lock when it succeeds. This needs to be released by
|
||||
start_waiting_global_read_lock(). We protect the normal CREATE
|
||||
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 &&
|
||||
!(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
|
||||
if (!thd->locked_tables_mode && wait_if_global_read_lock(thd, 0, 1))
|
||||
{
|
||||
res= 1;
|
||||
goto end_with_restore_list;
|
||||
@ -2617,8 +2615,7 @@ end_with_restore_list:
|
||||
"INDEX DIRECTORY");
|
||||
create_info.data_file_name= create_info.index_file_name= NULL;
|
||||
|
||||
if (!thd->locked_tables_mode &&
|
||||
!(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
|
||||
if (!thd->locked_tables_mode && wait_if_global_read_lock(thd, 0, 1))
|
||||
{
|
||||
res= 1;
|
||||
break;
|
||||
@ -2852,8 +2849,7 @@ end_with_restore_list:
|
||||
DBUG_ASSERT(first_table == all_tables && first_table != 0);
|
||||
if (update_precheck(thd, all_tables))
|
||||
break;
|
||||
if (!thd->locked_tables_mode &&
|
||||
!(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
|
||||
if (!thd->locked_tables_mode && wait_if_global_read_lock(thd, 0, 1))
|
||||
goto error;
|
||||
DBUG_ASSERT(select_lex->offset_limit == 0);
|
||||
unit->set_limit(select_lex);
|
||||
@ -2891,7 +2887,7 @@ end_with_restore_list:
|
||||
*/
|
||||
if (!thd->locked_tables_mode &&
|
||||
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;
|
||||
|
||||
res= mysql_multi_update_prepare(thd);
|
||||
@ -2993,8 +2989,7 @@ end_with_restore_list:
|
||||
if ((res= insert_precheck(thd, all_tables)))
|
||||
break;
|
||||
|
||||
if (!thd->locked_tables_mode &&
|
||||
!(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
|
||||
if (!thd->locked_tables_mode && wait_if_global_read_lock(thd, 0, 1))
|
||||
{
|
||||
res= 1;
|
||||
break;
|
||||
@ -3033,8 +3028,7 @@ end_with_restore_list:
|
||||
|
||||
unit->set_limit(select_lex);
|
||||
|
||||
if (! thd->locked_tables_mode &&
|
||||
! (need_start_waiting= ! wait_if_global_read_lock(thd, 0, 1)))
|
||||
if (!thd->locked_tables_mode && wait_if_global_read_lock(thd, 0, 1))
|
||||
{
|
||||
res= 1;
|
||||
break;
|
||||
@ -3104,7 +3098,7 @@ end_with_restore_list:
|
||||
ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
|
||||
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;
|
||||
res= mysql_truncate(thd, first_table, 0);
|
||||
break;
|
||||
@ -3116,8 +3110,7 @@ end_with_restore_list:
|
||||
DBUG_ASSERT(select_lex->offset_limit == 0);
|
||||
unit->set_limit(select_lex);
|
||||
|
||||
if (!thd->locked_tables_mode &&
|
||||
!(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
|
||||
if (!thd->locked_tables_mode && wait_if_global_read_lock(thd, 0, 1))
|
||||
{
|
||||
res= 1;
|
||||
break;
|
||||
@ -3137,8 +3130,7 @@ end_with_restore_list:
|
||||
(TABLE_LIST *)thd->lex->auxiliary_table_list.first;
|
||||
multi_delete *del_result;
|
||||
|
||||
if (!thd->locked_tables_mode &&
|
||||
!(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
|
||||
if (!thd->locked_tables_mode && wait_if_global_read_lock(thd, 0, 1))
|
||||
{
|
||||
res= 1;
|
||||
break;
|
||||
@ -3282,8 +3274,7 @@ end_with_restore_list:
|
||||
if (check_one_table_access(thd, privilege, all_tables))
|
||||
goto error;
|
||||
|
||||
if (!thd->locked_tables_mode &&
|
||||
!(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
|
||||
if (!thd->locked_tables_mode && wait_if_global_read_lock(thd, 0, 1))
|
||||
goto error;
|
||||
|
||||
res= mysql_load(thd, lex->exchange, first_table, lex->field_list,
|
||||
@ -3357,7 +3348,7 @@ end_with_restore_list:
|
||||
FALSE, UINT_MAX, FALSE))
|
||||
goto error;
|
||||
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;
|
||||
|
||||
init_mdl_requests(all_tables);
|
||||
@ -4575,7 +4566,7 @@ error:
|
||||
res= TRUE;
|
||||
|
||||
finish:
|
||||
if (need_start_waiting)
|
||||
if (thd->global_read_lock_protection > 0)
|
||||
{
|
||||
/*
|
||||
Release the protection against the global read lock and wake
|
||||
|
@ -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,
|
||||
my_bool drop_temporary)
|
||||
{
|
||||
bool error= FALSE, need_start_waiting= FALSE;
|
||||
bool error;
|
||||
Drop_table_error_handler err_handler(thd->get_internal_handler());
|
||||
|
||||
DBUG_ENTER("mysql_rm_table");
|
||||
|
||||
/* mark for close and remove all cached entries */
|
||||
|
||||
if (!drop_temporary)
|
||||
{
|
||||
if (!thd->locked_tables_mode &&
|
||||
!(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
|
||||
if (!thd->locked_tables_mode && wait_if_global_read_lock(thd, 0, 1))
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
thd->pop_internal_handler();
|
||||
|
||||
|
||||
if (need_start_waiting)
|
||||
if (thd->global_read_lock_protection > 0)
|
||||
start_waiting_global_read_lock(thd);
|
||||
|
||||
if (error)
|
||||
|
@ -328,7 +328,6 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
|
||||
TABLE *table;
|
||||
bool result= TRUE;
|
||||
String stmt_query;
|
||||
bool need_start_waiting= FALSE;
|
||||
bool lock_upgrade_done= FALSE;
|
||||
MDL_ticket *mdl_ticket= NULL;
|
||||
|
||||
@ -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).
|
||||
*/
|
||||
if (!thd->locked_tables_mode &&
|
||||
!(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
|
||||
if (!thd->locked_tables_mode && wait_if_global_read_lock(thd, 0, 1))
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
if (!create)
|
||||
@ -521,7 +519,7 @@ end:
|
||||
if (thd->locked_tables_mode && tables && lock_upgrade_done)
|
||||
mdl_ticket->downgrade_exclusive_lock();
|
||||
|
||||
if (need_start_waiting)
|
||||
if (thd->global_read_lock_protection > 0)
|
||||
start_waiting_global_read_lock(thd);
|
||||
|
||||
if (!result)
|
||||
|
Reference in New Issue
Block a user