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 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)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
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)
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
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)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
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()
|
|||||||
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:
|
|||||||
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
|
|||||||
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)
|
|||||||
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:
|
|||||||
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:
|
|||||||
"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:
|
|||||||
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:
|
|||||||
*/
|
*/
|
||||||
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:
|
|||||||
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:
|
|||||||
|
|
||||||
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:
|
|||||||
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:
|
|||||||
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:
|
|||||||
(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:
|
|||||||
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:
|
|||||||
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:
|
|||||||
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,
|
|||||||
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,
|
|||||||
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)
|
|||||||
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)
|
|||||||
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:
|
|||||||
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)
|
||||||
|
Reference in New Issue
Block a user