Bug #46402 falcon_transactions test crashes in TransactionManager::findOldestInActiveList()
Submitted: 27 Jul 2009 12:49 Modified: 26 May 2010 17:48
Reporter: Olav Sandstå Email Updates:
Status: Unsupported Impact on me:
None 
Category:MySQL Server: Falcon storage engine Severity:S1 (Critical)
Version:6.0.12-alpha OS:Microsoft Windows
Assigned to: Kevin Lewis CPU Architecture:Any
Triage: Triaged: D1 (Critical)

[27 Jul 2009 12:49] Olav Sandstå
Description:
When running the Random Query Generator test falcon_transactions a crash occured in TransactionManager::findOldestInActiveList().

The call stack looks like this:

mysqld.exe!TransactionManager::findOldestInActiveList()[transactionmanager.cpp:95]
mysqld.exe!Transaction::thawAll()[transaction.cpp:1619]
mysqld.exe!Transaction::fullyCommitted()[transaction.cpp:1481]
mysqld.exe!SerialLogTransaction::commit()[seriallogtransaction.cpp:102]
mysqld.exe!Gopher::gopherThread()[gopher.cpp:73]
mysqld.exe!Thread::thread()[thread.cpp:167]
mysqld.exe!Thread::thread()[thread.cpp:147]

How to repeat:
This crash occured when running the RQG test falcon_transactions. It has also been seen for some of the other RQG tests
[27 Jul 2009 12:53] Olav Sandstå
This crash was probably introduced by the following push:

  http://lists.mysql.com/commits/78794

since this includes a new call to TranactionManager::findOldestInActiveList()
[27 Jul 2009 13:02] Olav Sandstå
This might crash might be caused by TransactionManager::findOldestInActiveList() being called without having a lock on the TranactionManager's active transaction list. Calling Transactionmanager::findOldestInActiveList() requires that the caller has at least a shared lock on the activeTransasctions list:

TransId TransactionManager::findOldestInActiveList() const
{
	// Find the transaction id of the oldest active transaction in the 
	// active transaction list. If the list is empty, the
	// latest allocated transaction id will be returned.
	// This method assumes that the caller has set at least a shared lock
	// on the active list.

This method was originally mostly used by Transaction::commit() when cleaning (purging) old committed transaction objects. In that situation Transaction::committ() already had an exclusive lock on the active transaction list so it made sense (performance vise) to not have any extra locking inside Transaction::findOldestInActiveList() to keep it as in-expensive as possible.

I do not think this method is currently used as part of Transaction::commit() (but the change might be undone?).
[27 Jul 2009 13:13] Kevin Lewis
Woops, this was my fault,.  I neglected to lock Transaction::activeTransactions before making a call to Transaction::findOldestInActiveList() like the other two places it was called.  The linked list obviously changed while it was being traversed.  Patch will be available soon.
[27 Jul 2009 15:37] Bugs System
A patch for this bug has been committed. After review, it may
be pushed to the relevant source trees for release in the next
version. You can access the patch from:

  http://lists.mysql.com/commits/79348

2759 Kevin Lewis	2009-07-27
      Bug #46402 - Protect Transaction::findOldestInActiveList() with a shared lock on Transaction::activeTransactions.