Title: [143271] trunk/Source/WebCore
Revision
143271
Author
[email protected]
Date
2013-02-18 15:42:24 -0800 (Mon, 18 Feb 2013)

Log Message

Fix SQLTransaction leak.
https://bugs.webkit.org/show_bug.cgi?id=110052.

Reviewed by Geoffrey Garen.

With https://bugs.webkit.org/show_bug.cgi?id=104750, there is now a circular
reference between SQLTransaction and its backend. The clean up process needs
to be fixed to explicitly break this reference cycle.

The 5 phases of the SQLTransaction (and backend) phases and their clean up
actions are:

Phase 1. After Birth, before scheduling
- During shutdown, DatabaseThread::databaseThread() calls
  DatabaseBackendAsync::close(). DatabaseBackendAsync::close() iterates
  DatabaseBackendAsync::m_transactionQueue and calls
  SQLtransactionBackend::notifyDatabaseThreadIsShuttingDown() on each
  transaction there.

Phase 2. After scheduling, before state AcquireLock
- ~DatabaseTask() calls SQLtransactionBackend's
  notifyDatabaseThreadIsShuttingDown().

Phase 3. After state AcquireLock, before "lockAcquired"
- During shutdown, DatabaseThread::databaseThread() calls
  SQLTransactionCoordinator::shutdown(), which calls
  SQLTransactionBackend::notifyDatabaseThreadIsShuttingDown().

Phase 4: After "lockAcquired", before state CleanupAndTerminate
- Same as Phase 3.

Phase 5: After state CleanupAndTerminate
- state CleanupAndTerminate calls SQLTransactionBackend::doCleanup().

See comment at the top of SQLTransactionBackend.cpp for more details.

Other supporting changes:
- Moved Database::close() to the DatabaseBackendAsync.
- Moved the "if already cleaned up" check from SQLTransactionBackend's
  notifyDatabaseThreadIsShuttingDown() to doCleanup().
- Added a check to prevent SQLTransactionCoordinator's releaseLock()
  from running when it's shutting down.

No new tests.

* Modules/webdatabase/Database.cpp:
* Modules/webdatabase/Database.h:
* Modules/webdatabase/DatabaseBackendAsync.cpp:
(WebCore::DatabaseBackendAsync::close): Move from Database.cpp.
* Modules/webdatabase/DatabaseBackendAsync.h:
* Modules/webdatabase/DatabaseTask.cpp:
(WebCore::DatabaseBackendAsync::DatabaseTransactionTask::DatabaseTransactionTask):
(WebCore::DatabaseBackendAsync::DatabaseTransactionTask::~DatabaseTransactionTask):
(WebCore::DatabaseBackendAsync::DatabaseTransactionTask::doPerformTask):
* Modules/webdatabase/DatabaseTask.h:
(DatabaseBackendAsync::DatabaseTransactionTask):
* Modules/webdatabase/DatabaseThread.cpp:
(WebCore::DatabaseThread::databaseThread):
* Modules/webdatabase/SQLTransactionBackend.cpp:
(WebCore::SQLTransactionBackend::doCleanup):
(WebCore::SQLTransactionBackend::notifyDatabaseThreadIsShuttingDown):
(WebCore::SQLTransactionBackend::cleanupAndTerminate):
* Modules/webdatabase/SQLTransactionCoordinator.cpp:
(WebCore::SQLTransactionCoordinator::SQLTransactionCoordinator):
(WebCore::SQLTransactionCoordinator::acquireLock):
(WebCore::SQLTransactionCoordinator::releaseLock):
(WebCore::SQLTransactionCoordinator::shutdown):
* Modules/webdatabase/SQLTransactionCoordinator.h:
(SQLTransactionCoordinator):

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (143270 => 143271)


--- trunk/Source/WebCore/ChangeLog	2013-02-18 23:29:56 UTC (rev 143270)
+++ trunk/Source/WebCore/ChangeLog	2013-02-18 23:42:24 UTC (rev 143271)
@@ -1,3 +1,75 @@
+2013-02-17  Mark Lam  <[email protected]>
+
+        Fix SQLTransaction leak.
+        https://bugs.webkit.org/show_bug.cgi?id=110052.
+
+        Reviewed by Geoffrey Garen.
+
+        With https://bugs.webkit.org/show_bug.cgi?id=104750, there is now a circular
+        reference between SQLTransaction and its backend. The clean up process needs
+        to be fixed to explicitly break this reference cycle.
+
+        The 5 phases of the SQLTransaction (and backend) phases and their clean up
+        actions are:
+
+        Phase 1. After Birth, before scheduling
+        - During shutdown, DatabaseThread::databaseThread() calls
+          DatabaseBackendAsync::close(). DatabaseBackendAsync::close() iterates
+          DatabaseBackendAsync::m_transactionQueue and calls
+          SQLtransactionBackend::notifyDatabaseThreadIsShuttingDown() on each
+          transaction there.
+
+        Phase 2. After scheduling, before state AcquireLock
+        - ~DatabaseTask() calls SQLtransactionBackend's
+          notifyDatabaseThreadIsShuttingDown().
+
+        Phase 3. After state AcquireLock, before "lockAcquired"
+        - During shutdown, DatabaseThread::databaseThread() calls
+          SQLTransactionCoordinator::shutdown(), which calls
+          SQLTransactionBackend::notifyDatabaseThreadIsShuttingDown().
+
+        Phase 4: After "lockAcquired", before state CleanupAndTerminate
+        - Same as Phase 3.
+
+        Phase 5: After state CleanupAndTerminate
+        - state CleanupAndTerminate calls SQLTransactionBackend::doCleanup().
+
+        See comment at the top of SQLTransactionBackend.cpp for more details.
+
+        Other supporting changes:
+        - Moved Database::close() to the DatabaseBackendAsync.
+        - Moved the "if already cleaned up" check from SQLTransactionBackend's
+          notifyDatabaseThreadIsShuttingDown() to doCleanup().
+        - Added a check to prevent SQLTransactionCoordinator's releaseLock()
+          from running when it's shutting down.
+
+        No new tests.
+
+        * Modules/webdatabase/Database.cpp:
+        * Modules/webdatabase/Database.h:
+        * Modules/webdatabase/DatabaseBackendAsync.cpp:
+        (WebCore::DatabaseBackendAsync::close): Move from Database.cpp.
+        * Modules/webdatabase/DatabaseBackendAsync.h:
+        * Modules/webdatabase/DatabaseTask.cpp:
+        (WebCore::DatabaseBackendAsync::DatabaseTransactionTask::DatabaseTransactionTask):
+        (WebCore::DatabaseBackendAsync::DatabaseTransactionTask::~DatabaseTransactionTask):
+        (WebCore::DatabaseBackendAsync::DatabaseTransactionTask::doPerformTask):
+        * Modules/webdatabase/DatabaseTask.h:
+        (DatabaseBackendAsync::DatabaseTransactionTask):
+        * Modules/webdatabase/DatabaseThread.cpp:
+        (WebCore::DatabaseThread::databaseThread):
+        * Modules/webdatabase/SQLTransactionBackend.cpp:
+        (WebCore::SQLTransactionBackend::doCleanup):
+        (WebCore::SQLTransactionBackend::notifyDatabaseThreadIsShuttingDown):
+        (WebCore::SQLTransactionBackend::cleanupAndTerminate):
+        * Modules/webdatabase/SQLTransactionCoordinator.cpp:
+        (WebCore::SQLTransactionCoordinator::SQLTransactionCoordinator):
+        (WebCore::SQLTransactionCoordinator::acquireLock):
+        (WebCore::SQLTransactionCoordinator::releaseLock):
+        (WebCore::SQLTransactionCoordinator::shutdown):
+        * Modules/webdatabase/SQLTransactionCoordinator.h:
+        (SQLTransactionCoordinator):
+
 2013-02-18  David Kilzer  <[email protected]>
 
         Fix WebCore Xcode project

Modified: trunk/Source/WebCore/Modules/webdatabase/Database.cpp (143270 => 143271)


--- trunk/Source/WebCore/Modules/webdatabase/Database.cpp	2013-02-18 23:29:56 UTC (rev 143270)
+++ trunk/Source/WebCore/Modules/webdatabase/Database.cpp	2013-02-18 23:42:24 UTC (rev 143271)
@@ -159,19 +159,6 @@
     synchronizer.waitForTaskCompletion();
 }
 
-void Database::close()
-{
-    ASSERT(databaseContext()->databaseThread());
-    ASSERT(currentThread() == databaseContext()->databaseThread()->getThreadID());
-
-    closeDatabase();
-
-    // Must ref() before calling databaseThread()->recordDatabaseClosed().
-    RefPtr<Database> protect = this;
-    databaseContext()->databaseThread()->recordDatabaseClosed(this);
-    databaseContext()->databaseThread()->unscheduleDatabaseTasks(this);
-}
-
 void Database::closeImmediately()
 {
     ASSERT(m_scriptExecutionContext->isContextThread());

Modified: trunk/Source/WebCore/Modules/webdatabase/Database.h (143270 => 143271)


--- trunk/Source/WebCore/Modules/webdatabase/Database.h	2013-02-18 23:29:56 UTC (rev 143270)
+++ trunk/Source/WebCore/Modules/webdatabase/Database.h	2013-02-18 23:42:24 UTC (rev 143271)
@@ -71,7 +71,6 @@
     virtual void markAsDeletedAndClose();
     bool deleted() const { return m_deleted; }
 
-    void close();
     virtual void closeImmediately();
 
     unsigned long long databaseSize() const;

Modified: trunk/Source/WebCore/Modules/webdatabase/DatabaseBackendAsync.cpp (143270 => 143271)


--- trunk/Source/WebCore/Modules/webdatabase/DatabaseBackendAsync.cpp	2013-02-18 23:29:56 UTC (rev 143270)
+++ trunk/Source/WebCore/Modules/webdatabase/DatabaseBackendAsync.cpp	2013-02-18 23:42:24 UTC (rev 143271)
@@ -78,6 +78,40 @@
     return false;
 }
 
+void DatabaseBackendAsync::close()
+{
+    ASSERT(databaseContext()->databaseThread());
+    ASSERT(currentThread() == databaseContext()->databaseThread()->getThreadID());
+
+    {
+        MutexLocker locker(m_transactionInProgressMutex);
+
+        // Clean up transactions that have not been scheduled yet:
+        // Transaction phase 1 cleanup. See comment on "What happens if a
+        // transaction is interrupted?" at the top of SQLTransactionBackend.cpp.
+        RefPtr<SQLTransactionBackend> transaction;
+        while (!m_transactionQueue.isEmpty()) {
+            transaction = m_transactionQueue.takeFirst();
+            transaction->notifyDatabaseThreadIsShuttingDown();
+        }
+
+        m_isTransactionQueueEnabled = false;
+        m_transactionInProgress = false;
+    }
+
+    closeDatabase();
+
+    // DatabaseThread keeps databases alive by referencing them in its
+    // m_openDatabaseSet. DatabaseThread::recordDatabaseClose() will remove
+    // this database from that set (which effectively deref's it). We hold on
+    // to it with a local pointer here for a liitle longer, so that we can
+    // unschedule any DatabaseTasks that refer to it before the database gets
+    // deleted.
+    RefPtr<DatabaseBackendAsync> protect = this;
+    databaseContext()->databaseThread()->recordDatabaseClosed(this);
+    databaseContext()->databaseThread()->unscheduleDatabaseTasks(this);
+}
+
 PassRefPtr<SQLTransactionBackend> DatabaseBackendAsync::runTransaction(PassRefPtr<SQLTransaction> transaction,
     bool readOnly, const ChangeVersionData* data)
 {

Modified: trunk/Source/WebCore/Modules/webdatabase/DatabaseBackendAsync.h (143270 => 143271)


--- trunk/Source/WebCore/Modules/webdatabase/DatabaseBackendAsync.h	2013-02-18 23:29:56 UTC (rev 143270)
+++ trunk/Source/WebCore/Modules/webdatabase/DatabaseBackendAsync.h	2013-02-18 23:42:24 UTC (rev 143271)
@@ -53,6 +53,7 @@
     DatabaseBackendAsync(PassRefPtr<DatabaseBackendContext>, const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize);
 
     virtual bool openAndVerifyVersion(bool setVersionInNewDatabase, DatabaseError&, String& errorMessage);
+    void close();
 
     PassRefPtr<SQLTransactionBackend> runTransaction(PassRefPtr<SQLTransaction>, bool readOnly, const ChangeVersionData*);
     void scheduleTransactionStep(SQLTransactionBackend*);

Modified: trunk/Source/WebCore/Modules/webdatabase/DatabaseTask.cpp (143270 => 143271)


--- trunk/Source/WebCore/Modules/webdatabase/DatabaseTask.cpp	2013-02-18 23:29:56 UTC (rev 143270)
+++ trunk/Source/WebCore/Modules/webdatabase/DatabaseTask.cpp	2013-02-18 23:42:24 UTC (rev 143271)
@@ -150,12 +150,28 @@
 DatabaseBackendAsync::DatabaseTransactionTask::DatabaseTransactionTask(PassRefPtr<SQLTransactionBackend> transaction)
     : DatabaseTask(Database::from(transaction->database()), 0)
     , m_transaction(transaction)
+    , m_didPerformTask(false)
 {
 }
 
+DatabaseBackendAsync::DatabaseTransactionTask::~DatabaseTransactionTask()
+{
+    // If the task is being destructed without the transaction ever being run,
+    // then we must either have an error or an interruption. Give the
+    // transaction a chance to clean up since it may not have been able to
+    // run to its clean up state.
+
+    // Transaction phase 2 cleanup. See comment on "What happens if a
+    // transaction is interrupted?" at the top of SQLTransactionBackend.cpp.
+
+    if (!m_didPerformTask)
+        m_transaction->notifyDatabaseThreadIsShuttingDown();
+}
+
 void DatabaseBackendAsync::DatabaseTransactionTask::doPerformTask()
 {
     m_transaction->performNextStep();
+    m_didPerformTask = true;
 }
 
 #if !LOG_DISABLED

Modified: trunk/Source/WebCore/Modules/webdatabase/DatabaseTask.h (143270 => 143271)


--- trunk/Source/WebCore/Modules/webdatabase/DatabaseTask.h	2013-02-18 23:29:56 UTC (rev 143270)
+++ trunk/Source/WebCore/Modules/webdatabase/DatabaseTask.h	2013-02-18 23:42:24 UTC (rev 143271)
@@ -137,6 +137,8 @@
 
 class DatabaseBackendAsync::DatabaseTransactionTask : public DatabaseTask {
 public:
+    virtual ~DatabaseTransactionTask();
+
     // Transaction task is never synchronous, so no 'synchronizer' parameter.
     static PassOwnPtr<DatabaseTransactionTask> create(PassRefPtr<SQLTransactionBackend> transaction)
     {
@@ -154,6 +156,7 @@
 #endif
 
     RefPtr<SQLTransactionBackend> m_transaction;
+    bool m_didPerformTask;
 };
 
 class DatabaseBackendAsync::DatabaseTableNamesTask : public DatabaseTask {

Modified: trunk/Source/WebCore/Modules/webdatabase/DatabaseThread.cpp (143270 => 143271)


--- trunk/Source/WebCore/Modules/webdatabase/DatabaseThread.cpp	2013-02-18 23:29:56 UTC (rev 143270)
+++ trunk/Source/WebCore/Modules/webdatabase/DatabaseThread.cpp	2013-02-18 23:42:24 UTC (rev 143271)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2007, 2008, 2013 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -126,7 +126,7 @@
         openSetCopy.swap(m_openDatabaseSet);
         DatabaseSet::iterator end = openSetCopy.end();
         for (DatabaseSet::iterator it = openSetCopy.begin(); it != end; ++it)
-            Database::from((*it).get())->close();
+            (*it).get()->close();
     }
 
     // Detach the thread so its resources are no longer of any concern to anyone else

Modified: trunk/Source/WebCore/Modules/webdatabase/SQLTransactionBackend.cpp (143270 => 143271)


--- trunk/Source/WebCore/Modules/webdatabase/SQLTransactionBackend.cpp	2013-02-18 23:29:56 UTC (rev 143270)
+++ trunk/Source/WebCore/Modules/webdatabase/SQLTransactionBackend.cpp	2013-02-18 23:42:24 UTC (rev 143271)
@@ -285,15 +285,58 @@
 //     What happens if a transaction is interrupted?
 //     ============================================
 //     If the transaction is interrupted half way, it won't get to run to state
-//     CleanupAndTerminate, and hence, would not have called doCleanup(). doCleanup() is
-//     where we nullify SQLTransactionBackend::m_frontend to break the reference cycle
-//     between the frontend and backend. Hence, that transaction will never get cleaned
-//     up, unless ...
+//     CleanupAndTerminate, and hence, would not have called SQLTransactionBackend's
+//     doCleanup(). doCleanup() is where we nullify SQLTransactionBackend::m_frontend
+//     to break the reference cycle between the frontend and backend. Hence, we need
+//     to cleanup the transaction by other means.
 //
-//     ... unless we have a cleanup mechanism for interruptions. That is, during the
-//     shutdown of the DatabaseThread, the SQLTransactionCoordinator will discover this
-//     interrupted transaction and invoke its notifyDatabaseThreadIsShuttingDown(),
-//     which in turn will call doCleanup(). With that, all is well again.
+//     Note: calling SQLTransactionBackend::notifyDatabaseThreadIsShuttingDown()
+//     is effectively the same as calling SQLTransactionBackend::doClean().
+//
+//     In terms of who needs to call doCleanup(), there are 5 phases in the
+//     SQLTransactionBackend life-cycle. These are the phases and how the clean
+//     up is done:
+//
+//     Phase 1. After Birth, before scheduling
+//
+//     - To clean up, DatabaseThread::databaseThread() will call
+//       DatabaseBackendAsync::close() during its shutdown.
+//     - DatabaseBackendAsync::close() will iterate
+//       DatabaseBackendAsync::m_transactionQueue and call
+//       notifyDatabaseThreadIsShuttingDown() on each transaction there.
+//        
+//     Phase 2. After scheduling, before state AcquireLock
+//
+//     - If the interruption occures before the DatabaseTransactionTask is
+//       scheduled in DatabaseThread::m_queue but hasn't gotten to execute
+//       (i.e. DatabaseTransactionTask::performTask() has not been called),
+//       then the DatabaseTransactionTask may get destructed before it ever
+//       gets to execute.
+//     - To clean up, the destructor will check if the task's m_wasExecuted is
+//       set. If not, it will call notifyDatabaseThreadIsShuttingDown() on
+//       the task's transaction.
+//
+//     Phase 3. After state AcquireLock, before "lockAcquired"
+//
+//     - In this phase, the transaction would have been added to the
+//       SQLTransactionCoordinator's CoordinationInfo's pendingTransactions.
+//     - To clean up, during shutdown, DatabaseThread::databaseThread() calls
+//       SQLTransactionCoordinator::shutdown(), which calls
+//       notifyDatabaseThreadIsShuttingDown().
+//
+//     Phase 4: After "lockAcquired", before state CleanupAndTerminate
+//
+//     - In this phase, the transaction would have been added either to the
+//       SQLTransactionCoordinator's CoordinationInfo's activeWriteTransaction
+//       or activeReadTransactions.
+//     - To clean up, during shutdown, DatabaseThread::databaseThread() calls
+//       SQLTransactionCoordinator::shutdown(), which calls
+//       notifyDatabaseThreadIsShuttingDown().
+//
+//     Phase 5: After state CleanupAndTerminate
+//
+//     - This is how a transaction ends normally.
+//     - state CleanupAndTerminate calls doCleanup().
 
 
 // There's no way of knowing exactly how much more space will be required when a statement hits the quota limit.
@@ -335,6 +378,9 @@
 
 void SQLTransactionBackend::doCleanup()
 {
+    if (!m_frontend)
+        return;
+
     ASSERT(currentThread() == database()->databaseContext()->databaseThread()->getThreadID());
 
     MutexLocker locker(m_statementMutex);
@@ -481,11 +527,7 @@
     // DB thread. Amongst other work, doCleanup() will clear m_sqliteTransaction
     // which invokes SQLiteTransaction's destructor, which will do the roll back
     // if necessary.
-
-    // Note: if doCleanup() has already been invoked, then m_frontend would have
-    // been nullified.
-    if (m_frontend)
-        doCleanup();
+    doCleanup();
 }
 
 SQLTransactionState SQLTransactionBackend::acquireLock()
@@ -719,6 +761,7 @@
     LOG(StorageAPI, "Transaction %p is complete\n", this);
     ASSERT(!m_database->sqliteDatabase().transactionInProgress());
 
+    // Phase 5 cleanup. See comment on the SQLTransaction life-cycle above.
     doCleanup();
     m_database->inProgressTransactionCompleted();
     return SQLTransactionState::End;

Modified: trunk/Source/WebCore/Modules/webdatabase/SQLTransactionCoordinator.cpp (143270 => 143271)


--- trunk/Source/WebCore/Modules/webdatabase/SQLTransactionCoordinator.cpp	2013-02-18 23:29:56 UTC (rev 143270)
+++ trunk/Source/WebCore/Modules/webdatabase/SQLTransactionCoordinator.cpp	2013-02-18 23:42:24 UTC (rev 143271)
@@ -50,6 +50,11 @@
     return database->stringIdentifier();
 }
 
+SQLTransactionCoordinator::SQLTransactionCoordinator()
+    : m_isShuttingDown(false)
+{
+}
+
 void SQLTransactionCoordinator::processPendingTransactions(CoordinationInfo& info)
 {
     if (info.activeWriteTransaction || info.pendingTransactions.isEmpty())
@@ -71,6 +76,8 @@
 
 void SQLTransactionCoordinator::acquireLock(SQLTransactionBackend* transaction)
 {
+    ASSERT(!m_isShuttingDown);
+
     String dbIdentifier = getDatabaseIdentifier(transaction);
 
     CoordinationInfoMap::iterator coordinationInfoIterator = m_coordinationInfoMap.find(dbIdentifier);
@@ -86,7 +93,7 @@
 
 void SQLTransactionCoordinator::releaseLock(SQLTransactionBackend* transaction)
 {
-    if (m_coordinationInfoMap.isEmpty())
+    if (m_isShuttingDown)
         return;
 
     String dbIdentifier = getDatabaseIdentifier(transaction);
@@ -108,10 +115,18 @@
 
 void SQLTransactionCoordinator::shutdown()
 {
+    // Prevent releaseLock() from accessing / changing the coordinationInfo
+    // while we're shutting down.
+    m_isShuttingDown = true;
+
     // Notify all transactions in progress that the database thread is shutting down
     for (CoordinationInfoMap::iterator coordinationInfoIterator = m_coordinationInfoMap.begin();
          coordinationInfoIterator != m_coordinationInfoMap.end(); ++coordinationInfoIterator) {
         CoordinationInfo& info = coordinationInfoIterator->value;
+
+        // Clean up transactions that have reached "lockAcquired":
+        // Transaction phase 4 cleanup. See comment on "What happens if a
+        // transaction is interrupted?" at the top of SQLTransactionBackend.cpp.
         if (info.activeWriteTransaction)
             info.activeWriteTransaction->notifyDatabaseThreadIsShuttingDown();
         for (HashSet<RefPtr<SQLTransactionBackend> >::iterator activeReadTransactionsIterator =
@@ -120,6 +135,14 @@
              ++activeReadTransactionsIterator) {
             (*activeReadTransactionsIterator)->notifyDatabaseThreadIsShuttingDown();
         }
+
+        // Clean up transactions that have NOT reached "lockAcquired":
+        // Transaction phase 3 cleanup. See comment on "What happens if a
+        // transaction is interrupted?" at the top of SQLTransactionBackend.cpp.
+        while (!info.pendingTransactions.isEmpty()) {
+            RefPtr<SQLTransactionBackend> transaction = info.pendingTransactions.first();
+            transaction->notifyDatabaseThreadIsShuttingDown();
+        }
     }
 
     // Clean up all pending transactions for all databases

Modified: trunk/Source/WebCore/Modules/webdatabase/SQLTransactionCoordinator.h (143270 => 143271)


--- trunk/Source/WebCore/Modules/webdatabase/SQLTransactionCoordinator.h	2013-02-18 23:29:56 UTC (rev 143270)
+++ trunk/Source/WebCore/Modules/webdatabase/SQLTransactionCoordinator.h	2013-02-18 23:42:24 UTC (rev 143271)
@@ -47,7 +47,7 @@
 class SQLTransactionCoordinator {
     WTF_MAKE_NONCOPYABLE(SQLTransactionCoordinator); WTF_MAKE_FAST_ALLOCATED;
 public:
-    SQLTransactionCoordinator() { }
+    SQLTransactionCoordinator();
     void acquireLock(SQLTransactionBackend*);
     void releaseLock(SQLTransactionBackend*);
     void shutdown();
@@ -61,6 +61,7 @@
     // Maps database names to information about pending transactions
     typedef HashMap<String, CoordinationInfo> CoordinationInfoMap;
     CoordinationInfoMap m_coordinationInfoMap;
+    bool m_isShuttingDown;
 
     void processPendingTransactions(CoordinationInfo&);
 };
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to