summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorunknown <knielsen@knielsen-hq.org>2014-02-09 00:56:18 +0100
committerunknown <knielsen@knielsen-hq.org>2014-02-09 00:56:18 +0100
commit07eaf6ea76900a724fc6b89e1c4ccd0713c6a174 (patch)
treeb78fd6293a8aac3026e2b47436e1fe9e08e78c7f /sql
parent76e929a92e6fb06b649ed9f199484b64946b6152 (diff)
downloadmariadb-git-07eaf6ea76900a724fc6b89e1c4ccd0713c6a174.tar.gz
MDEV-5636: Deadlock in RESET MASTER
The problem is a deadlock between MYSQL_BIN_LOG::reset_logs() and MYSQL_BIN_LOG::mark_xid_done(). The former takes LOCK_log and waits for the latter to complete. But the latter also tries to take LOCK_log; this can lead to a deadlock. There was already code that tries to deal with this, with the flag reset_master_pending. However, there was still a small opportunity for deadlock, when an previous mark_xid_done() is still running when reset_logs() is called and is at the precise point where it first releases LOCK_xid_list and then re-aquires both LOCK_log and LOCK_xid_list. Solve by setting reset_master_pending in reset_logs() before taking LOCK_log. And also count how many invocations of LOCK_xid_list are in the progress of releasing and re-aquiring locks, and in reset_logs() wait for that number to drop to zero after setting reset_master_pending and before taking LOCK_log.
Diffstat (limited to 'sql')
-rw-r--r--sql/log.cc35
-rw-r--r--sql/log.h1
2 files changed, 24 insertions, 12 deletions
diff --git a/sql/log.cc b/sql/log.cc
index c5cdc2cccc5..6fead95a4b1 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -2933,7 +2933,7 @@ const char *MYSQL_LOG::generate_name(const char *log_name,
MYSQL_BIN_LOG::MYSQL_BIN_LOG(uint *sync_period)
- :reset_master_pending(false),
+ :reset_master_pending(false), mark_xid_done_waiting(0),
bytes_written(0), file_id(1), open_count(1),
group_commit_queue(0), group_commit_queue_busy(FALSE),
num_commits(0), num_group_commits(0),
@@ -3749,22 +3749,11 @@ bool MYSQL_BIN_LOG::reset_logs(THD* thd, bool create_new_log,
const char* save_name;
DBUG_ENTER("reset_logs");
- if (thd)
- ha_reset_logs(thd);
- /*
- We need to get both locks to be sure that no one is trying to
- write to the index log file.
- */
- mysql_mutex_lock(&LOCK_log);
- mysql_mutex_lock(&LOCK_index);
-
if (!is_relay_log)
{
if (init_state && !is_empty_state())
{
my_error(ER_BINLOG_MUST_BE_EMPTY, MYF(0));
- mysql_mutex_unlock(&LOCK_index);
- mysql_mutex_unlock(&LOCK_log);
DBUG_RETURN(1);
}
@@ -3773,11 +3762,29 @@ bool MYSQL_BIN_LOG::reset_logs(THD* thd, bool create_new_log,
This ensures that a binlog checkpoint will not try to write binlog
checkpoint events, which would be useless (as we are deleting the binlog
anyway) and could deadlock, as we are holding LOCK_log.
+
+ Wait for any mark_xid_done() calls that might be already running to
+ complete (mark_xid_done_waiting counter to drop to zero); we need to
+ do this before we take the LOCK_log to not deadlock.
*/
mysql_mutex_lock(&LOCK_xid_list);
reset_master_pending= true;
+ while (mark_xid_done_waiting > 0)
+ mysql_cond_wait(&COND_xid_list, &LOCK_xid_list);
mysql_mutex_unlock(&LOCK_xid_list);
+ }
+ if (thd)
+ ha_reset_logs(thd);
+ /*
+ We need to get both locks to be sure that no one is trying to
+ write to the index log file.
+ */
+ mysql_mutex_lock(&LOCK_log);
+ mysql_mutex_lock(&LOCK_index);
+
+ if (!is_relay_log)
+ {
/*
We are going to nuke all binary log files.
Without binlog, we cannot XA recover prepared-but-not-committed
@@ -8834,9 +8841,13 @@ TC_LOG_BINLOG::mark_xid_done(ulong binlog_id, bool write_checkpoint)
locks in the opposite order.
*/
+ ++mark_xid_done_waiting;
mysql_mutex_unlock(&LOCK_xid_list);
mysql_mutex_lock(&LOCK_log);
mysql_mutex_lock(&LOCK_xid_list);
+ --mark_xid_done_waiting;
+ if (unlikely(reset_master_pending))
+ mysql_cond_signal(&COND_xid_list);
/* We need to reload current_binlog_id due to release/re-take of lock. */
current= current_binlog_id;
diff --git a/sql/log.h b/sql/log.h
index 45381152d97..4249246277f 100644
--- a/sql/log.h
+++ b/sql/log.h
@@ -471,6 +471,7 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
checkpoint arrives - when all have arrived, RESET MASTER will complete.
*/
bool reset_master_pending;
+ ulong mark_xid_done_waiting;
/* LOCK_log and LOCK_index are inited by init_pthread_objects() */
mysql_mutex_t LOCK_index;