[Webkit-unassigned] [Bug 27966] New: Race condition in the database code

bugzilla-daemon at webkit.org bugzilla-daemon at webkit.org
Mon Aug 3 19:38:40 PDT 2009


https://bugs.webkit.org/show_bug.cgi?id=27966

           Summary: Race condition in the database code
           Product: WebKit
           Version: 528+ (Nightly build)
          Platform: All
        OS/Version: All
            Status: UNCONFIRMED
          Severity: Normal
          Priority: P2
         Component: New Bugs
        AssignedTo: webkit-unassigned at lists.webkit.org
        ReportedBy: dumi at chromium.org


The WebCore DB code has a bug that can lead to a deadlock. Here are the
details:

1. The user creates 2 handles to the same DB on the same page, and executes a
transaction on each handle:
    db1 = openDatabase("Foo", "1.0", "Foo", 1000);
    db2 = openDatabase("Foo", "1.0", "Foo", 1000);
    db1.transaction(...);
    db2.transaction(...);

2. The main thread gets to db1.transaction() and schedules a task (#1) on the
DB thread that will call SQLTransaction::openTransactionAndPreflight().
3. The main thread gets to db2.transaction() and schedules a task (#2) on the
DB thread that will call SQLTransaction::openTransactionAndPreflight().
4. The DB thread executes SQLTransaction::openTransactionAndPreflight() for the
first transaction (db1). It calls m_sqliteTransaction->begin() which makes
SQLite acquire a lock on the DB file. Then the DB thread calls
m_database->scheduleTransactionCallback() (which schedules a task on the main
thread) and now the DB thread is done with task #1.
5. The DB thread moves on to task #2, which wants to run db2's transaction. The
DB thread tries to execute SQLTransaction::openTransactionAndPreflight() for
db2, and when it gets to m_sqliteTransaction->begin(), it blocks, because db1's
transaction has the lock on the DB file.
6. So now the DB thread cannot finish transaction #2 until transaction #1 is
done and releases the lock on the DB file, and it cannot move on with
transaction #1 because it is blocked trying to acquire a lock for transaction
#2.

Eventually, the call to m_sqliteDatabase for transaction #2 will fail (when
sqlite's busy timeout is reached), transaction #2 will fail, and the DB thread
will continue executing transaction #1, which should succeed.


Proposed solution: Add a "transaction coordinator" interface with 2 methods:
aquireLock(SQLTransaction*) and releaseLock(SQLTransaction*); and add a step
that calls TransactionCoordinator::aquireLock() before calling
SQLTransaction::openTransactionAndPreflight() (m_nextStep in SQLTransaction's
constructor would be set to this step), and another step that calls
TransactionCoordinator::releaseLock() that is called at the end of every
transaction.

Here's how the above example would work with these 2 steps:

1. The main thread gets to db1.transaction() and schedules task #1 on the DB
thread that will call TransactionCoordinator::aquireLock();
2. The main thread gets to db2.transaction() and schedules task #2 on the DB
thread that will call TransactionCoordinator::aquireLock();
3. The DB thread calls aquireLock() for db1's transaction, which returns 'true'
(success). The DB thread sets m_nextStep =
&SQLTransaction::openTransactionAndPreflight, and calls
db1->scheduleTransactionCallback() as it does for all other steps.
4. The DB thread calls aquireLock() for db2's transaction. The transaction
coordinator saves transaction #2 to the queue of pending transactions for the
given database, and returns 'false'. Since aquireLock() failed, the DB thread
does not do anything else related to transaction #2.
5. Eventually, transaction #1 finishes executing (the DB thread is no longer
blocked on other transactions), and releaseLock(transaction_1) is called. The
transaction coordinator checks the list of pending transactions for the given
database, and sees transaction #2 there. Before returning from releaseLock(),
the transaction coordinator calls db2->scheduleTransactionCallback() to
reschedule transaction #2.
6. Now the DB thread can acquire the lock for transaction #2 and run it without
causing any deadlocks.

Thoughts? Suggestions?

-- 
Configure bugmail: https://bugs.webkit.org/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are the assignee for the bug.



More information about the webkit-unassigned mailing list