From: Andrei Elkin Date: November 21 2012 7:49am Subject: bzr push into mysql-5.6 branch (andrei.elkin:4642 to 4643) Bug#15867985 List-Archive: http://lists.mysql.com/commits/145339 X-Bug: 15867985 Message-Id: <201211210749.qAL7nssZ027404@mysql1000.dsl.inet.fi> MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit 4643 Andrei Elkin 2012-11-21 Bug#15867985 STRESS TESTS CAUSES DBUG SERVER TO EXIT WITH SIGNAL 6 When run with --mysqld=--binlog-order-commits=1 (default) mysqld executes a piece of BGC logics that tames a group of threads in a way that they give up their commit execution path to a leader thread. This preemption appeared to have a specific to DBUG_ON build flaw in that the leader and a follower thread could modify a thread-specific area concurrently which led to a segfault. Fixed with refining the preemption logics to be thread-safe for the DBUG_ON build. modified: sql/binlog.cc sql/binlog.h sql/sql_class.h 4642 Sunny Bains 2012-11-21 Bug#14787508 - ASSERT TRX INTR || BUF_FLUSH_GET_DIRTY_PAGES_COUNT(ID) == 0 FLUSH TABLES FOR EXP There was a race in the purge stop code. If thread T1 changed the state to PURGE_STATE_STOP and before purge was suspended (purge_sys->running == false) if another thread T2 also tried to stop purge it would see that the purge sys state was stopped and it would proceed. The fix is for T2 to wait for the purge_sys->running to be set to false. Approved by Jimmy Yang over IM. modified: storage/innobase/include/trx0purge.h storage/innobase/srv/srv0srv.cc storage/innobase/trx/trx0purge.cc === modified file 'sql/binlog.cc' --- a/sql/binlog.cc revid:sunny.bains@stripped +++ b/sql/binlog.cc revid:andrei.elkin@stripped @@ -1334,6 +1334,16 @@ Stage_manager::enroll_for(StageID stage, if (!leader) { mysql_mutex_lock(&m_lock_done); +#ifndef DBUG_OFF + /* + Leader can be awaiting all-clear to preempt follower's execution. + With setting the status the follower ensures it won't execute anything + including thread-specific code. + */ + thd->transaction.flags.ready_preempt= 1; + if (leader_await_preempt_status) + mysql_cond_signal(&m_cond_preempt); +#endif while (thd->transaction.flags.pending) mysql_cond_wait(&m_cond_done, &m_lock_done); mysql_mutex_unlock(&m_lock_done); @@ -1361,6 +1371,22 @@ THD *Stage_manager::Mutex_queue::fetch_a DBUG_RETURN(result); } +#ifndef DBUG_OFF +void Stage_manager::clear_preempt_status(THD *head) +{ + DBUG_ASSERT(head); + + mysql_mutex_lock(&m_lock_done); + while(!head->transaction.flags.ready_preempt) + { + leader_await_preempt_status= true; + mysql_cond_wait(&m_cond_preempt, &m_lock_done); + } + leader_await_preempt_status= false; + mysql_mutex_unlock(&m_lock_done); +} +#endif + /** Write a rollback record of the transaction to the binary log. @@ -6009,6 +6035,9 @@ MYSQL_BIN_LOG::process_commit_stage_queu { mysql_mutex_assert_owner(&LOCK_commit); Thread_excursion excursion(thd); +#ifndef DBUG_OFF + thd->transaction.flags.ready_preempt= 1; // formality by the leader +#endif for (THD *head= first ; head ; head = head->next_to_commit) { DBUG_PRINT("debug", ("Thread ID: %lu, commit_error: %d, flags.pending: %s", @@ -6022,6 +6051,9 @@ MYSQL_BIN_LOG::process_commit_stage_queu If flush succeeded, attach to the session and commit it in the engines. */ +#ifndef DBUG_OFF + stage_manager.clear_preempt_status(head); +#endif if (flush_error != 0) head->commit_error= flush_error; else if (int error= excursion.attach_to(head)) @@ -6031,6 +6063,9 @@ MYSQL_BIN_LOG::process_commit_stage_queu bool all= head->transaction.flags.real_commit; if (head->transaction.flags.commit_low) { + /* head is parked to have exited append() */ + DBUG_ASSERT(head->transaction.flags.ready_preempt); + if (int error= ha_commit_low(head, all)) head->commit_error= error; else if (head->transaction.flags.xid_written) @@ -6269,6 +6304,17 @@ int MYSQL_BIN_LOG::ordered_commit(THD *t thd->transaction.flags.real_commit= all; thd->transaction.flags.xid_written= false; thd->transaction.flags.commit_low= !skip_commit; +#ifndef DBUG_OFF + /* + The group commit Leader may have to wait for follower whose transaction + is not ready to be preempted. Initially the status is pessimistic. + Preemption guarding logics is necessary only when DBUG_ON is set. + It won't be required for the dbug-off case as long as the follower won't + execute any thread-specific write access code in this method, which is + the case as of current. + */ + thd->transaction.flags.ready_preempt= 0; +#endif DBUG_PRINT("enter", ("flags.pending: %s, commit_error: %d, thread_id: %lu", YESNO(thd->transaction.flags.pending), === modified file 'sql/binlog.h' --- a/sql/binlog.h revid:sunny.bains@stripped +++ b/sql/binlog.h revid:andrei.elkin@stripped @@ -119,6 +119,10 @@ public: { mysql_mutex_init(key_LOCK_done, &m_lock_done, MY_MUTEX_INIT_FAST); mysql_cond_init(key_COND_done, &m_cond_done, NULL); +#ifndef DBUG_OFF + /* reuse key_COND_done 'cos a new PSI object would be wasteful in DBUG_ON */ + mysql_cond_init(key_COND_done, &m_cond_preempt, NULL); +#endif m_queue[FLUSH_STAGE].init( #ifdef HAVE_PSI_INTERFACE key_LOCK_flush_queue @@ -155,6 +159,7 @@ public: If wait_if_follower is true the thread is not the stage leader, the thread will be wait for the queue to be processed by the leader before it returns. + In DBUG-ON version the follower marks is preempt status as ready. @param stage Stage identifier for the queue to append to. @param first Queue to append. @@ -172,6 +177,17 @@ public: return m_queue[stage].pop_front(); } +#ifndef DBUG_OFF + /** + The method ensures the follower's execution path can be preempted + by the leader's thread. + Preempt status of @c head follower is checked to engange the leader + into waiting when set. + + @param head THD* of a follower thread + */ + void clear_preempt_status(THD *head); +#endif /** Fetch the entire queue and empty it. @@ -206,6 +222,13 @@ private: /** Mutex used for the condition variable above */ mysql_mutex_t m_lock_done; +#ifndef DBUG_OFF + /** Flag is set by Leader when it starts waiting for follower's all-clear */ + bool leader_await_preempt_status; + + /** Condition variable to indicate a follower started waiting for commit */ + mysql_cond_t m_cond_preempt; +#endif }; === modified file 'sql/sql_class.h' --- a/sql/sql_class.h revid:sunny.bains@stripped +++ b/sql/sql_class.h revid:andrei.elkin@stripped @@ -2352,6 +2352,9 @@ public: bool xid_written:1; // The session wrote an XID bool real_commit:1; // Is this a "real" commit? bool commit_low:1; // see MYSQL_BIN_LOG::ordered_commit +#ifndef DBUG_OFF + bool ready_preempt:1; // internal in MYSQL_BIN_LOG::ordered_commit +#endif } flags; void cleanup() No bundle (reason: useless for push emails).