Title: [141166] trunk/Source/WebCore
Revision
141166
Author
mark....@apple.com
Date
2013-01-29 14:51:31 -0800 (Tue, 29 Jan 2013)

Log Message

Change DatabaseContext lookup to be thread-safe.
https://bugs.webkit.org/show_bug.cgi?id=107784.

Reviewed by Geoffrey Garen.

DatabaseContext will no longer be a Supplement of ScriptExecutionContext.
Instead we will maintain a mutex guarded contextMap in the DatabaseManager
which maps ScriptExecutionContexts to DatabaseContexts.

Also cleaned up the shutdown mechanism of the DatabaseContext,
DatabaseThread, and Databases when their owner ScriptExecutionContext
destructs.

No new tests.

* Modules/webdatabase/AbstractDatabase.cpp:
(WebCore::AbstractDatabase::AbstractDatabase):
* Modules/webdatabase/AbstractDatabase.h:
(WebCore::AbstractDatabase::databaseContext):
(AbstractDatabase):
* Modules/webdatabase/Database.cpp:
(WebCore::Database::Database):
* Modules/webdatabase/Database.h:
(WebCore):
(Database):
* Modules/webdatabase/DatabaseContext.cpp:
(WebCore):
(WebCore::DatabaseContext::DatabaseContext):
(WebCore::DatabaseContext::~DatabaseContext):
(WebCore::DatabaseContext::contextDestroyed):
(WebCore::DatabaseContext::stop):
(WebCore::DatabaseContext::databaseThread):
(WebCore::DatabaseContext::stopDatabases):
* Modules/webdatabase/DatabaseContext.h:
(DatabaseContext):
(WebCore::DatabaseContext::scriptExecutionContext):
(WebCore::DatabaseContext::hasOpenDatabases):
(WebCore::DatabaseContext::stopDatabases):
* Modules/webdatabase/DatabaseManager.cpp:
(WebCore::DatabaseManager::manager):
(WebCore::DatabaseManager::DatabaseManager):
(WebCore::DatabaseManager::getExistingDatabaseContext):
(WebCore):
(WebCore::DatabaseManager::getDatabaseContext):
(WebCore::DatabaseManager::registerDatabaseContext):
(WebCore::DatabaseManager::unregisterDatabaseContext):
(WebCore::DatabaseManager::notifyDatabaseContextConstructed):
(WebCore::DatabaseManager::notifyDatabaseContextDestructed):
(WebCore::DatabaseManager::openDatabase):
(WebCore::DatabaseManager::openDatabaseSync):
(WebCore::DatabaseManager::hasOpenDatabases):
(WebCore::DatabaseManager::stopDatabases):
(WebCore::DatabaseManager::interruptAllDatabasesForContext):
* Modules/webdatabase/DatabaseManager.h:
(WebCore):
(DatabaseManager):
(WebCore::DatabaseManager::notifyDatabaseContextConstructed):
(WebCore::DatabaseManager::notifyDatabaseContextDestructed):
* Modules/webdatabase/DatabaseSync.cpp:
(WebCore::DatabaseSync::DatabaseSync):
* Modules/webdatabase/DatabaseSync.h:
(WebCore):
(DatabaseSync):
* Modules/webdatabase/DatabaseThread.cpp:
(WebCore::DatabaseThread::~DatabaseThread):
(WebCore::DatabaseThread::requestTermination):
* Modules/webdatabase/DatabaseTracker.cpp:
(WebCore::DatabaseTracker::canEstablishDatabase):
* dom/ActiveDOMObject.cpp:
(WebCore::ActiveDOMObject::~ActiveDOMObject):

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (141165 => 141166)


--- trunk/Source/WebCore/ChangeLog	2013-01-29 22:24:55 UTC (rev 141165)
+++ trunk/Source/WebCore/ChangeLog	2013-01-29 22:51:31 UTC (rev 141166)
@@ -1,3 +1,76 @@
+2013-01-29  Mark Lam  <mark....@apple.com>
+
+        Change DatabaseContext lookup to be thread-safe.
+        https://bugs.webkit.org/show_bug.cgi?id=107784.
+
+        Reviewed by Geoffrey Garen.
+
+        DatabaseContext will no longer be a Supplement of ScriptExecutionContext.
+        Instead we will maintain a mutex guarded contextMap in the DatabaseManager
+        which maps ScriptExecutionContexts to DatabaseContexts.
+
+        Also cleaned up the shutdown mechanism of the DatabaseContext,
+        DatabaseThread, and Databases when their owner ScriptExecutionContext
+        destructs.
+
+        No new tests.
+
+        * Modules/webdatabase/AbstractDatabase.cpp:
+        (WebCore::AbstractDatabase::AbstractDatabase):
+        * Modules/webdatabase/AbstractDatabase.h:
+        (WebCore::AbstractDatabase::databaseContext):
+        (AbstractDatabase):
+        * Modules/webdatabase/Database.cpp:
+        (WebCore::Database::Database):
+        * Modules/webdatabase/Database.h:
+        (WebCore):
+        (Database):
+        * Modules/webdatabase/DatabaseContext.cpp:
+        (WebCore):
+        (WebCore::DatabaseContext::DatabaseContext):
+        (WebCore::DatabaseContext::~DatabaseContext):
+        (WebCore::DatabaseContext::contextDestroyed):
+        (WebCore::DatabaseContext::stop):
+        (WebCore::DatabaseContext::databaseThread):
+        (WebCore::DatabaseContext::stopDatabases):
+        * Modules/webdatabase/DatabaseContext.h:
+        (DatabaseContext):
+        (WebCore::DatabaseContext::scriptExecutionContext):
+        (WebCore::DatabaseContext::hasOpenDatabases):
+        (WebCore::DatabaseContext::stopDatabases):
+        * Modules/webdatabase/DatabaseManager.cpp:
+        (WebCore::DatabaseManager::manager):
+        (WebCore::DatabaseManager::DatabaseManager):
+        (WebCore::DatabaseManager::getExistingDatabaseContext):
+        (WebCore):
+        (WebCore::DatabaseManager::getDatabaseContext):
+        (WebCore::DatabaseManager::registerDatabaseContext):
+        (WebCore::DatabaseManager::unregisterDatabaseContext):
+        (WebCore::DatabaseManager::notifyDatabaseContextConstructed):
+        (WebCore::DatabaseManager::notifyDatabaseContextDestructed):
+        (WebCore::DatabaseManager::openDatabase):
+        (WebCore::DatabaseManager::openDatabaseSync):
+        (WebCore::DatabaseManager::hasOpenDatabases):
+        (WebCore::DatabaseManager::stopDatabases):
+        (WebCore::DatabaseManager::interruptAllDatabasesForContext):
+        * Modules/webdatabase/DatabaseManager.h:
+        (WebCore):
+        (DatabaseManager):
+        (WebCore::DatabaseManager::notifyDatabaseContextConstructed):
+        (WebCore::DatabaseManager::notifyDatabaseContextDestructed):
+        * Modules/webdatabase/DatabaseSync.cpp:
+        (WebCore::DatabaseSync::DatabaseSync):
+        * Modules/webdatabase/DatabaseSync.h:
+        (WebCore):
+        (DatabaseSync):
+        * Modules/webdatabase/DatabaseThread.cpp:
+        (WebCore::DatabaseThread::~DatabaseThread):
+        (WebCore::DatabaseThread::requestTermination):
+        * Modules/webdatabase/DatabaseTracker.cpp:
+        (WebCore::DatabaseTracker::canEstablishDatabase):
+        * dom/ActiveDOMObject.cpp:
+        (WebCore::ActiveDOMObject::~ActiveDOMObject):
+
 2013-01-29  Julien Chaffraix  <jchaffr...@webkit.org>
 
         [CSS Grid Layout] Make resolveContentBasedTrackSizingFunctionsForItems reuse distributeSpaceToTracks

Modified: trunk/Source/WebCore/Modules/webdatabase/AbstractDatabase.cpp (141165 => 141166)


--- trunk/Source/WebCore/Modules/webdatabase/AbstractDatabase.cpp	2013-01-29 22:24:55 UTC (rev 141165)
+++ trunk/Source/WebCore/Modules/webdatabase/AbstractDatabase.cpp	2013-01-29 22:51:31 UTC (rev 141166)
@@ -174,10 +174,10 @@
     return infoTableName;
 }
 
-AbstractDatabase::AbstractDatabase(ScriptExecutionContext* context, const String& name, const String& expectedVersion,
+AbstractDatabase::AbstractDatabase(PassRefPtr<DatabaseContext> databaseContext, const String& name, const String& expectedVersion,
                                    const String& displayName, unsigned long estimatedSize, DatabaseType databaseType)
-    : m_scriptExecutionContext(context)
-    , m_databaseContext(DatabaseContext::from(context))
+    : m_databaseContext(databaseContext)
+    , m_scriptExecutionContext(m_databaseContext->scriptExecutionContext())
     , m_name(name.isolatedCopy())
     , m_expectedVersion(expectedVersion.isolatedCopy())
     , m_displayName(displayName.isolatedCopy())
@@ -187,7 +187,7 @@
     , m_new(false)
     , m_isSyncDatabase(databaseType == SyncDatabase)
 {
-    ASSERT(context->isContextThread());
+    ASSERT(m_databaseContext->scriptExecutionContext()->isContextThread());
     m_contextThreadSecurityOrigin = m_scriptExecutionContext->securityOrigin()->isolatedCopy();
 
     m_databaseAuthorizer = DatabaseAuthorizer::create(infoTableName);

Modified: trunk/Source/WebCore/Modules/webdatabase/AbstractDatabase.h (141165 => 141166)


--- trunk/Source/WebCore/Modules/webdatabase/AbstractDatabase.h	2013-01-29 22:24:55 UTC (rev 141165)
+++ trunk/Source/WebCore/Modules/webdatabase/AbstractDatabase.h	2013-01-29 22:51:31 UTC (rev 141166)
@@ -35,6 +35,7 @@
 #include "DatabaseDetails.h"
 #include "SQLiteDatabase.h"
 #include <wtf/Forward.h>
+#include <wtf/RefPtr.h>
 #include <wtf/ThreadSafeRefCounted.h>
 #include <wtf/text/WTFString.h>
 
@@ -86,7 +87,7 @@
     virtual void markAsDeletedAndClose() = 0;
     virtual void closeImmediately() = 0;
 
-    DatabaseContext* databaseContext() const { return m_databaseContext; }
+    DatabaseContext* databaseContext() const { return m_databaseContext.get(); }
 
 protected:
     friend class ChangeVersionWrapper;
@@ -100,7 +101,7 @@
         SyncDatabase
     };
 
-    AbstractDatabase(ScriptExecutionContext*, const String& name, const String& expectedVersion,
+    AbstractDatabase(PassRefPtr<DatabaseContext>, const String& name, const String& expectedVersion,
                      const String& displayName, unsigned long estimatedSize, DatabaseType);
 
     void closeDatabase();
@@ -127,8 +128,8 @@
     static const char* databaseInfoTableName();
 
     RefPtr<SecurityOrigin> m_contextThreadSecurityOrigin;
+    RefPtr<DatabaseContext> m_databaseContext; // Associated with m_scriptExecutionContext.
     RefPtr<ScriptExecutionContext> m_scriptExecutionContext;
-    DatabaseContext* m_databaseContext; // Owned by m_scriptExecutionContext.
 
     String m_name;
     String m_expectedVersion;

Modified: trunk/Source/WebCore/Modules/webdatabase/Database.cpp (141165 => 141166)


--- trunk/Source/WebCore/Modules/webdatabase/Database.cpp	2013-01-29 22:24:55 UTC (rev 141165)
+++ trunk/Source/WebCore/Modules/webdatabase/Database.cpp	2013-01-29 22:51:31 UTC (rev 141166)
@@ -65,8 +65,8 @@
 
 namespace WebCore {
 
-Database::Database(ScriptExecutionContext* context, const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize)
-    : AbstractDatabase(context, name, expectedVersion, displayName, estimatedSize, AsyncDatabase)
+Database::Database(PassRefPtr<DatabaseContext> databaseContext, const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize)
+    : AbstractDatabase(databaseContext, name, expectedVersion, displayName, estimatedSize, AsyncDatabase)
     , m_transactionInProgress(false)
     , m_isTransactionQueueEnabled(true)
     , m_deleted(false)
@@ -74,7 +74,7 @@
     m_databaseThreadSecurityOrigin = m_contextThreadSecurityOrigin->isolatedCopy();
 
     ScriptController::initializeThreading();
-    ASSERT(databaseContext()->databaseThread());
+    ASSERT(m_databaseContext->databaseThread());
 }
 
 class DerefContextTask : public ScriptExecutionContext::Task {

Modified: trunk/Source/WebCore/Modules/webdatabase/Database.h (141165 => 141166)


--- trunk/Source/WebCore/Modules/webdatabase/Database.h	2013-01-29 22:24:55 UTC (rev 141165)
+++ trunk/Source/WebCore/Modules/webdatabase/Database.h	2013-01-29 22:51:31 UTC (rev 141166)
@@ -40,7 +40,6 @@
 namespace WebCore {
 
 class DatabaseCallback;
-class ScriptExecutionContext;
 class SecurityOrigin;
 class SQLTransaction;
 class SQLTransactionCallback;
@@ -87,7 +86,7 @@
     class DatabaseTransactionTask;
     class DatabaseTableNamesTask;
 
-    Database(ScriptExecutionContext*, const String& name, const String& expectedVersion,
+    Database(PassRefPtr<DatabaseContext>, const String& name, const String& expectedVersion,
              const String& displayName, unsigned long estimatedSize);
     void runTransaction(PassRefPtr<SQLTransactionCallback>, PassRefPtr<SQLTransactionErrorCallback>,
                         PassRefPtr<VoidCallback> successCallback, PassRefPtr<SQLTransactionWrapper>, bool readOnly);

Modified: trunk/Source/WebCore/Modules/webdatabase/DatabaseContext.cpp (141165 => 141166)


--- trunk/Source/WebCore/Modules/webdatabase/DatabaseContext.cpp	2013-01-29 22:24:55 UTC (rev 141165)
+++ trunk/Source/WebCore/Modules/webdatabase/DatabaseContext.cpp	2013-01-29 22:51:31 UTC (rev 141166)
@@ -44,44 +44,68 @@
 
 namespace WebCore {
 
-const char* DatabaseContext::supplementName()
+DatabaseContext::DatabaseContext(ScriptExecutionContext* context)
+    : ActiveDOMObject(context, this)
+    , m_hasOpenDatabases(false)
+    , m_isRegistered(true) // will register on construction below.
+    , m_hasRequestedTermination(false)
 {
-    return "DatabaseContext";
+    // ActiveDOMObject expects this to be called to set internal flags.
+    suspendIfNeeded();
+
+    // For debug accounting only. We must do this before we register the
+    // instance. The assertions assume this.
+    DatabaseManager::manager().didConstructDatabaseContext(); 
+
+    DatabaseManager::manager().registerDatabaseContext(this);
 }
 
-DatabaseContext* DatabaseContext::existingDatabaseContextFrom(ScriptExecutionContext* context)
+DatabaseContext::~DatabaseContext()
 {
-    return static_cast<DatabaseContext*>(Supplement<ScriptExecutionContext>::from(context, supplementName()));
+    stopDatabases();
+    ASSERT(!m_databaseThread || m_databaseThread->terminationRequested());
+
+    // For debug accounting only. We must call this last. The assertions assume
+    // this.
+    DatabaseManager::manager().didDestructDatabaseContext();
 }
 
-DatabaseContext::DatabaseContext(ScriptExecutionContext* context)
-    : m_scriptExecutionContext(context)
-    , m_hasOpenDatabases(false)
+// This is called if the associated ScriptExecutionContext is destructing while
+// we're still associated with it. That's our cue to disassociate and shutdown.
+// To do this, we stop the database and let everything shutdown naturally
+// because the database closing process may still make use of this context.
+// It is not safe to just delete the context here.
+void DatabaseContext::contextDestroyed()
 {
-}
+    stopDatabases();
 
-DatabaseContext::~DatabaseContext()
-{
-    if (m_databaseThread) {
-        ASSERT(m_databaseThread->terminationRequested());
-        m_databaseThread = 0;
-    }
+    // Normally, willDestroyActiveDOMObject() is called in ~ActiveDOMObject().
+    // However, we're here because the destructor hasn't been called, and the
+    // ScriptExecutionContext we're associated with is about to be destructed.
+    // So, go ahead an unregister self from the ActiveDOMObject list, and
+    // set m_scriptExecutionContext to 0 so that ~ActiveDOMObject() doesn't
+    // try to do so again.
+    m_scriptExecutionContext->willDestroyActiveDOMObject(this);
+    m_scriptExecutionContext = 0;
 }
 
-DatabaseContext* DatabaseContext::from(ScriptExecutionContext* context)
+// stop() is from stopActiveDOMObjects() which indicates that the owner Frame
+// or WorkerThread is shutting down. Initiate the orderly shutdown by stopping
+// the associated databases.
+void DatabaseContext::stop()
 {
-    DatabaseContext* supplement = existingDatabaseContextFrom(context);
-    if (!supplement) {
-        supplement = new DatabaseContext(context);
-        provideTo(context, supplementName(), adoptPtr(supplement));
-        ASSERT(supplement == existingDatabaseContextFrom(context));
-    }
-    return supplement;
+    stopDatabases();
 }
 
 DatabaseThread* DatabaseContext::databaseThread()
 {
     if (!m_databaseThread && !m_hasOpenDatabases) {
+        // It's OK to ask for the m_databaseThread after we've requested
+        // termination because we're still using it to execute the closing
+        // of the database. However, it is NOT OK to create a new thread
+        // after we've requested termination.
+        ASSERT(!m_hasRequestedTermination);
+
         // Create the database thread on first request - but not if at least one database was already opened,
         // because in that case we already had a database thread and terminated it and should not create another.
         m_databaseThread = DatabaseThread::create();
@@ -92,26 +116,29 @@
     return m_databaseThread.get();
 }
 
-bool DatabaseContext::hasOpenDatabases(ScriptExecutionContext* context)
+bool DatabaseContext::stopDatabases(DatabaseTaskSynchronizer* cleanupSync)
 {
-    // We don't use DatabaseContext::from because we don't want to cause
-    // DatabaseContext to be allocated if we don't have one already.
-    DatabaseContext* databaseContext = existingDatabaseContextFrom(context);
-    if (!databaseContext)
-        return false;
-    return databaseContext->m_hasOpenDatabases;
-}
+    if (m_isRegistered) {
+        DatabaseManager::manager().unregisterDatabaseContext(this);
+        m_isRegistered = false;
+    }
 
-void DatabaseContext::stopDatabases(ScriptExecutionContext* context, DatabaseTaskSynchronizer* cleanupSync)
-{
-    // We don't use DatabaseContext::from because we don't want to cause
-    // DatabaseContext to be allocated if we don't have one already.
-    DatabaseContext* databaseContext = existingDatabaseContextFrom(context);
+    // Though we initiate termination of the DatabaseThread here in
+    // stopDatabases(), we can't clear the m_databaseThread ref till we get to
+    // the destructor. This is because the Databases that are managed by
+    // DatabaseThread still rely on this ref between the context and the thread
+    // to execute the task for closing the database. By the time we get to the
+    // destructor, we're guaranteed that the databases are destructed (which is
+    // why our ref count is 0 then and we're destructing). Then, the
+    // m_databaseThread RefPtr destructor will deref and delete the
+    // DatabaseThread.
 
-    if (databaseContext && databaseContext->m_databaseThread)
-        databaseContext->m_databaseThread->requestTermination(cleanupSync);
-    else if (cleanupSync)
-        cleanupSync->taskCompleted();
+    if (m_databaseThread && !m_hasRequestedTermination) {
+        m_databaseThread->requestTermination(cleanupSync);
+        m_hasRequestedTermination = true;
+        return true;
+    }
+    return false;
 }
 
 bool DatabaseContext::allowDatabaseAccess() const

Modified: trunk/Source/WebCore/Modules/webdatabase/DatabaseContext.h (141165 => 141166)


--- trunk/Source/WebCore/Modules/webdatabase/DatabaseContext.h	2013-01-29 22:24:55 UTC (rev 141165)
+++ trunk/Source/WebCore/Modules/webdatabase/DatabaseContext.h	2013-01-29 22:51:31 UTC (rev 141166)
@@ -30,8 +30,10 @@
 
 #if ENABLE(SQL_DATABASE)
 
+#include "ActiveDOMObject.h"
 #include "DatabaseDetails.h"
-#include "Supplementable.h"
+#include <wtf/Assertions.h>
+#include <wtf/RefCounted.h>
 
 namespace WebCore {
 
@@ -40,31 +42,37 @@
 class DatabaseThread;
 class ScriptExecutionContext;
 
-class DatabaseContext : public Supplement<ScriptExecutionContext> {
+class DatabaseContext : public RefCounted<DatabaseContext>, ActiveDOMObject {
 public:
     virtual ~DatabaseContext();
-    static DatabaseContext* from(ScriptExecutionContext*);
 
+    // For life-cycle management (inherited from ActiveDOMObject):
+    virtual void contextDestroyed();
+    virtual void stop();
+
+    ScriptExecutionContext* scriptExecutionContext() { return m_scriptExecutionContext; }
     DatabaseThread* databaseThread();
 
     void setHasOpenDatabases() { m_hasOpenDatabases = true; }
+    bool hasOpenDatabases() { return m_hasOpenDatabases; }
 
-    static bool hasOpenDatabases(ScriptExecutionContext*);
-
     // When the database cleanup is done, cleanupSync will be signalled.
-    static void stopDatabases(ScriptExecutionContext*, DatabaseTaskSynchronizer*);
+    bool stopDatabases(DatabaseTaskSynchronizer*);
 
     bool allowDatabaseAccess() const;
     void databaseExceededQuota(const String& name, DatabaseDetails);
 
 private:
     explicit DatabaseContext(ScriptExecutionContext*);
-    static const char* supplementName();
-    static DatabaseContext* existingDatabaseContextFrom(ScriptExecutionContext*);
 
-    ScriptExecutionContext* m_scriptExecutionContext;
+    void stopDatabases() { stopDatabases(0); }
+
     RefPtr<DatabaseThread> m_databaseThread;
     bool m_hasOpenDatabases; // This never changes back to false, even after the database thread is closed.
+    bool m_isRegistered;
+    bool m_hasRequestedTermination;
+
+    friend class DatabaseManager;
 };
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/Modules/webdatabase/DatabaseManager.cpp (141165 => 141166)


--- trunk/Source/WebCore/Modules/webdatabase/DatabaseManager.cpp	2013-01-29 22:24:55 UTC (rev 141165)
+++ trunk/Source/WebCore/Modules/webdatabase/DatabaseManager.cpp	2013-01-29 22:51:31 UTC (rev 141166)
@@ -33,6 +33,7 @@
 #include "DatabaseCallback.h"
 #include "DatabaseContext.h"
 #include "DatabaseSync.h"
+#include "DatabaseTask.h"
 #include "InspectorDatabaseInstrumentation.h"
 #include "Logging.h"
 #include "ScriptExecutionContext.h"
@@ -50,6 +51,8 @@
 DatabaseManager& DatabaseManager::manager()
 {
     static DatabaseManager* dbManager = 0;
+    // FIXME: The following is vulnerable to a race between threads. Need to
+    // implement a thread safe on-first-use static initializer.
     if (!dbManager)
         dbManager = new DatabaseManager();
 
@@ -59,6 +62,10 @@
 DatabaseManager::DatabaseManager()
     : m_client(0)
     , m_databaseIsAvailable(true)
+#if !ASSERT_DISABLED
+    , m_databaseContextRegisteredCount(0)
+    , m_databaseContextInstanceCount(0)
+#endif
 {
 #if USE(PLATFORM_STRATEGIES)
     m_server = platformStrategies()->databaseStrategy()->getDatabaseServer();
@@ -122,26 +129,95 @@
     RefPtr<DatabaseCallback> m_creationCallback;
 };
 
+PassRefPtr<DatabaseContext> DatabaseManager::existingDatabaseContextFor(ScriptExecutionContext* context)
+{
+    MutexLocker locker(m_contextMapLock);
+
+    ASSERT(m_databaseContextRegisteredCount >= 0);
+    ASSERT(m_databaseContextInstanceCount >= 0);
+    ASSERT(m_databaseContextRegisteredCount <= m_databaseContextInstanceCount);
+
+    RefPtr<DatabaseContext> databaseContext = adoptRef(m_contextMap.get(context));
+    if (databaseContext) {
+        // If we're instantiating a new DatabaseContext, the new instance would
+        // carry a new refCount of 1. The client expects this and will simply
+        // adoptRef the databaseContext without ref'ing it.
+        //     However, instead of instantiating a new instance, we're reusing
+        // an existing one that corresponds to the specified ScriptExecutionContext.
+        // Hence, that new refCount need to be attributed to the reused instance
+        // to ensure that the refCount is accurate when the client adopts the ref.
+        // We do this by ref'ing the reused databaseContext before returning it.
+        databaseContext->ref();
+    }
+    return databaseContext.release();
+}
+
+PassRefPtr<DatabaseContext> DatabaseManager::databaseContextFor(ScriptExecutionContext* context)
+{
+    RefPtr<DatabaseContext> databaseContext = existingDatabaseContextFor(context);
+    if (!databaseContext)
+        databaseContext = adoptRef(new DatabaseContext(context));
+    return databaseContext.release();
+}
+
+void DatabaseManager::registerDatabaseContext(DatabaseContext* databaseContext)
+{
+    MutexLocker locker(m_contextMapLock);
+    ScriptExecutionContext* context = databaseContext->scriptExecutionContext();
+    m_contextMap.set(context, databaseContext);
+#if !ASSERT_DISABLED
+    m_databaseContextRegisteredCount++;
+#endif
+}
+
+void DatabaseManager::unregisterDatabaseContext(DatabaseContext* databaseContext)
+{
+    MutexLocker locker(m_contextMapLock);
+    ScriptExecutionContext* context = databaseContext->scriptExecutionContext();
+    ASSERT(m_contextMap.get(context));
+#if !ASSERT_DISABLED
+    m_databaseContextRegisteredCount--;
+#endif
+    m_contextMap.remove(context);
+}
+
+#if !ASSERT_DISABLED
+void DatabaseManager::didConstructDatabaseContext()
+{
+    MutexLocker lock(m_contextMapLock);
+    m_databaseContextInstanceCount++;
+}
+
+void DatabaseManager::didDestructDatabaseContext()
+{
+    MutexLocker lock(m_contextMapLock);
+    m_databaseContextInstanceCount--;
+    ASSERT(m_databaseContextRegisteredCount <= m_databaseContextInstanceCount);
+}
+#endif
+
 PassRefPtr<Database> DatabaseManager::openDatabase(ScriptExecutionContext* context,
     const String& name, const String& expectedVersion, const String& displayName,
     unsigned long estimatedSize, PassRefPtr<DatabaseCallback> creationCallback, ExceptionCode& e)
 {
+    RefPtr<DatabaseContext> databaseContext = databaseContextFor(context);
+
     if (!canEstablishDatabase(context, name, displayName, estimatedSize)) {
         LOG(StorageAPI, "Database %s for origin %s not allowed to be established", name.ascii().data(), context->securityOrigin()->toString().ascii().data());
         return 0;
     }
 
-    RefPtr<Database> database = adoptRef(new Database(context, name, expectedVersion, displayName, estimatedSize));
+    RefPtr<Database> database = adoptRef(new Database(databaseContext, name, expectedVersion, displayName, estimatedSize));
 
     String errorMessage;
     if (!database->openAndVerifyVersion(!creationCallback, e, errorMessage)) {
         database->logErrorMessage(errorMessage);
-        DatabaseManager::manager().removeOpenDatabase(database.get());
+        removeOpenDatabase(database.get());
         return 0;
     }
 
-    DatabaseManager::manager().setDatabaseDetails(context->securityOrigin(), name, displayName, estimatedSize);
-    DatabaseManager::manager().setHasOpenDatabases(context);
+    setDatabaseDetails(context->securityOrigin(), name, displayName, estimatedSize);
+    databaseContext->setHasOpenDatabases();
 
     InspectorInstrumentation::didOpenDatabase(context, database, context->securityOrigin()->host(), name, expectedVersion);
 
@@ -150,13 +226,14 @@
         database->m_scriptExecutionContext->postTask(DatabaseCreationCallbackTask::create(database, creationCallback));
     }
 
-    return database;
+    return database.release();
 }
 
 PassRefPtr<DatabaseSync> DatabaseManager::openDatabaseSync(ScriptExecutionContext* context,
     const String& name, const String& expectedVersion, const String& displayName,
     unsigned long estimatedSize, PassRefPtr<DatabaseCallback> creationCallback, ExceptionCode& ec)
 {
+    RefPtr<DatabaseContext> databaseContext = databaseContextFor(context);
     ASSERT(context->isContextThread());
 
     if (!canEstablishDatabase(context, name, displayName, estimatedSize)) {
@@ -164,38 +241,39 @@
         return 0;
     }
 
-    RefPtr<DatabaseSync> database = adoptRef(new DatabaseSync(context, name, expectedVersion, displayName, estimatedSize));
+    RefPtr<DatabaseSync> database = adoptRef(new DatabaseSync(databaseContext, name, expectedVersion, displayName, estimatedSize));
 
     String errorMessage;
     if (!database->performOpenAndVerify(!creationCallback, ec, errorMessage)) {
         database->logErrorMessage(errorMessage);
-        DatabaseManager::manager().removeOpenDatabase(database.get());
+        removeOpenDatabase(database.get());
         return 0;
     }
 
-    DatabaseManager::manager().setDatabaseDetails(context->securityOrigin(), name, displayName, estimatedSize);
+    setDatabaseDetails(context->securityOrigin(), name, displayName, estimatedSize);
 
     if (database->isNew() && creationCallback.get()) {
         LOG(StorageAPI, "Invoking the creation callback for database %p\n", database.get());
         creationCallback->handleEvent(database.get());
     }
 
-    return database;
+    return database.release();
 }
 
 bool DatabaseManager::hasOpenDatabases(ScriptExecutionContext* context)
 {
-    return DatabaseContext::hasOpenDatabases(context);
+    RefPtr<DatabaseContext> databaseContext = existingDatabaseContextFor(context);
+    if (!databaseContext)
+        return false;
+    return databaseContext->m_hasOpenDatabases;
 }
 
-void DatabaseManager::setHasOpenDatabases(ScriptExecutionContext* context)
-{
-    DatabaseContext::from(context)->setHasOpenDatabases();
-}
-
 void DatabaseManager::stopDatabases(ScriptExecutionContext* context, DatabaseTaskSynchronizer* synchronizer)
 {
-    DatabaseContext::stopDatabases(context, synchronizer);
+    RefPtr<DatabaseContext> databaseContext = existingDatabaseContextFor(context);
+    if (!databaseContext || !databaseContext->stopDatabases(synchronizer))
+        if (synchronizer)
+            synchronizer->taskCompleted();
 }
 
 String DatabaseManager::fullPathForDatabase(SecurityOrigin* origin, const String& name, bool createIfDoesNotExist)
@@ -272,9 +350,11 @@
 }
 #endif // PLATFORM(CHROMIUM)
 
-void DatabaseManager::interruptAllDatabasesForContext(const ScriptExecutionContext* context)
+void DatabaseManager::interruptAllDatabasesForContext(ScriptExecutionContext* context)
 {
-    m_server->interruptAllDatabasesForContext(context);
+    RefPtr<DatabaseContext> databaseContext = existingDatabaseContextFor(context);
+    if (databaseContext)
+        m_server->interruptAllDatabasesForContext(context);
 }
 
 bool DatabaseManager::canEstablishDatabase(ScriptExecutionContext* context, const String& name, const String& displayName, unsigned long estimatedSize)

Modified: trunk/Source/WebCore/Modules/webdatabase/DatabaseManager.h (141165 => 141166)


--- trunk/Source/WebCore/Modules/webdatabase/DatabaseManager.h	2013-01-29 22:24:55 UTC (rev 141165)
+++ trunk/Source/WebCore/Modules/webdatabase/DatabaseManager.h	2013-01-29 22:51:31 UTC (rev 141166)
@@ -30,13 +30,17 @@
 
 #include "AbstractDatabaseServer.h"
 #include "DatabaseBasicTypes.h"
+#include <wtf/Assertions.h>
+#include <wtf/HashMap.h>
 #include <wtf/PassRefPtr.h>
+#include <wtf/Threading.h>
 
 namespace WebCore {
 
 class AbstractDatabaseServer;
 class Database;
 class DatabaseCallback;
+class DatabaseContext;
 class DatabaseManagerClient;
 class DatabaseSync;
 class DatabaseTaskSynchronizer;
@@ -56,11 +60,27 @@
     bool isAvailable();
     void setIsAvailable(bool);
 
+    // This gets a DatabaseContext for the specified ScriptExecutionContext.
+    // If one doesn't already exist, it will create a new one.
+    PassRefPtr<DatabaseContext> databaseContextFor(ScriptExecutionContext*);
+
+    // These 2 methods are for DatabaseContext (un)registration, and should only
+    // be called by the DatabaseContext constructor and destructor.
+    void registerDatabaseContext(DatabaseContext*);
+    void unregisterDatabaseContext(DatabaseContext*);
+
+#if !ASSERT_DISABLED
+    void didConstructDatabaseContext();
+    void didDestructDatabaseContext();
+#else
+    void didConstructDatabaseContext() { }
+    void didDestructDatabaseContext() { }
+#endif
+
     PassRefPtr<Database> openDatabase(ScriptExecutionContext*, const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize, PassRefPtr<DatabaseCallback>, ExceptionCode&);
     PassRefPtr<DatabaseSync> openDatabaseSync(ScriptExecutionContext*, const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize, PassRefPtr<DatabaseCallback>, ExceptionCode&);
 
     bool hasOpenDatabases(ScriptExecutionContext*);
-    void setHasOpenDatabases(ScriptExecutionContext*);
     void stopDatabases(ScriptExecutionContext*, DatabaseTaskSynchronizer*);
 
     String fullPathForDatabase(SecurityOrigin*, const String& name, bool createIfDoesNotExist = true);
@@ -89,7 +109,7 @@
     void closeDatabasesImmediately(const String& originIdentifier, const String& name);
 #endif // PLATFORM(CHROMIUM)
 
-    void interruptAllDatabasesForContext(const ScriptExecutionContext*);
+    void interruptAllDatabasesForContext(ScriptExecutionContext*);
 
     bool canEstablishDatabase(ScriptExecutionContext*, const String& name, const String& displayName, unsigned long estimatedSize);
     void addOpenDatabase(AbstractDatabase*);
@@ -102,9 +122,22 @@
     DatabaseManager();
     ~DatabaseManager() { }
 
+    // This gets a DatabaseContext for the specified ScriptExecutionContext if
+    // it already exist previously. Otherwise, it returns 0.
+    PassRefPtr<DatabaseContext> existingDatabaseContextFor(ScriptExecutionContext*);
+
     AbstractDatabaseServer* m_server;
     DatabaseManagerClient* m_client;
     bool m_databaseIsAvailable;
+
+    // Access to the following fields require locking m_contextMapLock:
+    typedef HashMap<ScriptExecutionContext*, DatabaseContext*> ContextMap;
+    ContextMap m_contextMap;
+#if !ASSERT_DISABLED
+    int m_databaseContextRegisteredCount;
+    int m_databaseContextInstanceCount;
+#endif
+    Mutex m_contextMapLock;
 };
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/Modules/webdatabase/DatabaseSync.cpp (141165 => 141166)


--- trunk/Source/WebCore/Modules/webdatabase/DatabaseSync.cpp	2013-01-29 22:24:55 UTC (rev 141165)
+++ trunk/Source/WebCore/Modules/webdatabase/DatabaseSync.cpp	2013-01-29 22:51:31 UTC (rev 141166)
@@ -34,6 +34,7 @@
 #if ENABLE(SQL_DATABASE)
 
 #include "DatabaseCallback.h"
+#include "DatabaseContext.h"
 #include "DatabaseManager.h"
 #include "Logging.h"
 #include "SQLException.h"
@@ -47,9 +48,9 @@
 
 namespace WebCore {
 
-DatabaseSync::DatabaseSync(ScriptExecutionContext* context, const String& name, const String& expectedVersion,
+DatabaseSync::DatabaseSync(PassRefPtr<DatabaseContext> databaseContext, const String& name, const String& expectedVersion,
                            const String& displayName, unsigned long estimatedSize)
-    : AbstractDatabase(context, name, expectedVersion, displayName, estimatedSize, SyncDatabase)
+    : AbstractDatabase(databaseContext, name, expectedVersion, displayName, estimatedSize, SyncDatabase)
 {
 }
 

Modified: trunk/Source/WebCore/Modules/webdatabase/DatabaseSync.h (141165 => 141166)


--- trunk/Source/WebCore/Modules/webdatabase/DatabaseSync.h	2013-01-29 22:24:55 UTC (rev 141165)
+++ trunk/Source/WebCore/Modules/webdatabase/DatabaseSync.h	2013-01-29 22:51:31 UTC (rev 141166)
@@ -47,7 +47,6 @@
 class DatabaseCallback;
 class SQLTransactionSync;
 class SQLTransactionSyncCallback;
-class ScriptExecutionContext;
 class SecurityOrigin;
 
 // Instances of this class should be created and used only on the worker's context thread.
@@ -74,7 +73,7 @@
     }
 
 private:
-    DatabaseSync(ScriptExecutionContext*, const String& name, const String& expectedVersion,
+    DatabaseSync(PassRefPtr<DatabaseContext>, const String& name, const String& expectedVersion,
                  const String& displayName, unsigned long estimatedSize);
     void runTransaction(PassRefPtr<SQLTransactionSyncCallback>, bool readOnly, ExceptionCode&);
 

Modified: trunk/Source/WebCore/Modules/webdatabase/DatabaseThread.cpp (141165 => 141166)


--- trunk/Source/WebCore/Modules/webdatabase/DatabaseThread.cpp	2013-01-29 22:24:55 UTC (rev 141165)
+++ trunk/Source/WebCore/Modules/webdatabase/DatabaseThread.cpp	2013-01-29 22:51:31 UTC (rev 141166)
@@ -52,7 +52,13 @@
 
 DatabaseThread::~DatabaseThread()
 {
-    // FIXME: Any cleanup required here?  Since the thread deletes itself after running its detached course, I don't think so.  Lets be sure.
+    // The DatabaseThread will only be destructed when both its owner
+    // DatabaseContext has deref'ed it, and the databaseThread() thread function
+    // has deref'ed the DatabaseThread object. The DatabaseContext destructor
+    // will take care of ensuring that a termination request has been issued.
+    // The termination request will trigger an orderly shutdown of the thread
+    // function databaseThread(). In shutdown, databaseThread() will deref the
+    // DatabaseThread before returning.
     ASSERT(terminationRequested());
 }
 
@@ -70,7 +76,6 @@
 
 void DatabaseThread::requestTermination(DatabaseTaskSynchronizer *cleanupSync)
 {
-    ASSERT(!m_cleanupSync);
     m_cleanupSync = cleanupSync;
     LOG(StorageAPI, "DatabaseThread %p was asked to terminate\n", this);
     m_queue.kill();

Modified: trunk/Source/WebCore/Modules/webdatabase/DatabaseTracker.cpp (141165 => 141166)


--- trunk/Source/WebCore/Modules/webdatabase/DatabaseTracker.cpp	2013-01-29 22:24:55 UTC (rev 141165)
+++ trunk/Source/WebCore/Modules/webdatabase/DatabaseTracker.cpp	2013-01-29 22:51:31 UTC (rev 141166)
@@ -35,6 +35,7 @@
 #include "Chrome.h"
 #include "ChromeClient.h"
 #include "DatabaseContext.h"
+#include "DatabaseManager.h"
 #include "DatabaseManagerClient.h"
 #include "DatabaseThread.h"
 #include "Logging.h"
@@ -167,7 +168,8 @@
 
     // Give the chrome client a chance to increase the quota.
     // Drop all locks before calling out; we don't know what they'll do.
-    DatabaseContext::from(context)->databaseExceededQuota(name, DatabaseDetails(name.isolatedCopy(), displayName.isolatedCopy(), estimatedSize, 0));
+    RefPtr<DatabaseContext> databaseContext = DatabaseManager::manager().databaseContextFor(context);
+    databaseContext->databaseExceededQuota(name, DatabaseDetails(name.isolatedCopy(), displayName.isolatedCopy(), estimatedSize, 0));
 
     MutexLocker lockDatabase(m_databaseGuard);
 

Modified: trunk/Source/WebCore/dom/ActiveDOMObject.cpp (141165 => 141166)


--- trunk/Source/WebCore/dom/ActiveDOMObject.cpp	2013-01-29 22:24:55 UTC (rev 141165)
+++ trunk/Source/WebCore/dom/ActiveDOMObject.cpp	2013-01-29 22:51:31 UTC (rev 141166)
@@ -54,8 +54,17 @@
         return;
 
     ASSERT(m_suspendIfNeededCalled);
-    ASSERT(m_scriptExecutionContext->isContextThread());
-    m_scriptExecutionContext->willDestroyActiveDOMObject(this);
+
+    // ActiveDOMObject may be inherited by a sub-class whose life-cycle
+    // exceeds that of the associated ScriptExecutionContext. In those cases,
+    // m_scriptExecutionContext would/should have been nullified by
+    // ContextDestructionObserver::contextDestroyed() (which we implement /
+    // inherit). Hence, we should ensure that this is not 0 before use it
+    // here.
+    if (m_scriptExecutionContext) {
+        ASSERT(m_scriptExecutionContext->isContextThread());
+        m_scriptExecutionContext->willDestroyActiveDOMObject(this);
+    }
 }
 
 void ActiveDOMObject::suspendIfNeeded()
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to