List:Commits« Previous MessageNext Message »
From:Andrei Elkin Date:November 21 2012 7:49am
Subject:bzr push into mysql-5.6 branch (andrei.elkin:4642 to 4643) Bug#15867985
View as plain text  
 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).
Thread
bzr push into mysql-5.6 branch (andrei.elkin:4642 to 4643) Bug#15867985Andrei Elkin21 Nov