diff --git a/sql/lock.cc b/sql/lock.cc index 8d314c4ad19..420868a25c4 100644 --- a/sql/lock.cc +++ b/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; diff --git a/sql/sql_class.cc b/sql/sql_class.cc index d83b60810ab..d42a06b3811 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -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), diff --git a/sql/sql_class.h b/sql/sql_class.h index f4504bfed0d..889d7c5472b 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -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; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 06f29cd6039..211788768f9 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -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 diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 341d2d16757..5500bc286b8 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -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) diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index 015e0d4daa1..b3108cae3d9 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -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)