Commit 06d0d090 authored by Kristian Nielsen's avatar Kristian Nielsen

MDEV-6582: DEBUG_SYNC does not reset mysys_var->current_mutex, causes...

MDEV-6582: DEBUG_SYNC does not reset mysys_var->current_mutex, causes assertion "Trying to unlock mutex that wasn't locked"

The bug was in DEBUG_SYNC. When waiting, debug_sync_execute() temporarily sets
thd->mysys_var->current_mutex to a new value while waiting. However, if the
old value of current_mutex was NULL, it was not restored, current_mutex
remained set to the temporary value (debug_sync_global.ds_mutex).

This made possible the following race: Thread T1 goes to KILL thread T2. In
THD::awake(), T1 loads T2->mysys_var->current_mutex, it is set to ds_mutex, T1
locks this mutex.

Now T2 runs, it does ENTER_COND, it sets T2->mysys_var->current_mutex to
LOCK_wait_commit (for example).

Then T1 resumes, it reloads mysys_var->current_mutex, now it is set to
LOCK_wait_commit, T1 unlocks this mutex instead of the ds_mutex that it locked
previously.

This causes safe_mutex to assert with the message: "Trying to unlock mutex
LOCK_wait_commit that wasn't locked".

The fix is to ensure that DEBUG_SYNC also will restore
mysys_var->current_mutex in the case where the original value was NULL.
parent e79b7ca9
...@@ -1394,8 +1394,9 @@ static void debug_sync_execute(THD *thd, st_debug_sync_action *action) ...@@ -1394,8 +1394,9 @@ static void debug_sync_execute(THD *thd, st_debug_sync_action *action)
if (action->wait_for.length()) if (action->wait_for.length())
{ {
mysql_mutex_t *old_mutex; mysql_mutex_t *old_mutex= NULL;
mysql_cond_t *old_cond= NULL; mysql_cond_t *old_cond= NULL;
bool restore_current_mutex;
int error= 0; int error= 0;
struct timespec abstime; struct timespec abstime;
...@@ -1412,11 +1413,12 @@ static void debug_sync_execute(THD *thd, st_debug_sync_action *action) ...@@ -1412,11 +1413,12 @@ static void debug_sync_execute(THD *thd, st_debug_sync_action *action)
{ {
old_mutex= thd->mysys_var->current_mutex; old_mutex= thd->mysys_var->current_mutex;
old_cond= thd->mysys_var->current_cond; old_cond= thd->mysys_var->current_cond;
restore_current_mutex = true;
thd->mysys_var->current_mutex= &debug_sync_global.ds_mutex; thd->mysys_var->current_mutex= &debug_sync_global.ds_mutex;
thd->mysys_var->current_cond= &debug_sync_global.ds_cond; thd->mysys_var->current_cond= &debug_sync_global.ds_cond;
} }
else else
old_mutex= NULL; restore_current_mutex = false;
set_timespec(abstime, action->timeout); set_timespec(abstime, action->timeout);
DBUG_EXECUTE("debug_sync_exec", { DBUG_EXECUTE("debug_sync_exec", {
...@@ -1476,7 +1478,7 @@ static void debug_sync_execute(THD *thd, st_debug_sync_action *action) ...@@ -1476,7 +1478,7 @@ static void debug_sync_execute(THD *thd, st_debug_sync_action *action)
is locked. (See comment in THD::exit_cond().) is locked. (See comment in THD::exit_cond().)
*/ */
mysql_mutex_unlock(&debug_sync_global.ds_mutex); mysql_mutex_unlock(&debug_sync_global.ds_mutex);
if (old_mutex) if (restore_current_mutex)
{ {
mysql_mutex_lock(&thd->mysys_var->mutex); mysql_mutex_lock(&thd->mysys_var->mutex);
thd->mysys_var->current_mutex= old_mutex; thd->mysys_var->current_mutex= old_mutex;
......
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