Commit 6fe99e2d authored by Marko Mäkelä's avatar Marko Mäkelä

Bug 12323643 - CLEAN UP THE INNODB THREAD SHUTDOWN AND ASSERTIONS (WL#5136)

Remove most references to thread id in InnoDB. Three references
remain: the current holder of a mutex, and the current x-lock holder
of a rw-lock, and some references in UNIV_SYNC_DEBUG checks. This
allows MySQL to change the thread associated to a client connection.

Tighten the UNIV_SYNC_DEBUG checks, trying to ensure that no InnoDB
mutex or x-lock is being held when returning control to MySQL. The
only semaphore that may be held is the btr_search_latch in shared mode.

sync_thread_levels_empty_except_dict(): A wrapper for
sync_thread_levels_empty_gen(TRUE).

sync_thread_levels_nonempty_trx(): Check that the current thread is
not holding any InnoDB semaphores, except btr_search_latch if
trx->has_search_latch.

sync_thread_levels_empty(): Unused function; remove.

trx_t: Remove mysql_thread_id and mysql_process_no.

srv_slot_t: Remove id and handle.

row_search_for_mysql(), srv_conc_enter_innodb(),
srv_conc_force_enter_innodb(), srv_conc_force_exit_innodb(),
srv_conc_exit_innodb(), srv_suspend_mysql_thread: Assert
!sync_thread_levels_nonempty_trx().

rb:634 approved by Sunny Bains
parent d8ddf751
......@@ -1716,7 +1716,7 @@ buf_flush_batch(
ut_ad(flush_type == BUF_FLUSH_LRU || flush_type == BUF_FLUSH_LIST);
#ifdef UNIV_SYNC_DEBUG
ut_ad((flush_type != BUF_FLUSH_LIST)
|| sync_thread_levels_empty_gen(TRUE));
|| sync_thread_levels_empty_except_dict());
#endif /* UNIV_SYNC_DEBUG */
buf_pool_mutex_enter(buf_pool);
......
......@@ -435,7 +435,7 @@ log_free_check(void)
{
#ifdef UNIV_SYNC_DEBUG
ut_ad(sync_thread_levels_empty_gen(TRUE));
ut_ad(sync_thread_levels_empty_except_dict());
#endif /* UNIV_SYNC_DEBUG */
if (log_sys->check_flush_or_checkpoint) {
......
......@@ -413,13 +413,6 @@ sync_thread_reset_level(
/*====================*/
void* latch); /*!< in: pointer to a mutex or an rw-lock */
/******************************************************************//**
Checks that the level array for the current thread is empty.
@return TRUE if empty */
UNIV_INTERN
ibool
sync_thread_levels_empty(void);
/*==========================*/
/******************************************************************//**
Checks if the level array for the current thread contains a
mutex or rw-latch at the specified level.
@return a matching latch, or NULL if not found */
......@@ -430,17 +423,33 @@ sync_thread_levels_contains(
ulint level); /*!< in: latching order level
(SYNC_DICT, ...)*/
/******************************************************************//**
Checks if the level array for the current thread is empty.
Checks that the level array for the current thread is empty.
@return a latch, or NULL if empty except the exceptions specified below */
UNIV_INTERN
void*
sync_thread_levels_nonempty_gen(
/*============================*/
ibool dict_mutex_allowed); /*!< in: TRUE if dictionary mutex is
allowed to be owned by the thread,
also purge_is_running mutex is
allowed */
#define sync_thread_levels_empty_gen(d) (!sync_thread_levels_nonempty_gen(d))
ibool dict_mutex_allowed) /*!< in: TRUE if dictionary mutex is
allowed to be owned by the thread */
__attribute__((warn_unused_result));
/******************************************************************//**
Checks if the level array for the current thread is empty,
except for data dictionary latches. */
#define sync_thread_levels_empty_except_dict() \
(!sync_thread_levels_nonempty_gen(TRUE))
/******************************************************************//**
Checks if the level array for the current thread is empty,
except for the btr_search_latch.
@return a latch, or NULL if empty except the exceptions specified below */
UNIV_INTERN
void*
sync_thread_levels_nonempty_trx(
/*============================*/
ibool has_search_latch)
/*!< in: TRUE if and only if the thread
is supposed to hold btr_search_latch */
__attribute__((warn_unused_result));
/******************************************************************//**
Gets the debug information for a reserved mutex. */
UNIV_INTERN
......
......@@ -569,11 +569,6 @@ struct trx_struct{
ib_int64_t mysql_log_offset;/* if MySQL binlog is used, this field
contains the end offset of the binlog
entry */
os_thread_id_t mysql_thread_id;/* id of the MySQL thread associated
with this transaction object */
ulint mysql_process_no;/* since in Linux, 'top' reports
process id's and not thread id's, we
store the process number too */
/*------------------------------*/
ulint n_mysql_tables_in_use; /* number of Innobase tables
used in the processing of the current
......
......@@ -1929,7 +1929,6 @@ row_merge_lock_table(
sel_node_t* node;
ut_ad(trx);
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
ut_ad(mode == LOCK_X || mode == LOCK_S);
heap = mem_heap_create(512);
......@@ -2390,7 +2389,6 @@ row_merge_rename_tables(
pars_info_t* info;
char old_name[MAX_FULL_NAME_LEN + 1];
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
ut_ad(old_table != new_table);
ut_ad(mutex_own(&dict_sys->mutex));
......
......@@ -976,7 +976,6 @@ row_lock_table_autoinc_for_mysql(
ibool was_lock_wait;
ut_ad(trx);
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
/* If we already hold an AUTOINC lock on the table then do nothing.
Note: We peek at the value of the current owner without acquiring
......@@ -1056,7 +1055,6 @@ row_lock_table_for_mysql(
ibool was_lock_wait;
ut_ad(trx);
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
trx->op_info = "setting table lock";
......@@ -1130,7 +1128,6 @@ row_insert_for_mysql(
ins_node_t* node = prebuilt->ins_node;
ut_ad(trx);
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
if (prebuilt->table->ibd_file_missing) {
ut_print_timestamp(stderr);
......@@ -1364,7 +1361,6 @@ row_update_for_mysql(
trx_t* trx = prebuilt->trx;
ut_ad(prebuilt && trx);
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
UT_NOT_USED(mysql_rec);
if (prebuilt->table->ibd_file_missing) {
......@@ -1532,7 +1528,6 @@ row_unlock_for_mysql(
trx_t* trx = prebuilt->trx;
ut_ad(prebuilt && trx);
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
if (UNIV_UNLIKELY
(!srv_locks_unsafe_for_binlog
......@@ -1834,7 +1829,6 @@ row_create_table_for_mysql(
ulint table_name_len;
ulint err;
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
#ifdef UNIV_SYNC_DEBUG
ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_EX));
#endif /* UNIV_SYNC_DEBUG */
......@@ -2008,7 +2002,6 @@ row_create_index_for_mysql(
ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_EX));
#endif /* UNIV_SYNC_DEBUG */
ut_ad(mutex_own(&(dict_sys->mutex)));
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
trx->op_info = "creating index";
......@@ -2411,8 +2404,6 @@ row_discard_tablespace_for_mysql(
table->n_foreign_key_checks_running > 0, we do not allow the
discard. We also reserve the data dictionary latch. */
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
trx->op_info = "discarding tablespace";
trx_start_if_not_started(trx);
......@@ -2571,8 +2562,6 @@ row_import_tablespace_for_mysql(
ib_uint64_t current_lsn;
ulint err = DB_SUCCESS;
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
trx_start_if_not_started(trx);
trx->op_info = "importing tablespace";
......@@ -2756,7 +2745,6 @@ row_truncate_table_for_mysql(
redo log records on the truncated tablespace, we will assign
a new tablespace identifier to the truncated tablespace. */
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
ut_ad(table);
if (srv_created_new_raw) {
......@@ -3607,7 +3595,6 @@ row_drop_database_for_mysql(
int err = DB_SUCCESS;
ulint namelen = strlen(name);
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
ut_a(name != NULL);
ut_a(name[namelen - 1] == '/');
......@@ -3777,7 +3764,6 @@ row_rename_table_for_mysql(
ibool old_is_tmp, new_is_tmp;
pars_info_t* info = NULL;
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
ut_a(old_name != NULL);
ut_a(new_name != NULL);
......
......@@ -1951,7 +1951,7 @@ row_sel(
mtr_commit(&mtr);
#ifdef UNIV_SYNC_DEBUG
ut_ad(sync_thread_levels_empty_gen(TRUE));
ut_ad(sync_thread_levels_empty_except_dict());
#endif /* UNIV_SYNC_DEBUG */
err = DB_SUCCESS;
goto func_exit;
......@@ -1971,7 +1971,7 @@ row_sel(
mtr_has_extra_clust_latch = FALSE;
#ifdef UNIV_SYNC_DEBUG
ut_ad(sync_thread_levels_empty_gen(TRUE));
ut_ad(sync_thread_levels_empty_except_dict());
#endif /* UNIV_SYNC_DEBUG */
goto table_loop;
......@@ -1988,7 +1988,7 @@ row_sel(
mtr_commit(&mtr);
#ifdef UNIV_SYNC_DEBUG
ut_ad(sync_thread_levels_empty_gen(TRUE));
ut_ad(sync_thread_levels_empty_except_dict());
#endif /* UNIV_SYNC_DEBUG */
func_exit:
......@@ -3370,7 +3370,6 @@ row_search_for_mysql(
rec_offs_init(offsets_);
ut_ad(index && pcur && search_tuple);
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
if (UNIV_UNLIKELY(prebuilt->table->ibd_file_missing)) {
ut_print_timestamp(stderr);
......@@ -3387,11 +3386,17 @@ row_search_for_mysql(
"InnoDB: how you can resolve the problem.\n",
prebuilt->table->name);
#ifdef UNIV_SYNC_DEBUG
ut_ad(!sync_thread_levels_nonempty_trx(trx->has_search_latch));
#endif /* UNIV_SYNC_DEBUG */
return(DB_ERROR);
}
if (UNIV_UNLIKELY(!prebuilt->index_usable)) {
#ifdef UNIV_SYNC_DEBUG
ut_ad(!sync_thread_levels_nonempty_trx(trx->has_search_latch));
#endif /* UNIV_SYNC_DEBUG */
return(DB_MISSING_HISTORY);
}
......@@ -4680,6 +4685,10 @@ row_search_for_mysql(
prebuilt->row_read_type = ROW_READ_TRY_SEMI_CONSISTENT;
}
}
#ifdef UNIV_SYNC_DEBUG
ut_ad(!sync_thread_levels_nonempty_trx(trx->has_search_latch));
#endif /* UNIV_SYNC_DEBUG */
return(err);
}
......
......@@ -687,8 +687,6 @@ Unix.*/
/* Thread slot in the thread table */
struct srv_slot_struct{
os_thread_id_t id; /*!< thread id */
os_thread_t handle; /*!< thread handle */
unsigned type:1; /*!< thread type: user, utility etc. */
unsigned in_use:1; /*!< TRUE if this slot is in use */
unsigned suspended:1; /*!< TRUE if the thread is waiting
......@@ -887,8 +885,6 @@ srv_table_reserve_slot(
slot->suspended = FALSE;
slot->type = type;
ut_ad(srv_slot_get_type(slot) == type);
slot->id = os_thread_get_curr_id();
slot->handle = os_thread_get_curr();
return(slot);
}
......@@ -907,7 +903,6 @@ srv_suspend_thread(
ut_ad(mutex_own(&kernel_mutex));
ut_ad(slot->in_use);
ut_ad(!slot->suspended);
ut_ad(slot->id == os_thread_get_curr_id());
if (srv_print_thread_releases) {
fprintf(stderr,
......@@ -962,10 +957,9 @@ srv_release_threads(
if (srv_print_thread_releases) {
fprintf(stderr,
"Releasing thread %lu type %lu"
"Releasing thread type %lu"
" from slot %lu\n",
(ulong) slot->id, (ulong) type,
(ulong) i);
(ulong) type, (ulong) i);
}
count++;
......@@ -1149,6 +1143,10 @@ srv_conc_enter_innodb(
srv_conc_slot_t* slot = NULL;
ulint i;
#ifdef UNIV_SYNC_DEBUG
ut_ad(!sync_thread_levels_nonempty_trx(trx->has_search_latch));
#endif /* UNIV_SYNC_DEBUG */
if (trx->mysql_thd != NULL
&& thd_is_replication_slave_thread(trx->mysql_thd)) {
......@@ -1272,6 +1270,10 @@ srv_conc_enter_innodb(
/* Go to wait for the event; when a thread leaves InnoDB it will
release this thread */
ut_ad(!trx->has_search_latch);
#ifdef UNIV_SYNC_DEBUG
ut_ad(!sync_thread_levels_nonempty_trx(trx->has_search_latch));
#endif /* UNIV_SYNC_DEBUG */
trx->op_info = "waiting in InnoDB queue";
thd_wait_begin(trx->mysql_thd, THD_WAIT_ROW_TABLE_LOCK);
......@@ -1307,6 +1309,10 @@ srv_conc_force_enter_innodb(
trx_t* trx) /*!< in: transaction object associated with the
thread */
{
#ifdef UNIV_SYNC_DEBUG
ut_ad(!sync_thread_levels_nonempty_trx(trx->has_search_latch));
#endif /* UNIV_SYNC_DEBUG */
if (UNIV_LIKELY(!srv_thread_concurrency)) {
return;
......@@ -1378,6 +1384,10 @@ srv_conc_force_exit_innodb(
if (slot != NULL) {
os_event_set(slot->event);
}
#ifdef UNIV_SYNC_DEBUG
ut_ad(!sync_thread_levels_nonempty_trx(trx->has_search_latch));
#endif /* UNIV_SYNC_DEBUG */
}
/*********************************************************************//**
......@@ -1389,6 +1399,10 @@ srv_conc_exit_innodb(
trx_t* trx) /*!< in: transaction object associated with the
thread */
{
#ifdef UNIV_SYNC_DEBUG
ut_ad(!sync_thread_levels_nonempty_trx(trx->has_search_latch));
#endif /* UNIV_SYNC_DEBUG */
if (trx->n_tickets_to_enter_innodb > 0) {
/* We will pretend the thread is still inside InnoDB though it
now leaves the InnoDB engine. In this way we save
......@@ -1505,10 +1519,9 @@ srv_table_reserve_slot_for_mysql(void)
slot = srv_mysql_table + i;
fprintf(stderr,
"Slot %lu: thread id %lu, type %lu,"
"Slot %lu: thread type %lu,"
" in use %lu, susp %lu, time %lu\n",
(ulong) i,
(ulong) os_thread_pf(slot->id),
(ulong) slot->type,
(ulong) slot->in_use,
(ulong) slot->suspended,
......@@ -1525,8 +1538,6 @@ srv_table_reserve_slot_for_mysql(void)
ut_a(slot->in_use == FALSE);
slot->in_use = TRUE;
slot->id = os_thread_get_curr_id();
slot->handle = os_thread_get_curr();
return(slot);
}
......@@ -1733,6 +1744,10 @@ srv_suspend_mysql_thread(
trx->error_state = DB_INTERRUPTED;
}
#ifdef UNIV_SYNC_DEBUG
ut_ad(!sync_thread_levels_nonempty_trx(trx->has_search_latch));
#endif /* UNIV_SYNC_DEBUG */
}
/********************************************************************//**
......
......@@ -189,12 +189,12 @@ UNIV_INTERN sync_array_t* sync_primary_wait_array;
/** This variable is set to TRUE when sync_init is called */
UNIV_INTERN ibool sync_initialized = FALSE;
#ifdef UNIV_SYNC_DEBUG
/** An acquired mutex or rw-lock and its level in the latching order */
typedef struct sync_level_struct sync_level_t;
/** Mutexes or rw-locks held by a thread */
typedef struct sync_thread_struct sync_thread_t;
#ifdef UNIV_SYNC_DEBUG
/** The latch levels currently owned by threads are stored in this data
structure; the size of this array is OS_THREAD_MAX_N */
......@@ -221,7 +221,6 @@ UNIV_INTERN mysql_pfs_key_t mutex_list_mutex_key;
#ifdef UNIV_SYNC_DEBUG
/** Latching order checks start when this is set TRUE */
UNIV_INTERN ibool sync_order_checks_on = FALSE;
#endif /* UNIV_SYNC_DEBUG */
/** Number of slots reserved for each OS thread in the sync level array */
static const ulint SYNC_THREAD_N_LEVELS = 10000;
......@@ -258,6 +257,7 @@ struct sync_level_struct{
the ordinal value of the next free
element */
};
#endif /* UNIV_SYNC_DEBUG */
/******************************************************************//**
Creates, or rather, initializes a mutex object in a specified memory
......@@ -1020,9 +1020,7 @@ void*
sync_thread_levels_nonempty_gen(
/*============================*/
ibool dict_mutex_allowed) /*!< in: TRUE if dictionary mutex is
allowed to be owned by the thread,
also purge_is_running mutex is
allowed */
allowed to be owned by the thread */
{
ulint i;
sync_arr_t* arr;
......@@ -1069,14 +1067,61 @@ sync_thread_levels_nonempty_gen(
}
/******************************************************************//**
Checks that the level array for the current thread is empty.
@return TRUE if empty */
Checks if the level array for the current thread is empty,
except for the btr_search_latch.
@return a latch, or NULL if empty except the exceptions specified below */
UNIV_INTERN
ibool
sync_thread_levels_empty(void)
/*==========================*/
void*
sync_thread_levels_nonempty_trx(
/*============================*/
ibool has_search_latch)
/*!< in: TRUE if and only if the thread
is supposed to hold btr_search_latch */
{
return(sync_thread_levels_empty_gen(FALSE));
ulint i;
sync_arr_t* arr;
sync_thread_t* thread_slot;
if (!sync_order_checks_on) {
return(NULL);
}
ut_a(!has_search_latch
|| sync_thread_levels_contains(SYNC_SEARCH_SYS));
mutex_enter(&sync_thread_mutex);
thread_slot = sync_thread_level_arrays_find_slot();
if (thread_slot == NULL) {
mutex_exit(&sync_thread_mutex);
return(NULL);
}
arr = thread_slot->levels;
for (i = 0; i < arr->n_elems; ++i) {
const sync_level_t* slot;
slot = &arr->elems[i];
if (slot->latch != NULL
&& (!has_search_latch
|| slot->level != SYNC_SEARCH_SYS)) {
mutex_exit(&sync_thread_mutex);
ut_error;
return(slot->latch);
}
}
mutex_exit(&sync_thread_mutex);
return(NULL);
}
/******************************************************************//**
......
......@@ -460,10 +460,6 @@ trx_rollback_active(
(ulong) rows_to_undo, unit);
mutex_exit(&kernel_mutex);
trx->mysql_thread_id = os_thread_get_curr_id();
trx->mysql_process_no = os_proc_get_number();
if (trx_get_dict_operation(trx) != TRX_DICT_OP_NONE) {
row_mysql_lock_data_dictionary(trx);
dictionary_locked = TRUE;
......
......@@ -214,10 +214,6 @@ trx_allocate_for_mysql(void)
mutex_exit(&kernel_mutex);
trx->mysql_thread_id = os_thread_get_curr_id();
trx->mysql_process_no = os_proc_get_number();
return(trx);
}
......@@ -1729,12 +1725,6 @@ trx_print(
fprintf(f, " state %lu", (ulong) trx->conc_state);
}
#ifdef UNIV_LINUX
fprintf(f, ", process no %lu", trx->mysql_process_no);
#endif
fprintf(f, ", OS thread id %lu",
(ulong) os_thread_pf(trx->mysql_thread_id));
if (*trx->op_info) {
putc(' ', f);
fputs(trx->op_info, f);
......
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