Changeset 56293 in webkit


Ignore:
Timestamp:
Mar 19, 2010 8:03:37 PM (14 years ago)
Author:
eric@webkit.org
Message:

2010-03-19 Eric Uhrhane <ericu@chromium.org>

Reviewed by Dmitry Titov.

Refactor DatabaseTracker.cpp for thread safety
https://bugs.webkit.org/show_bug.cgi?id=34991

This enables calling into DatabaseTracker from multiple context threads,
as will happen once Workers can access the Database. It required a fair
amount of reshuffling of locks. I ended up splitting the public
interface [calls that take locks and call private functions] from the
implementations [calls that assert that locks are already held] in order
to avoid lock conflicts. I also had to make sure we weren't sharing
Strings or SecurityOrigins across threads.

No new tests.

Allow access to database handles from multiple threads IFF SQLite is new enough and the user requests it.

  • platform/sql/SQLiteDatabase.cpp: (WebCore::SQLiteDatabase::SQLiteDatabase): (WebCore::SQLiteDatabase::disableThreadingChecks):
  • platform/sql/SQLiteDatabase.h: (WebCore::SQLiteDatabase::sqlite3Handle): (WebCore::SQLiteDatabase::disableThreadingChecks):

Remove an asynchronous call from Database::close back to the execution thread, so that cleanup can be more deterministic.

  • storage/Database.cpp: (WebCore::Database::markAsDeletedAndClose): (WebCore::Database::close):
  • storage/Database.h: (WebCore::Database::):
  • storage/DatabaseDetails.h: (WebCore::DatabaseDetails::DatabaseDetails): (WebCore::DatabaseDetails::thread):
  • storage/DatabaseTask.cpp: (WebCore::DatabaseCloseTask::doPerformTask):
  • storage/DatabaseThread.cpp: (WebCore::DatabaseThread::databaseThread):

Any Strings that get stored in DatabaseTracker, and any Strings returned from DatabaseTracker, are now threadsafeCopies.
Public functions now take all needed locks, then generally call only private functions [there are a few exceptions: deletion functions and origins()].
Private functions no longer take locks.
m_quotaMapGuard becomes m_databaseGuard, and now protects m_database, m_quotaMap, m_proposedDatabases, m_databaseDirectoryPath, m_originsBeingDeleted, m_beingCreated, and m_beingDeleted.
m_proposedDatabases replaces m_proposedDatabase, to account for reentrancy.

  • storage/DatabaseTracker.h:
  • storage/DatabaseTracker.cpp: (WebCore::DatabaseTracker::originQuotaManagerNoLock): (WebCore::DatabaseTracker::originQuotaManager): (WebCore::DatabaseTracker::DatabaseTracker): (WebCore::DatabaseTracker::setDatabaseDirectoryPath): (WebCore::DatabaseTracker::databaseDirectoryPath): (WebCore::DatabaseTracker::trackerDatabasePath): (WebCore::DatabaseTracker::openTrackerDatabase): (WebCore::DatabaseTracker::canEstablishDatabase): (WebCore::DatabaseTracker::hasEntryForOriginNoLock): (WebCore::DatabaseTracker::hasEntryForOrigin): (WebCore::DatabaseTracker::hasEntryForDatabase): (WebCore::DatabaseTracker::originPath): (WebCore::DatabaseTracker::fullPathForDatabaseNoLock): (WebCore::DatabaseTracker::fullPathForDatabase): (WebCore::DatabaseTracker::populateOrigins): (WebCore::DatabaseTracker::origins): (WebCore::DatabaseTracker::databaseNamesForOriginNoLock): (WebCore::DatabaseTracker::databaseNamesForOrigin): (WebCore::DatabaseTracker::detailsForNameAndOrigin): (WebCore::DatabaseTracker::setDatabaseDetails): (WebCore::DatabaseTracker::usageForDatabase): (WebCore::DatabaseTracker::addOpenDatabase): (WebCore::DatabaseTracker::removeOpenDatabase): (WebCore::DatabaseTracker::usageForOriginNoLock): (WebCore::DatabaseTracker::usageForOrigin): (WebCore::DatabaseTracker::quotaForOriginNoLock): (WebCore::DatabaseTracker::quotaForOrigin): (WebCore::DatabaseTracker::setQuota): (WebCore::DatabaseTracker::addDatabase): (WebCore::DatabaseTracker::deleteAllDatabases): (WebCore::DatabaseTracker::deleteOrigin): (WebCore::DatabaseTracker::deleteDatabase): (WebCore::DatabaseTracker::deleteDatabaseFile): (WebCore::DatabaseTracker::setClient): (WebCore::DatabaseTracker::scheduleNotifyDatabaseChanged): (WebCore::DatabaseTracker::notifyDatabasesChanged):

These functions keep track of in-progress deletions and creations, so that we can make sure nobody every deletes a database file while a live database is using it.
(WebCore::DatabaseTracker::canCreateDatabase):
(WebCore::DatabaseTracker::recordCreatingDatabase):
(WebCore::DatabaseTracker::doneCreatingDatabase):
(WebCore::DatabaseTracker::creatingDatabase):
(WebCore::DatabaseTracker::canDeleteDatabase):
(WebCore::DatabaseTracker::recordDeletingDatabase):
(WebCore::DatabaseTracker::doneDeletingDatabase):
(WebCore::DatabaseTracker::deletingDatabase):
(WebCore::DatabaseTracker::canDeleteOrigin):
(WebCore::DatabaseTracker::deletingOrigin):
(WebCore::DatabaseTracker::recordDeletingOrigin):
(WebCore::DatabaseTracker::doneDeletingOrigin):

Any SecurityOrigins stored in OriginQuotaManager are now threadsafeCopies of inputs. There's a new tryLock() function in addition to the existing lock().

  • storage/OriginQuotaManager.cpp: (WebCore::OriginQuotaManager::tryLock): (WebCore::OriginQuotaManager::trackOrigin):
  • storage/OriginQuotaManager.h:
  • page/SecurityOrigin.cpp: (WebCore::SecurityOrigin::databaseIdentifier): Removed DEFINE_STATIC_LOCAL wrapper on a local variable; it appears to have been a small optimization, but it's not thread-safe.
Location:
trunk/WebCore
Files:
13 edited

Legend:

Unmodified
Added
Removed
  • trunk/WebCore/ChangeLog

    r56292 r56293  
     12010-03-19  Eric Uhrhane  <ericu@chromium.org>
     2
     3        Reviewed by Dmitry Titov.
     4
     5        Refactor DatabaseTracker.cpp for thread safety
     6        https://bugs.webkit.org/show_bug.cgi?id=34991
     7
     8        This enables calling into DatabaseTracker from multiple context threads,
     9        as will happen once Workers can access the Database.  It required a fair
     10        amount of reshuffling of locks.  I ended up splitting the public
     11        interface [calls that take locks and call private functions] from the
     12        implementations [calls that assert that locks are already held] in order
     13        to avoid lock conflicts.  I also had to make sure we weren't sharing
     14        Strings or SecurityOrigins across threads.
     15
     16        No new tests.
     17
     18        Allow access to database handles from multiple threads IFF SQLite is new enough and the user requests it.
     19
     20        * platform/sql/SQLiteDatabase.cpp:
     21        (WebCore::SQLiteDatabase::SQLiteDatabase):
     22        (WebCore::SQLiteDatabase::disableThreadingChecks):
     23        * platform/sql/SQLiteDatabase.h:
     24        (WebCore::SQLiteDatabase::sqlite3Handle):
     25        (WebCore::SQLiteDatabase::disableThreadingChecks):
     26
     27        Remove an asynchronous call from Database::close back to the execution thread, so that cleanup can be more deterministic.
     28
     29        * storage/Database.cpp:
     30        (WebCore::Database::markAsDeletedAndClose):
     31        (WebCore::Database::close):
     32        * storage/Database.h:
     33        (WebCore::Database::):
     34        * storage/DatabaseDetails.h:
     35        (WebCore::DatabaseDetails::DatabaseDetails):
     36        (WebCore::DatabaseDetails::thread):
     37        * storage/DatabaseTask.cpp:
     38        (WebCore::DatabaseCloseTask::doPerformTask):
     39        * storage/DatabaseThread.cpp:
     40        (WebCore::DatabaseThread::databaseThread):
     41
     42        Any Strings that get stored in DatabaseTracker, and any Strings returned from DatabaseTracker, are now threadsafeCopies.
     43        Public functions now take all needed locks, then generally call only private functions [there are a few exceptions: deletion functions and origins()].
     44        Private functions no longer take locks.
     45        m_quotaMapGuard becomes m_databaseGuard, and now protects m_database, m_quotaMap, m_proposedDatabases, m_databaseDirectoryPath, m_originsBeingDeleted, m_beingCreated, and m_beingDeleted.
     46        m_proposedDatabases replaces m_proposedDatabase, to account for reentrancy.
     47
     48        * storage/DatabaseTracker.h:
     49        * storage/DatabaseTracker.cpp:
     50        (WebCore::DatabaseTracker::originQuotaManagerNoLock):
     51        (WebCore::DatabaseTracker::originQuotaManager):
     52        (WebCore::DatabaseTracker::DatabaseTracker):
     53        (WebCore::DatabaseTracker::setDatabaseDirectoryPath):
     54        (WebCore::DatabaseTracker::databaseDirectoryPath):
     55        (WebCore::DatabaseTracker::trackerDatabasePath):
     56        (WebCore::DatabaseTracker::openTrackerDatabase):
     57        (WebCore::DatabaseTracker::canEstablishDatabase):
     58        (WebCore::DatabaseTracker::hasEntryForOriginNoLock):
     59        (WebCore::DatabaseTracker::hasEntryForOrigin):
     60        (WebCore::DatabaseTracker::hasEntryForDatabase):
     61        (WebCore::DatabaseTracker::originPath):
     62        (WebCore::DatabaseTracker::fullPathForDatabaseNoLock):
     63        (WebCore::DatabaseTracker::fullPathForDatabase):
     64        (WebCore::DatabaseTracker::populateOrigins):
     65        (WebCore::DatabaseTracker::origins):
     66        (WebCore::DatabaseTracker::databaseNamesForOriginNoLock):
     67        (WebCore::DatabaseTracker::databaseNamesForOrigin):
     68        (WebCore::DatabaseTracker::detailsForNameAndOrigin):
     69        (WebCore::DatabaseTracker::setDatabaseDetails):
     70        (WebCore::DatabaseTracker::usageForDatabase):
     71        (WebCore::DatabaseTracker::addOpenDatabase):
     72        (WebCore::DatabaseTracker::removeOpenDatabase):
     73        (WebCore::DatabaseTracker::usageForOriginNoLock):
     74        (WebCore::DatabaseTracker::usageForOrigin):
     75        (WebCore::DatabaseTracker::quotaForOriginNoLock):
     76        (WebCore::DatabaseTracker::quotaForOrigin):
     77        (WebCore::DatabaseTracker::setQuota):
     78        (WebCore::DatabaseTracker::addDatabase):
     79        (WebCore::DatabaseTracker::deleteAllDatabases):
     80        (WebCore::DatabaseTracker::deleteOrigin):
     81        (WebCore::DatabaseTracker::deleteDatabase):
     82        (WebCore::DatabaseTracker::deleteDatabaseFile):
     83        (WebCore::DatabaseTracker::setClient):
     84        (WebCore::DatabaseTracker::scheduleNotifyDatabaseChanged):
     85        (WebCore::DatabaseTracker::notifyDatabasesChanged):
     86
     87        These functions keep track of in-progress deletions and creations, so that we can make sure nobody every deletes a database file while a live database is using it.
     88        (WebCore::DatabaseTracker::canCreateDatabase):
     89        (WebCore::DatabaseTracker::recordCreatingDatabase):
     90        (WebCore::DatabaseTracker::doneCreatingDatabase):
     91        (WebCore::DatabaseTracker::creatingDatabase):
     92        (WebCore::DatabaseTracker::canDeleteDatabase):
     93        (WebCore::DatabaseTracker::recordDeletingDatabase):
     94        (WebCore::DatabaseTracker::doneDeletingDatabase):
     95        (WebCore::DatabaseTracker::deletingDatabase):
     96        (WebCore::DatabaseTracker::canDeleteOrigin):
     97        (WebCore::DatabaseTracker::deletingOrigin):
     98        (WebCore::DatabaseTracker::recordDeletingOrigin):
     99        (WebCore::DatabaseTracker::doneDeletingOrigin):
     100
     101        Any SecurityOrigins stored in OriginQuotaManager are now threadsafeCopies of inputs.  There's a new tryLock() function in addition to the existing lock().
     102
     103        * storage/OriginQuotaManager.cpp:
     104        (WebCore::OriginQuotaManager::tryLock):
     105        (WebCore::OriginQuotaManager::trackOrigin):
     106        * storage/OriginQuotaManager.h:
     107
     108        * page/SecurityOrigin.cpp:
     109        (WebCore::SecurityOrigin::databaseIdentifier):
     110        Removed DEFINE_STATIC_LOCAL wrapper on a local variable; it appears to have been a small optimization, but it's not thread-safe.
     111
    11122010-03-19  Luiz Agostini  <luiz.agostini@openbossa.org>
    2113
  • trunk/WebCore/page/SecurityOrigin.cpp

    r56139 r56293  
    463463String SecurityOrigin::databaseIdentifier() const
    464464{
    465     DEFINE_STATIC_LOCAL(String, separatorString, (&SeparatorCharacter, 1));
     465    String separatorString(&SeparatorCharacter, 1);
    466466
    467467    if (m_encodedHost.isEmpty())
  • trunk/WebCore/platform/sql/SQLiteDatabase.cpp

    r54393 r56293  
    4949    , m_pageSize(-1)
    5050    , m_transactionInProgress(false)
     51    , m_sharable(false)
    5152    , m_openingThread(0)
    5253{
     
    252253    return sqlite3_errmsg(m_db);
    253254}
     255
     256#ifndef NDEBUG
     257void SQLiteDatabase::disableThreadingChecks()
     258{
     259    // This doesn't guarantee that SQList was compiled with -DTHREADSAFE, or that you haven't turned off the mutexes.
     260#if SQLITE_VERSION_NUMBER >= 3003001
     261    m_sharable = true;
     262#else
     263    ASSERT(0); // Your SQLite doesn't support sharing handles across threads.
     264#endif
     265}
     266#endif
    254267
    255268int SQLiteDatabase::authorizerFunction(void* userData, int actionCode, const char* parameter1, const char* parameter2, const char* /*databaseName*/, const char* /*trigger_or_view*/)
  • trunk/WebCore/platform/sql/SQLiteDatabase.h

    r54393 r56293  
    9898   
    9999    sqlite3* sqlite3Handle() const {
    100         ASSERT(currentThread() == m_openingThread);
     100        ASSERT(m_sharable || currentThread() == m_openingThread);
    101101        return m_db;
    102102    }
     
    108108    void unlock();
    109109    bool isAutoCommitOn() const;
     110
     111    // Set this flag to allow access from multiple threads.  Not all multi-threaded accesses are safe!
     112    // See http://www.sqlite.org/cvstrac/wiki?p=MultiThreading for more info.
     113#ifndef NDEBUG
     114    void disableThreadingChecks();
     115#else
     116    void disableThreadingChecks() {}
     117#endif
    110118
    111119private:
     
    121129   
    122130    bool m_transactionInProgress;
     131    bool m_sharable;
    123132   
    124133    Mutex m_authorizerLock;
  • trunk/WebCore/storage/Database.cpp

    r55823 r56293  
    390390    m_scriptExecutionContext->databaseThread()->scheduleImmediateTask(task.release());
    391391    synchronizer.waitForTaskCompletion();
     392
     393    // DatabaseCloseTask tells Database::close not to do these two removals so that we can do them here synchronously.
     394    m_scriptExecutionContext->removeOpenDatabase(this);
     395    DatabaseTracker::tracker().removeOpenDatabase(this);
    392396}
    393397
     
    416420};
    417421
    418 void Database::close()
     422void Database::close(ClosePolicy policy)
    419423{
    420424    RefPtr<Database> protect = this;
     
    445449
    446450    m_scriptExecutionContext->databaseThread()->unscheduleDatabaseTasks(this);
    447     m_scriptExecutionContext->postTask(ContextRemoveOpenDatabaseTask::create(this));
     451    if (policy == RemoveDatabaseFromContext)
     452        m_scriptExecutionContext->postTask(ContextRemoveOpenDatabaseTask::create(this));
    448453}
    449454
  • trunk/WebCore/storage/Database.h

    r55823 r56293  
    110110    bool deleted() const { return m_deleted; }
    111111
    112     void close();
     112    enum ClosePolicy { DoNotRemoveDatabaseFromContext, RemoveDatabaseFromContext };
     113    void close(ClosePolicy);
    113114    bool opened() const { return m_opened; }
    114115
  • trunk/WebCore/storage/DatabaseDetails.h

    r47808 r56293  
    4242        , m_currentUsage(0)
    4343    {
     44#ifndef NDEBUG
     45        m_thread = currentThread();
     46#endif
    4447    }
    4548
     
    5053        , m_currentUsage(currentUsage)
    5154    {
     55#ifndef NDEBUG
     56        m_thread = currentThread();
     57#endif
    5258    }
    5359
     
    5662    unsigned long long expectedUsage() const { return m_expectedUsage; }
    5763    unsigned long long currentUsage() const { return m_currentUsage; }
     64#ifndef NDEBUG
     65    ThreadIdentifier thread() const { return m_thread; }
     66#endif
    5867
    5968private:
     
    6271    unsigned long long m_expectedUsage;
    6372    unsigned long long m_currentUsage;
    64 
     73#ifndef NDEBUG
     74    ThreadIdentifier m_thread;
     75#endif
    6576};
    6677
  • trunk/WebCore/storage/DatabaseTask.cpp

    r50360 r56293  
    118118void DatabaseCloseTask::doPerformTask()
    119119{
    120     database()->close();
     120    // Tell the database not to call back to the context thread; we'll handle it.
     121    database()->close(Database::DoNotRemoveDatabaseFromContext);
    121122}
    122123
  • trunk/WebCore/storage/DatabaseThread.cpp

    r55429 r56293  
    114114        DatabaseSet::iterator end = openSetCopy.end();
    115115        for (DatabaseSet::iterator it = openSetCopy.begin(); it != end; ++it)
    116            (*it)->close();
     116           (*it)->close(Database::RemoveDatabaseFromContext);
    117117    }
    118118
  • trunk/WebCore/storage/DatabaseTracker.cpp

    r54506 r56293  
    5252namespace WebCore {
    5353
    54 OriginQuotaManager& DatabaseTracker::originQuotaManager()
    55 {
    56     populateOrigins();
     54OriginQuotaManager& DatabaseTracker::originQuotaManagerNoLock()
     55{
    5756    ASSERT(m_quotaManager);
    5857    return *m_quotaManager;
    5958}
    6059
     60OriginQuotaManager& DatabaseTracker::originQuotaManager()
     61{
     62    MutexLocker lockDatabase(m_databaseGuard);
     63    populateOrigins();
     64    return originQuotaManagerNoLock();
     65}
     66
    6167DatabaseTracker& DatabaseTracker::tracker()
    6268{
     
    6773DatabaseTracker::DatabaseTracker()
    6874    : m_client(0)
    69     , m_proposedDatabase(0)
    70 #ifndef NDEBUG
    71     , m_thread(currentThread())
    72 #endif
    7375{
    7476    SQLiteFileSystem::registerSQLiteVFS();
     
    7779void DatabaseTracker::setDatabaseDirectoryPath(const String& path)
    7880{
    79     ASSERT(currentThread() == m_thread);
     81    MutexLocker lockDatabase(m_databaseGuard);
    8082    ASSERT(!m_database.isOpen());
    81     m_databaseDirectoryPath = path;
    82 }
    83 
    84 const String& DatabaseTracker::databaseDirectoryPath() const
    85 {
    86     ASSERT(currentThread() == m_thread);
    87     return m_databaseDirectoryPath;
     83    m_databaseDirectoryPath = path.threadsafeCopy();
     84}
     85
     86String DatabaseTracker::databaseDirectoryPath() const
     87{
     88    return m_databaseDirectoryPath.threadsafeCopy();
    8889}
    8990
    9091String DatabaseTracker::trackerDatabasePath() const
    9192{
    92     ASSERT(currentThread() == m_thread);
    9393    return SQLiteFileSystem::appendDatabaseFileNameToPath(m_databaseDirectoryPath, "Databases.db");
    9494}
     
    9696void DatabaseTracker::openTrackerDatabase(bool createIfDoesNotExist)
    9797{
    98     ASSERT(currentThread() == m_thread);
     98    ASSERT(!m_databaseGuard.tryLock());
    9999
    100100    if (m_database.isOpen())
     
    107107    if (!m_database.open(databasePath)) {
    108108        // FIXME: What do do here?
    109         return;
    110     }
     109        LOG_ERROR("Failed to open databasePath %s.", databasePath.ascii().data());
     110        return;
     111    }
     112    m_database.disableThreadingChecks();
    111113    if (!m_database.tableExists("Origins")) {
    112114        if (!m_database.executeCommand("CREATE TABLE Origins (origin TEXT UNIQUE ON CONFLICT REPLACE, quota INTEGER NOT NULL ON CONFLICT FAIL);")) {
    113115            // FIXME: and here
     116            LOG_ERROR("Failed to create Origins table");
    114117        }
    115118    }
     
    117120        if (!m_database.executeCommand("CREATE TABLE Databases (guid INTEGER PRIMARY KEY AUTOINCREMENT, origin TEXT, name TEXT, displayName TEXT, estimatedSize INTEGER, path TEXT);")) {
    118121            // FIXME: and here
     122            LOG_ERROR("Failed to create Databases table");
    119123        }
    120124    }
     
    123127bool DatabaseTracker::canEstablishDatabase(ScriptExecutionContext* context, const String& name, const String& displayName, unsigned long estimatedSize)
    124128{
    125     ASSERT(currentThread() == m_thread);
    126 
    127     // Populate the origins before we establish a database; this guarantees that quotaForOrigin
    128     // can run on the database thread later.
    129     populateOrigins();
    130 
    131129    SecurityOrigin* origin = context->securityOrigin();
    132 
    133     // Since we're imminently opening a database within this context's origin, make sure this origin is being tracked by the QuotaTracker
    134     // by fetching it's current usage now
    135     unsigned long long usage = usageForOrigin(origin);
    136 
    137     // If a database already exists, ignore the passed-in estimated size and say it's OK.
    138     if (hasEntryForDatabase(origin, name))
    139         return true;
    140 
    141     // If the database will fit, allow its creation.
    142     unsigned long long requirement = usage + max(1UL, estimatedSize);
    143     if (requirement < usage)
    144         return false; // If the estimated size is so big it causes an overflow, don't allow creation.
     130    ProposedDatabase details;
     131
     132    unsigned long long requirement;
     133    unsigned long long tempUsage;
     134    {
     135        Locker<OriginQuotaManager> locker(originQuotaManager());
     136        MutexLocker lockDatabase(m_databaseGuard);
     137
     138        if (!canCreateDatabase(origin, name))
     139            return false;
     140
     141        recordCreatingDatabase(origin, name);
     142
     143        populateOrigins();
     144
     145        // Since we're imminently opening a database within this context's origin, make sure this origin is being tracked by the QuotaTracker
     146        // by fetching its current usage now.
     147        unsigned long long usage = usageForOriginNoLock(origin);
     148
     149        // If a database already exists, ignore the passed-in estimated size and say it's OK.
     150        if (hasEntryForDatabase(origin, name))
     151            return true;
     152
     153        // If the database will fit, allow its creation.
     154        requirement = usage + max(1UL, estimatedSize);
     155        tempUsage = usage;
     156        if (requirement < usage) {
     157            doneCreatingDatabase(origin, name);
     158            return false; // If the estimated size is so big it causes an overflow, don't allow creation.
     159        }
     160        if (requirement <= quotaForOriginNoLock(origin))
     161            return true;
     162
     163        // Give the chrome client a chance to increase the quota.
     164        // Temporarily make the details of the proposed database available, so the client can get at them.
     165        // FIXME: We should really just pass the details into this call, rather than using m_proposedDatabases.
     166        details = ProposedDatabase(origin->threadsafeCopy(), DatabaseDetails(name.threadsafeCopy(), displayName.threadsafeCopy(), estimatedSize, 0));
     167        m_proposedDatabases.add(&details);
     168    }
     169    // Drop all locks before calling out; we don't know what they'll do.
     170    context->databaseExceededQuota(name);
     171    {
     172        MutexLocker lockDatabase(m_databaseGuard);
     173        m_proposedDatabases.remove(&details);
     174    }
     175
     176    // If the database will fit now, allow its creation.
    145177    if (requirement <= quotaForOrigin(origin))
    146178        return true;
    147179
    148     // Give the chrome client a chance to increase the quota.
    149     // Temporarily make the details of the proposed database available, so the client can get at them.
    150     pair<SecurityOrigin*, DatabaseDetails> details(origin, DatabaseDetails(name, displayName, estimatedSize, 0));
    151     m_proposedDatabase = &details;
    152     context->databaseExceededQuota(name);
    153     m_proposedDatabase = 0;
    154 
    155     // If the database will fit now, allow its creation.
    156     return requirement <= quotaForOrigin(origin);
     180    doneCreatingDatabase(origin, name);
     181    return false;
     182}
     183
     184bool DatabaseTracker::hasEntryForOriginNoLock(SecurityOrigin* origin)
     185{
     186    ASSERT(!m_databaseGuard.tryLock());
     187    ASSERT(m_quotaMap);
     188    return m_quotaMap->contains(origin);
    157189}
    158190
    159191bool DatabaseTracker::hasEntryForOrigin(SecurityOrigin* origin)
    160192{
    161     ASSERT(currentThread() == m_thread);
     193    MutexLocker lockDatabase(m_databaseGuard);
    162194    populateOrigins();
    163     MutexLocker lockQuotaMap(m_quotaMapGuard);
    164     return m_quotaMap->contains(origin);
     195    return hasEntryForOriginNoLock(origin);
    165196}
    166197
    167198bool DatabaseTracker::hasEntryForDatabase(SecurityOrigin* origin, const String& databaseIdentifier)
    168199{
    169     ASSERT(currentThread() == m_thread);
     200    ASSERT(!m_databaseGuard.tryLock());
    170201    openTrackerDatabase(false);
    171202    if (!m_database.isOpen())
     
    194225String DatabaseTracker::originPath(SecurityOrigin* origin) const
    195226{
    196     ASSERT(currentThread() == m_thread);
    197     return SQLiteFileSystem::appendDatabaseFileNameToPath(m_databaseDirectoryPath, origin->databaseIdentifier());
    198 }
    199 
    200 String DatabaseTracker::fullPathForDatabase(SecurityOrigin* origin, const String& name, bool createIfNotExists)
    201 {
    202     ASSERT(currentThread() == m_thread);
    203 
    204     if (m_proposedDatabase && m_proposedDatabase->first == origin && m_proposedDatabase->second.name() == name)
    205         return String();
     227    return SQLiteFileSystem::appendDatabaseFileNameToPath(m_databaseDirectoryPath.threadsafeCopy(), origin->databaseIdentifier());
     228}
     229
     230String DatabaseTracker::fullPathForDatabaseNoLock(SecurityOrigin* origin, const String& name, bool createIfNotExists)
     231{
     232    ASSERT(!m_databaseGuard.tryLock());
     233    ASSERT(!originQuotaManagerNoLock().tryLock());
     234
     235    for (HashSet<ProposedDatabase*>::iterator iter = m_proposedDatabases.begin(); iter != m_proposedDatabases.end(); ++iter)
     236        if ((*iter)->first == origin && (*iter)->second.name() == name)
     237            return String();
    206238
    207239    String originIdentifier = origin->databaseIdentifier();
     
    213245
    214246    // See if we have a path for this database yet
    215     openTrackerDatabase(false);
    216247    if (!m_database.isOpen())
    217248        return String();
     
    232263
    233264    if (result != SQLResultDone) {
    234         LOG_ERROR("Failed to retrieve filename from Database Tracker for origin %s, name %s", origin->databaseIdentifier().ascii().data(), name.ascii().data());
     265        LOG_ERROR("Failed to retrieve filename from Database Tracker for origin %s, name %s", originIdentifier.ascii().data(), name.ascii().data());
    235266        return String();
    236267    }
    237268    statement.finalize();
    238269
    239     String fileName = SQLiteFileSystem::getFileNameForNewDatabase(originPath, name, origin->databaseIdentifier(), &m_database);
     270    String fileName = SQLiteFileSystem::getFileNameForNewDatabase(originPath, name, originIdentifier, &m_database);
    240271    if (!addDatabase(origin, name, fileName))
    241272        return String();
     
    244275    // to the quota manager now
    245276    String fullFilePath = SQLiteFileSystem::appendDatabaseFileNameToPath(originPath, fileName);
    246     {
    247         Locker<OriginQuotaManager> locker(originQuotaManager());
    248         if (originQuotaManager().tracksOrigin(origin))
    249             originQuotaManager().addDatabase(origin, name, fullFilePath);
    250     }
     277    if (originQuotaManagerNoLock().tracksOrigin(origin))
     278        originQuotaManagerNoLock().addDatabase(origin, name, fullFilePath);
    251279
    252280    return fullFilePath;
    253281}
    254282
     283String DatabaseTracker::fullPathForDatabase(SecurityOrigin* origin, const String& name, bool createIfNotExists)
     284{
     285    Locker<OriginQuotaManager> locker(originQuotaManager());
     286    MutexLocker lockDatabase(m_databaseGuard);
     287    populateOrigins();
     288
     289    return fullPathForDatabaseNoLock(origin, name, createIfNotExists).threadsafeCopy();
     290}
     291
    255292void DatabaseTracker::populateOrigins()
    256293{
     294    ASSERT(!m_databaseGuard.tryLock());
    257295    if (m_quotaMap)
    258296        return;
    259297
    260     ASSERT(currentThread() == m_thread);
    261 
    262298    m_quotaMap.set(new QuotaMap);
    263     m_quotaManager.set(new OriginQuotaManager);
     299    if (!m_quotaManager)
     300        m_quotaManager.set(new OriginQuotaManager);
    264301
    265302    openTrackerDatabase(false);
     
    269306    SQLiteStatement statement(m_database, "SELECT origin, quota FROM Origins");
    270307
    271     if (statement.prepare() != SQLResultOk)
    272         return;
     308    if (statement.prepare() != SQLResultOk) {
     309        LOG_ERROR("Failed to prepare statement.");
     310        return;
     311    }
    273312
    274313    int result;
    275314    while ((result = statement.step()) == SQLResultRow) {
    276315        RefPtr<SecurityOrigin> origin = SecurityOrigin::createFromDatabaseIdentifier(statement.getColumnText(0));
    277         m_quotaMap->set(origin.get(), statement.getColumnInt64(1));
     316        m_quotaMap->set(origin.get()->threadsafeCopy(), statement.getColumnInt64(1));
    278317    }
    279318
    280319    if (result != SQLResultDone)
    281         LOG_ERROR("Failed to read in all origins from the database");
     320        LOG_ERROR("Failed to read in all origins from the database.");
    282321}
    283322
    284323void DatabaseTracker::origins(Vector<RefPtr<SecurityOrigin> >& result)
    285324{
    286     ASSERT(currentThread() == m_thread);
     325    MutexLocker lockDatabase(m_databaseGuard);
    287326    populateOrigins();
    288     MutexLocker lockQuotaMap(m_quotaMapGuard);
     327    ASSERT(m_quotaMap);
    289328    copyKeysToVector(*m_quotaMap, result);
    290329}
    291330
    292 bool DatabaseTracker::databaseNamesForOrigin(SecurityOrigin* origin, Vector<String>& resultVector)
    293 {
    294     ASSERT(currentThread() == m_thread);
     331bool DatabaseTracker::databaseNamesForOriginNoLock(SecurityOrigin* origin, Vector<String>& resultVector)
     332{
     333    ASSERT(!m_databaseGuard.tryLock());
    295334    openTrackerDatabase(false);
    296335    if (!m_database.isOpen())
     
    316355}
    317356
     357bool DatabaseTracker::databaseNamesForOrigin(SecurityOrigin* origin, Vector<String>& resultVector)
     358{
     359    MutexLocker lockDatabase(m_databaseGuard);
     360    Vector<String> temp;
     361    if (!databaseNamesForOriginNoLock(origin, temp))
     362        return false;
     363
     364    for (Vector<String>::iterator iter = temp.begin(); iter != temp.end(); ++iter)
     365        resultVector.append(iter->threadsafeCopy());
     366    return true;
     367}
     368
    318369DatabaseDetails DatabaseTracker::detailsForNameAndOrigin(const String& name, SecurityOrigin* origin)
    319370{
    320     ASSERT(currentThread() == m_thread);
    321 
    322     if (m_proposedDatabase && m_proposedDatabase->first == origin && m_proposedDatabase->second.name() == name)
    323         return m_proposedDatabase->second;
    324 
    325371    String originIdentifier = origin->databaseIdentifier();
    326 
    327     openTrackerDatabase(false);
    328     if (!m_database.isOpen())
    329         return DatabaseDetails();
    330     SQLiteStatement statement(m_database, "SELECT displayName, estimatedSize FROM Databases WHERE origin=? AND name=?");
    331     if (statement.prepare() != SQLResultOk)
    332         return DatabaseDetails();
    333 
    334     statement.bindText(1, originIdentifier);
    335     statement.bindText(2, name);
    336 
    337     int result = statement.step();
    338     if (result == SQLResultDone)
    339         return DatabaseDetails();
    340 
    341     if (result != SQLResultRow) {
    342         LOG_ERROR("Error retrieving details for database %s in origin %s from tracker database", name.ascii().data(), originIdentifier.ascii().data());
    343         return DatabaseDetails();
    344     }
    345 
    346     return DatabaseDetails(name, statement.getColumnText(0), statement.getColumnInt64(1), usageForDatabase(name, origin));
     372    String displayName;
     373    int64_t expectedUsage;
     374
     375    {
     376        MutexLocker lockDatabase(m_databaseGuard);
     377
     378        for (HashSet<ProposedDatabase*>::iterator iter = m_proposedDatabases.begin(); iter != m_proposedDatabases.end(); ++iter)
     379            if ((*iter)->first == origin && (*iter)->second.name() == name) {
     380                ASSERT((*iter)->second.thread() == currentThread());
     381                return (*iter)->second;
     382            }
     383
     384        openTrackerDatabase(false);
     385        if (!m_database.isOpen())
     386            return DatabaseDetails();
     387        SQLiteStatement statement(m_database, "SELECT displayName, estimatedSize FROM Databases WHERE origin=? AND name=?");
     388        if (statement.prepare() != SQLResultOk)
     389            return DatabaseDetails();
     390
     391        statement.bindText(1, originIdentifier);
     392        statement.bindText(2, name);
     393
     394        int result = statement.step();
     395        if (result == SQLResultDone)
     396            return DatabaseDetails();
     397
     398        if (result != SQLResultRow) {
     399            LOG_ERROR("Error retrieving details for database %s in origin %s from tracker database", name.ascii().data(), originIdentifier.ascii().data());
     400            return DatabaseDetails();
     401        }
     402        displayName = statement.getColumnText(0);
     403        expectedUsage = statement.getColumnInt64(1);
     404    }
     405
     406    return DatabaseDetails(name, displayName, expectedUsage, usageForDatabase(name, origin));
    347407}
    348408
    349409void DatabaseTracker::setDatabaseDetails(SecurityOrigin* origin, const String& name, const String& displayName, unsigned long estimatedSize)
    350410{
    351     ASSERT(currentThread() == m_thread);
    352 
    353411    String originIdentifier = origin->databaseIdentifier();
    354412    int64_t guid = 0;
     413
     414    MutexLocker lockDatabase(m_databaseGuard);
    355415
    356416    openTrackerDatabase(true);
     
    401461unsigned long long DatabaseTracker::usageForDatabase(const String& name, SecurityOrigin* origin)
    402462{
    403     ASSERT(currentThread() == m_thread);
    404463    String path = fullPathForDatabase(origin, name, false);
    405464    if (path.isEmpty())
     
    423482    if (!nameMap) {
    424483        nameMap = new DatabaseNameMap;
    425         m_openDatabaseMap->set(database->securityOrigin(), nameMap);
     484        m_openDatabaseMap->set(database->securityOrigin()->threadsafeCopy(), nameMap);
    426485    }
    427486
     
    429488    if (!databaseSet) {
    430489        databaseSet = new DatabaseSet;
    431         nameMap->set(name, databaseSet);
     490        nameMap->set(name.threadsafeCopy(), databaseSet);
    432491    }
    433492
     
    435494
    436495    LOG(StorageAPI, "Added open Database %s (%p)\n", database->stringIdentifier().ascii().data(), database);
     496
     497    MutexLocker lockDatabase(m_databaseGuard);
     498    doneCreatingDatabase(database->securityOrigin(), database->stringIdentifier());
    437499}
    438500
     
    442504        return;
    443505
     506    Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager());
     507    MutexLocker lockDatabase(m_databaseGuard);
    444508    MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard);
    445509
     
    477541    m_openDatabaseMap->remove(database->securityOrigin());
    478542    delete nameMap;
     543    originQuotaManagerNoLock().removeOrigin(database->securityOrigin());
    479544}
    480545
     
    497562}
    498563
     564unsigned long long DatabaseTracker::usageForOriginNoLock(SecurityOrigin* origin)
     565{
     566    ASSERT(!originQuotaManagerNoLock().tryLock());
     567
     568    // Use the OriginQuotaManager mechanism to calculate the usage
     569    if (originQuotaManagerNoLock().tracksOrigin(origin))
     570        return originQuotaManagerNoLock().diskUsage(origin);
     571
     572    // If the OriginQuotaManager doesn't track this origin already, prime it to do so
     573    originQuotaManagerNoLock().trackOrigin(origin);
     574
     575    Vector<String> names;
     576    databaseNamesForOriginNoLock(origin, names);
     577
     578    for (unsigned i = 0; i < names.size(); ++i)
     579        originQuotaManagerNoLock().addDatabase(origin, names[i], fullPathForDatabaseNoLock(origin, names[i], false));
     580
     581    if (!originQuotaManagerNoLock().tracksOrigin(origin))
     582        return 0;
     583    return originQuotaManagerNoLock().diskUsage(origin);
     584}
     585
    499586unsigned long long DatabaseTracker::usageForOrigin(SecurityOrigin* origin)
    500587{
    501     ASSERT(currentThread() == m_thread);
    502588    Locker<OriginQuotaManager> locker(originQuotaManager());
    503589
    504     // Use the OriginQuotaManager mechanism to calculate the usage
    505     if (originQuotaManager().tracksOrigin(origin))
    506         return originQuotaManager().diskUsage(origin);
    507 
    508     // If the OriginQuotaManager doesn't track this origin already, prime it to do so
    509     originQuotaManager().trackOrigin(origin);
    510 
    511     Vector<String> names;
    512     databaseNamesForOrigin(origin, names);
    513 
    514     for (unsigned i = 0; i < names.size(); ++i)
    515         originQuotaManager().addDatabase(origin, names[i], fullPathForDatabase(origin, names[i], false));
    516 
    517     if (!originQuotaManager().tracksOrigin(origin))
    518         return 0;
    519     return originQuotaManager().diskUsage(origin);
     590    return usageForOriginNoLock(origin);
     591}
     592
     593unsigned long long DatabaseTracker::quotaForOriginNoLock(SecurityOrigin* origin)
     594{
     595    ASSERT(!m_databaseGuard.tryLock());
     596    ASSERT(m_quotaMap);
     597    return m_quotaMap->get(origin);
    520598}
    521599
    522600unsigned long long DatabaseTracker::quotaForOrigin(SecurityOrigin* origin)
    523601{
    524     ASSERT(currentThread() == m_thread || m_quotaMap);
     602    MutexLocker lockDatabase(m_databaseGuard);
    525603    populateOrigins();
    526     MutexLocker lockQuotaMap(m_quotaMapGuard);
    527     return m_quotaMap->get(origin);
     604    return quotaForOriginNoLock(origin);
    528605}
    529606
    530607void DatabaseTracker::setQuota(SecurityOrigin* origin, unsigned long long quota)
    531608{
    532     ASSERT(currentThread() == m_thread);
    533     if (quotaForOrigin(origin) == quota)
     609    MutexLocker lockDatabase(m_databaseGuard);
     610
     611    populateOrigins();
     612    if (quotaForOriginNoLock(origin) == quota)
    534613        return;
    535614
     
    538617        return;
    539618
    540     {
    541         MutexLocker lockQuotaMap(m_quotaMapGuard);
    542 
    543         if (!m_quotaMap->contains(origin)) {
    544             SQLiteStatement statement(m_database, "INSERT INTO Origins VALUES (?, ?)");
    545             if (statement.prepare() != SQLResultOk) {
     619    if (!m_quotaMap->contains(origin)) {
     620        SQLiteStatement statement(m_database, "INSERT INTO Origins VALUES (?, ?)");
     621        if (statement.prepare() != SQLResultOk) {
     622            LOG_ERROR("Unable to establish origin %s in the tracker", origin->databaseIdentifier().ascii().data());
     623        } else {
     624            statement.bindText(1, origin->databaseIdentifier());
     625            statement.bindInt64(2, quota);
     626
     627            if (statement.step() != SQLResultDone)
    546628                LOG_ERROR("Unable to establish origin %s in the tracker", origin->databaseIdentifier().ascii().data());
    547             } else {
    548                 statement.bindText(1, origin->databaseIdentifier());
    549                 statement.bindInt64(2, quota);
    550 
    551                 if (statement.step() != SQLResultDone)
    552                     LOG_ERROR("Unable to establish origin %s in the tracker", origin->databaseIdentifier().ascii().data());
    553             }
    554         } else {
    555             SQLiteStatement statement(m_database, "UPDATE Origins SET quota=? WHERE origin=?");
    556             bool error = statement.prepare() != SQLResultOk;
    557             if (!error) {
    558                 statement.bindInt64(1, quota);
    559                 statement.bindText(2, origin->databaseIdentifier());
    560 
    561                 error = !statement.executeCommand();
    562             }
    563 
    564             if (error)
    565                 LOG_ERROR("Failed to set quota %llu in tracker database for origin %s", quota, origin->databaseIdentifier().ascii().data());
    566         }
    567 
    568         // FIXME: Is it really OK to update the quota in memory if we failed to update it on disk?
    569         m_quotaMap->set(origin, quota);
    570     }
     629        }
     630    } else {
     631        SQLiteStatement statement(m_database, "UPDATE Origins SET quota=? WHERE origin=?");
     632        bool error = statement.prepare() != SQLResultOk;
     633        if (!error) {
     634            statement.bindInt64(1, quota);
     635            statement.bindText(2, origin->databaseIdentifier());
     636
     637            error = !statement.executeCommand();
     638        }
     639
     640        if (error)
     641            LOG_ERROR("Failed to set quota %llu in tracker database for origin %s", quota, origin->databaseIdentifier().ascii().data());
     642    }
     643
     644    // FIXME: Is it really OK to update the quota in memory if we failed to update it on disk?
     645    m_quotaMap->set(origin->threadsafeCopy(), quota);
    571646
    572647    if (m_client)
     
    576651bool DatabaseTracker::addDatabase(SecurityOrigin* origin, const String& name, const String& path)
    577652{
    578     ASSERT(currentThread() == m_thread);
     653    ASSERT(!m_databaseGuard.tryLock());
     654    ASSERT(m_quotaMap);
    579655    openTrackerDatabase(true);
    580656    if (!m_database.isOpen())
     
    582658
    583659    // New database should never be added until the origin has been established
    584     ASSERT(hasEntryForOrigin(origin));
     660    ASSERT(hasEntryForOriginNoLock(origin));
    585661
    586662    SQLiteStatement statement(m_database, "INSERT INTO Databases (origin, name, path) VALUES (?, ?, ?);");
     
    606682void DatabaseTracker::deleteAllDatabases()
    607683{
    608     ASSERT(currentThread() == m_thread);
    609 
    610684    Vector<RefPtr<SecurityOrigin> > originsCopy;
    611685    origins(originsCopy);
     
    615689}
    616690
     691// It is the caller's responsibility to make sure that nobody is trying to create, delete, open, or close databases in this origin while the deletion is
     692// taking place.
    617693void DatabaseTracker::deleteOrigin(SecurityOrigin* origin)
    618694{
    619     ASSERT(currentThread() == m_thread);
    620     openTrackerDatabase(false);
    621     if (!m_database.isOpen())
    622         return;
    623 
    624695    Vector<String> databaseNames;
    625     if (!databaseNamesForOrigin(origin, databaseNames)) {
    626         LOG_ERROR("Unable to retrieve list of database names for origin %s", origin->databaseIdentifier().ascii().data());
    627         return;
    628     }
    629 
     696    {
     697        MutexLocker lockDatabase(m_databaseGuard);
     698        openTrackerDatabase(false);
     699        if (!m_database.isOpen())
     700            return;
     701
     702        if (!databaseNamesForOriginNoLock(origin, databaseNames)) {
     703            LOG_ERROR("Unable to retrieve list of database names for origin %s", origin->databaseIdentifier().ascii().data());
     704            return;
     705        }
     706        if (!canDeleteOrigin(origin)) {
     707            LOG_ERROR("Tried to delete an origin (%s) while either creating database in it or already deleting it", origin->databaseIdentifier().ascii().data());
     708            ASSERT(false);
     709            return;
     710        }
     711        recordDeletingOrigin(origin);
     712    }
     713
     714    // We drop the lock here because holding locks during a call to deleteDatabaseFile will deadlock.
    630715    for (unsigned i = 0; i < databaseNames.size(); ++i) {
    631716        if (!deleteDatabaseFile(origin, databaseNames[i])) {
     
    635720    }
    636721
    637     SQLiteStatement statement(m_database, "DELETE FROM Databases WHERE origin=?");
    638     if (statement.prepare() != SQLResultOk) {
    639         LOG_ERROR("Unable to prepare deletion of databases from origin %s from tracker", origin->databaseIdentifier().ascii().data());
    640         return;
    641     }
    642 
    643     statement.bindText(1, origin->databaseIdentifier());
    644 
    645     if (!statement.executeCommand()) {
    646         LOG_ERROR("Unable to execute deletion of databases from origin %s from tracker", origin->databaseIdentifier().ascii().data());
    647         return;
    648     }
    649 
    650     SQLiteStatement originStatement(m_database, "DELETE FROM Origins WHERE origin=?");
    651     if (originStatement.prepare() != SQLResultOk) {
    652         LOG_ERROR("Unable to prepare deletion of origin %s from tracker", origin->databaseIdentifier().ascii().data());
    653         return;
    654     }
    655 
    656     originStatement.bindText(1, origin->databaseIdentifier());
    657 
    658     if (!originStatement.executeCommand()) {
    659         LOG_ERROR("Unable to execute deletion of databases from origin %s from tracker", origin->databaseIdentifier().ascii().data());
    660         return;
    661     }
    662 
    663     SQLiteFileSystem::deleteEmptyDatabaseDirectory(originPath(origin));
    664 
    665     RefPtr<SecurityOrigin> originPossiblyLastReference = origin;
    666722    {
    667         MutexLocker lockQuotaMap(m_quotaMapGuard);
     723        // To satisfy the lock hierarchy, we have to lock the originQuotaManager before m_databaseGuard if there's any chance we'll to lock both.
     724        Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager());
     725        MutexLocker lockDatabase(m_databaseGuard);
     726        SQLiteStatement statement(m_database, "DELETE FROM Databases WHERE origin=?");
     727
     728        doneDeletingOrigin(origin);
     729
     730        if (statement.prepare() != SQLResultOk) {
     731            LOG_ERROR("Unable to prepare deletion of databases from origin %s from tracker", origin->databaseIdentifier().ascii().data());
     732            return;
     733        }
     734
     735        statement.bindText(1, origin->databaseIdentifier());
     736
     737        if (!statement.executeCommand()) {
     738            LOG_ERROR("Unable to execute deletion of databases from origin %s from tracker", origin->databaseIdentifier().ascii().data());
     739            return;
     740        }
     741
     742        SQLiteStatement originStatement(m_database, "DELETE FROM Origins WHERE origin=?");
     743        if (originStatement.prepare() != SQLResultOk) {
     744            LOG_ERROR("Unable to prepare deletion of origin %s from tracker", origin->databaseIdentifier().ascii().data());
     745            return;
     746        }
     747
     748        originStatement.bindText(1, origin->databaseIdentifier());
     749
     750        if (!originStatement.executeCommand()) {
     751            LOG_ERROR("Unable to execute deletion of databases from origin %s from tracker", origin->databaseIdentifier().ascii().data());
     752            return;
     753        }
     754
     755        SQLiteFileSystem::deleteEmptyDatabaseDirectory(originPath(origin));
     756
     757        RefPtr<SecurityOrigin> originPossiblyLastReference = origin;
    668758        m_quotaMap->remove(origin);
    669759
    670         Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager());
    671         originQuotaManager().removeOrigin(origin);
     760        originQuotaManagerNoLock().removeOrigin(origin);
    672761
    673762        // If we removed the last origin, do some additional deletion.
     
    678767           SQLiteFileSystem::deleteEmptyDatabaseDirectory(m_databaseDirectoryPath);
    679768        }
    680     }
    681 
    682     if (m_client) {
    683         m_client->dispatchDidModifyOrigin(origin);
    684         for (unsigned i = 0; i < databaseNames.size(); ++i)
    685             m_client->dispatchDidModifyDatabase(origin, databaseNames[i]);
    686     }
     769
     770        if (m_client) {
     771            m_client->dispatchDidModifyOrigin(origin);
     772            for (unsigned i = 0; i < databaseNames.size(); ++i)
     773                m_client->dispatchDidModifyDatabase(origin, databaseNames[i]);
     774        }
     775    }
     776}
     777
     778bool DatabaseTracker::canCreateDatabase(SecurityOrigin *origin, const String& name)
     779{
     780    ASSERT(!m_databaseGuard.tryLock());
     781    // Can't create a database while someone else is deleting it; there's a risk of leaving untracked database debris on the disk.
     782    return !deletingDatabase(origin, name) && !deletingOrigin(origin);
     783}
     784
     785void DatabaseTracker::recordCreatingDatabase(SecurityOrigin *origin, const String& name)
     786{
     787    ASSERT(!m_databaseGuard.tryLock());
     788    NameCountMap* nameMap = m_beingCreated.get(origin);
     789    if (!nameMap) {
     790        nameMap = new NameCountMap();
     791        m_beingCreated.set(origin->threadsafeCopy(), nameMap);
     792    }
     793    long count = nameMap->get(name);
     794    nameMap->set(name.threadsafeCopy(), count + 1);
     795}
     796
     797void DatabaseTracker::doneCreatingDatabase(SecurityOrigin *origin, const String& name)
     798{
     799    ASSERT(!m_databaseGuard.tryLock());
     800    NameCountMap* nameMap = m_beingCreated.get(origin);
     801    if (nameMap) {
     802        long count = nameMap->get(name);
     803        ASSERT(count > 0);
     804        if (count <= 1) {
     805            nameMap->remove(name);
     806            if (nameMap->isEmpty()) {
     807                m_beingCreated.remove(origin);
     808                delete nameMap;
     809            }
     810        } else
     811            nameMap->set(name, count - 1);
     812    } else
     813        ASSERT(false);
     814}
     815
     816bool DatabaseTracker::creatingDatabase(SecurityOrigin *origin, const String& name)
     817{
     818    ASSERT(!m_databaseGuard.tryLock());
     819    NameCountMap* nameMap = m_beingCreated.get(origin);
     820    return nameMap && nameMap->get(name);
     821}
     822
     823bool DatabaseTracker::canDeleteDatabase(SecurityOrigin *origin, const String& name)
     824{
     825    ASSERT(!m_databaseGuard.tryLock());
     826    return !creatingDatabase(origin, name) && !deletingDatabase(origin, name);
     827}
     828
     829void DatabaseTracker::recordDeletingDatabase(SecurityOrigin *origin, const String& name)
     830{
     831    ASSERT(!m_databaseGuard.tryLock());
     832    ASSERT(canDeleteDatabase(origin, name));
     833    NameSet* nameSet = m_beingDeleted.get(origin);
     834    if (!nameSet) {
     835        nameSet = new NameSet();
     836        m_beingDeleted.set(origin->threadsafeCopy(), nameSet);
     837    }
     838    ASSERT(!nameSet->contains(name));
     839    nameSet->add(name.threadsafeCopy());
     840}
     841
     842void DatabaseTracker::doneDeletingDatabase(SecurityOrigin *origin, const String& name)
     843{
     844    ASSERT(!m_databaseGuard.tryLock());
     845    NameSet* nameSet = m_beingDeleted.get(origin);
     846    if (nameSet) {
     847        ASSERT(nameSet->contains(name));
     848        nameSet->remove(name);
     849        if (nameSet->isEmpty()) {
     850            m_beingDeleted.remove(origin);
     851            delete nameSet;
     852        }
     853    } else {
     854        ASSERT(false);
     855    }
     856}
     857
     858bool DatabaseTracker::deletingDatabase(SecurityOrigin *origin, const String& name)
     859{
     860    ASSERT(!m_databaseGuard.tryLock());
     861    NameSet* nameSet = m_beingDeleted.get(origin);
     862    return nameSet && nameSet->contains(name);
     863}
     864
     865bool DatabaseTracker::canDeleteOrigin(SecurityOrigin *origin)
     866{
     867    ASSERT(!m_databaseGuard.tryLock());
     868    return !(deletingOrigin(origin) || m_beingCreated.get(origin));
     869}
     870
     871bool DatabaseTracker::deletingOrigin(SecurityOrigin *origin)
     872{
     873    ASSERT(!m_databaseGuard.tryLock());
     874    return m_originsBeingDeleted.contains(origin);
     875}
     876
     877void DatabaseTracker::recordDeletingOrigin(SecurityOrigin *origin)
     878{
     879    ASSERT(!m_databaseGuard.tryLock());
     880    ASSERT(!deletingOrigin(origin));
     881    m_originsBeingDeleted.add(origin->threadsafeCopy());
     882}
     883
     884void DatabaseTracker::doneDeletingOrigin(SecurityOrigin *origin)
     885{
     886    ASSERT(!m_databaseGuard.tryLock());
     887    ASSERT(deletingOrigin(origin));
     888    m_originsBeingDeleted.remove(origin);
    687889}
    688890
    689891void DatabaseTracker::deleteDatabase(SecurityOrigin* origin, const String& name)
    690892{
    691     ASSERT(currentThread() == m_thread);
    692     openTrackerDatabase(false);
    693     if (!m_database.isOpen())
    694         return;
    695 
     893    {
     894        MutexLocker lockDatabase(m_databaseGuard);
     895        openTrackerDatabase(false);
     896        if (!m_database.isOpen())
     897            return;
     898
     899        if (!canDeleteDatabase(origin, name)) {
     900            ASSERT(FALSE);
     901            return;
     902        }
     903        recordDeletingDatabase(origin, name);
     904    }
     905
     906    // We drop the lock here because holding locks during a call to deleteDatabaseFile will deadlock.
    696907    if (!deleteDatabaseFile(origin, name)) {
    697908        LOG_ERROR("Unable to delete file for database %s in origin %s", name.ascii().data(), origin->databaseIdentifier().ascii().data());
    698         return;
    699     }
     909        MutexLocker lockDatabase(m_databaseGuard);
     910        doneDeletingDatabase(origin, name);
     911        return;
     912    }
     913
     914    // To satisfy the lock hierarchy, we have to lock the originQuotaManager before m_databaseGuard if there's any chance we'll to lock both.
     915    Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager());
     916    MutexLocker lockDatabase(m_databaseGuard);
    700917
    701918    SQLiteStatement statement(m_database, "DELETE FROM Databases WHERE origin=? AND name=?");
    702919    if (statement.prepare() != SQLResultOk) {
    703920        LOG_ERROR("Unable to prepare deletion of database %s from origin %s from tracker", name.ascii().data(), origin->databaseIdentifier().ascii().data());
     921        doneDeletingDatabase(origin, name);
    704922        return;
    705923    }
     
    710928    if (!statement.executeCommand()) {
    711929        LOG_ERROR("Unable to execute deletion of database %s from origin %s from tracker", name.ascii().data(), origin->databaseIdentifier().ascii().data());
    712         return;
    713     }
    714 
    715     {
    716         Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager());
    717         originQuotaManager().removeDatabase(origin, name);
    718     }
     930        doneDeletingDatabase(origin, name);
     931        return;
     932    }
     933
     934    originQuotaManagerNoLock().removeDatabase(origin, name);
    719935
    720936    if (m_client) {
     
    722938        m_client->dispatchDidModifyDatabase(origin, name);
    723939    }
    724 }
    725 
     940    doneDeletingDatabase(origin, name);
     941}
     942
     943// deleteDatabaseFile has to release locks between looking up the list of databases to close and closing them.  While this is in progress, the caller
     944// is responsible for making sure no new databases are opened in the file to be deleted.
    726945bool DatabaseTracker::deleteDatabaseFile(SecurityOrigin* origin, const String& name)
    727946{
    728     ASSERT(currentThread() == m_thread);
    729947    String fullPath = fullPathForDatabase(origin, name, false);
    730948    if (fullPath.isEmpty())
    731949        return true;
    732950
     951#ifndef NDEBUG
     952    {
     953    MutexLocker lockDatabase(m_databaseGuard);
     954    ASSERT(deletingDatabase(origin, name) || deletingOrigin(origin));
     955    }
     956#endif
     957
    733958    Vector<RefPtr<Database> > deletedDatabases;
    734959
    735     // Make sure not to hold the m_openDatabaseMapGuard mutex when calling
     960    // Make sure not to hold the any locks when calling
    736961    // Database::markAsDeletedAndClose(), since that can cause a deadlock
    737962    // during the synchronous DatabaseThread call it triggers.
    738 
    739963    {
    740964        MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard);
     
    743967            DatabaseNameMap* nameMap = m_openDatabaseMap->get(origin);
    744968            if (nameMap && nameMap->size()) {
    745                 // There are some open databases for this origin, lets check
     969                // There are some open databases for this origin, let's check
    746970                // if they are this database by name.
    747971                DatabaseSet* databaseSet = nameMap->get(name);
     
    764988void DatabaseTracker::setClient(DatabaseTrackerClient* client)
    765989{
    766     ASSERT(currentThread() == m_thread);
    767990    m_client = client;
    768991}
     
    774997}
    775998
    776 typedef Vector<pair<SecurityOrigin*, String> > NotificationQueue;
     999typedef Vector<pair<RefPtr<SecurityOrigin>, String> > NotificationQueue;
    7771000
    7781001static NotificationQueue& notificationQueue()
     
    7861009    MutexLocker locker(notificationMutex());
    7871010
    788     notificationQueue().append(pair<SecurityOrigin*, String>(origin, name.crossThreadString()));
     1011    notificationQueue().append(pair<RefPtr<SecurityOrigin>, String>(origin->threadsafeCopy(), name.crossThreadString()));
    7891012    scheduleForNotification();
    7901013}
     
    8211044
    8221045    for (unsigned i = 0; i < notifications.size(); ++i)
    823         theTracker.m_client->dispatchDidModifyDatabase(notifications[i].first, notifications[i].second);
     1046        theTracker.m_client->dispatchDidModifyDatabase(notifications[i].first.get(), notifications[i].second);
    8241047}
    8251048
  • trunk/WebCore/storage/DatabaseTracker.h

    r54506 r56293  
    6161public:
    6262    static DatabaseTracker& tracker();
    63     // FIXME: Due to workers having multiple threads in a single process sharing
    64     // a DatabaseTracker, this singleton will have to be synchronized or moved
    65     // to TLS.
     63    // This singleton will potentially be used from multiple worker threads and the page's context thread simultaneously.  To keep this safe, it's
     64    // currently using 4 locks.  In order to avoid deadlock when taking multiple locks, you must take them in the correct order:
     65    // originQuotaManager() before m_databaseGuard or m_openDatabaseMapGuard
     66    // m_databaseGuard before m_openDatabaseMapGuard
     67    // notificationMutex() is currently independent of the other locks.
    6668
    6769    bool canEstablishDatabase(ScriptExecutionContext*, const String& name, const String& displayName, unsigned long estimatedSize);
     
    8890public:
    8991    void setDatabaseDirectoryPath(const String&);
    90     const String& databaseDirectoryPath() const;
     92    String databaseDirectoryPath() const;
    9193
    9294    void origins(Vector<RefPtr<SecurityOrigin> >& result);
     
    115117
    116118private:
     119    OriginQuotaManager& originQuotaManagerNoLock();
     120    bool hasEntryForOriginNoLock(SecurityOrigin* origin);
     121    String fullPathForDatabaseNoLock(SecurityOrigin*, const String& name, bool createIfDoesNotExist);
     122    bool databaseNamesForOriginNoLock(SecurityOrigin* origin, Vector<String>& resultVector);
     123    unsigned long long usageForOriginNoLock(SecurityOrigin* origin);
     124    unsigned long long quotaForOriginNoLock(SecurityOrigin* origin);
     125
    117126    String trackerDatabasePath() const;
    118127    void openTrackerDatabase(bool createIfDoesNotExist);
     
    127136    bool deleteDatabaseFile(SecurityOrigin*, const String& name);
    128137
     138    // This lock protects m_database, m_quotaMap, m_proposedDatabases, m_databaseDirectoryPath, m_originsBeingDeleted, m_beingCreated, and m_beingDeleted.
     139    Mutex m_databaseGuard;
    129140    SQLiteDatabase m_database;
    130141
    131142    typedef HashMap<RefPtr<SecurityOrigin>, unsigned long long, SecurityOriginHash> QuotaMap;
    132     Mutex m_quotaMapGuard;
    133143    mutable OwnPtr<QuotaMap> m_quotaMap;
    134144
     
    139149    DatabaseTrackerClient* m_client;
    140150
    141     std::pair<SecurityOrigin*, DatabaseDetails>* m_proposedDatabase;
     151    typedef std::pair<RefPtr<SecurityOrigin>, DatabaseDetails> ProposedDatabase;
     152    HashSet<ProposedDatabase*> m_proposedDatabases;
    142153
    143 #ifndef NDEBUG
    144     ThreadIdentifier m_thread;
    145 #endif
     154    typedef HashMap<String, long> NameCountMap;
     155    typedef HashMap<RefPtr<SecurityOrigin>, NameCountMap*, SecurityOriginHash> CreateSet;
     156    CreateSet m_beingCreated;
     157    typedef HashSet<String> NameSet;
     158    HashMap<RefPtr<SecurityOrigin>, NameSet*, SecurityOriginHash> m_beingDeleted;
     159    HashSet<RefPtr<SecurityOrigin>, SecurityOriginHash> m_originsBeingDeleted;
     160    bool canCreateDatabase(SecurityOrigin *origin, const String& name);
     161    void recordCreatingDatabase(SecurityOrigin *origin, const String& name);
     162    void doneCreatingDatabase(SecurityOrigin *origin, const String& name);
     163    bool creatingDatabase(SecurityOrigin *origin, const String& name);
     164    bool canDeleteDatabase(SecurityOrigin *origin, const String& name);
     165    void recordDeletingDatabase(SecurityOrigin *origin, const String& name);
     166    void doneDeletingDatabase(SecurityOrigin *origin, const String& name);
     167    bool deletingDatabase(SecurityOrigin *origin, const String& name);
     168    bool canDeleteOrigin(SecurityOrigin *origin);
     169    bool deletingOrigin(SecurityOrigin *origin);
     170    void recordDeletingOrigin(SecurityOrigin *origin);
     171    void doneDeletingOrigin(SecurityOrigin *origin);
    146172
    147173    static void scheduleForNotification();
  • trunk/WebCore/storage/OriginQuotaManager.cpp

    r50169 r56293  
    4343}
    4444
     45bool OriginQuotaManager::tryLock()
     46{
     47    bool locked = m_usageRecordGuard.tryLock();
     48#ifndef NDEBUG
     49    if (locked)
     50        m_usageRecordGuardLocked = true;
     51    else
     52        ASSERT(m_usageRecordGuardLocked);
     53#endif
     54    return locked;
     55}
     56
    4557void OriginQuotaManager::lock()
    4658{
     
    6476    ASSERT(!m_usageMap.contains(origin.get()));
    6577
    66     m_usageMap.set(origin, new OriginUsageRecord);
     78    m_usageMap.set(origin->threadsafeCopy(), new OriginUsageRecord);
    6779}
    6880
  • trunk/WebCore/storage/OriginQuotaManager.h

    r43283 r56293  
    4646    OriginQuotaManager();
    4747
     48    bool tryLock();
    4849    void lock();
    4950    void unlock();
Note: See TracChangeset for help on using the changeset viewer.