Bug #46402 falcon_transactions test crashes in TransactionManager::findOldestInActiveList()
Submitted: 27 Jul 2009 12:49 Modified: 26 May 2010 17:48
Reporter: Olav Sandstaa Email Updates:
Status: Unsupported
Category:Server: Falcon Severity:S1 (Critical)
Version:6.0.12-alpha OS:Microsoft Windows
Assigned to: Kevin Lewis Target Version:
Triage: Triaged: D1 (Critical)

[27 Jul 2009 12:49] Olav Sandstaa
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 Sandstaa
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 Sandstaa
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.