diff options
author | Steve Block <steveblock@google.com> | 2010-04-27 16:31:00 +0100 |
---|---|---|
committer | Steve Block <steveblock@google.com> | 2010-05-11 14:42:12 +0100 |
commit | dcc8cf2e65d1aa555cce12431a16547e66b469ee (patch) | |
tree | 92a8d65cd5383bca9749f5327fb5e440563926e6 /WebCore/storage/DatabaseTracker.cpp | |
parent | ccac38a6b48843126402088a309597e682f40fe6 (diff) | |
download | external_webkit-dcc8cf2e65d1aa555cce12431a16547e66b469ee.zip external_webkit-dcc8cf2e65d1aa555cce12431a16547e66b469ee.tar.gz external_webkit-dcc8cf2e65d1aa555cce12431a16547e66b469ee.tar.bz2 |
Merge webkit.org at r58033 : Initial merge by git
Change-Id: If006c38561af287c50cd578d251629b51e4d8cd1
Diffstat (limited to 'WebCore/storage/DatabaseTracker.cpp')
-rw-r--r-- | WebCore/storage/DatabaseTracker.cpp | 692 |
1 files changed, 460 insertions, 232 deletions
diff --git a/WebCore/storage/DatabaseTracker.cpp b/WebCore/storage/DatabaseTracker.cpp index 76492c9..8e2e3f3 100644 --- a/WebCore/storage/DatabaseTracker.cpp +++ b/WebCore/storage/DatabaseTracker.cpp @@ -49,15 +49,14 @@ using namespace std; -namespace WebCore { - -OriginQuotaManager& DatabaseTracker::originQuotaManager() +static WebCore::OriginQuotaManager& originQuotaManager() { - populateOrigins(); - ASSERT(m_quotaManager); - return *m_quotaManager; + DEFINE_STATIC_LOCAL(WebCore::OriginQuotaManager, quotaManager, ()); + return quotaManager; } +namespace WebCore { + DatabaseTracker& DatabaseTracker::tracker() { DEFINE_STATIC_LOCAL(DatabaseTracker, tracker, ()); @@ -66,36 +65,33 @@ DatabaseTracker& DatabaseTracker::tracker() DatabaseTracker::DatabaseTracker() : m_client(0) - , m_proposedDatabase(0) -#ifndef NDEBUG - , m_thread(currentThread()) -#endif { SQLiteFileSystem::registerSQLiteVFS(); + + MutexLocker lockDatabase(m_databaseGuard); + populateOrigins(); } void DatabaseTracker::setDatabaseDirectoryPath(const String& path) { - ASSERT(currentThread() == m_thread); + MutexLocker lockDatabase(m_databaseGuard); ASSERT(!m_database.isOpen()); - m_databaseDirectoryPath = path; + m_databaseDirectoryPath = path.threadsafeCopy(); } -const String& DatabaseTracker::databaseDirectoryPath() const +String DatabaseTracker::databaseDirectoryPath() const { - ASSERT(currentThread() == m_thread); - return m_databaseDirectoryPath; + return m_databaseDirectoryPath.threadsafeCopy(); } String DatabaseTracker::trackerDatabasePath() const { - ASSERT(currentThread() == m_thread); return SQLiteFileSystem::appendDatabaseFileNameToPath(m_databaseDirectoryPath, "Databases.db"); } void DatabaseTracker::openTrackerDatabase(bool createIfDoesNotExist) { - ASSERT(currentThread() == m_thread); + ASSERT(!m_databaseGuard.tryLock()); if (m_database.isOpen()) return; @@ -106,67 +102,96 @@ void DatabaseTracker::openTrackerDatabase(bool createIfDoesNotExist) if (!m_database.open(databasePath)) { // FIXME: What do do here? + LOG_ERROR("Failed to open databasePath %s.", databasePath.ascii().data()); return; } + m_database.disableThreadingChecks(); if (!m_database.tableExists("Origins")) { if (!m_database.executeCommand("CREATE TABLE Origins (origin TEXT UNIQUE ON CONFLICT REPLACE, quota INTEGER NOT NULL ON CONFLICT FAIL);")) { // FIXME: and here + LOG_ERROR("Failed to create Origins table"); } } if (!m_database.tableExists("Databases")) { if (!m_database.executeCommand("CREATE TABLE Databases (guid INTEGER PRIMARY KEY AUTOINCREMENT, origin TEXT, name TEXT, displayName TEXT, estimatedSize INTEGER, path TEXT);")) { // FIXME: and here + LOG_ERROR("Failed to create Databases table"); } } } bool DatabaseTracker::canEstablishDatabase(ScriptExecutionContext* context, const String& name, const String& displayName, unsigned long estimatedSize) { - ASSERT(currentThread() == m_thread); + SecurityOrigin* origin = context->securityOrigin(); + ProposedDatabase details; - // Populate the origins before we establish a database; this guarantees that quotaForOrigin - // can run on the database thread later. - populateOrigins(); + unsigned long long requirement; + unsigned long long tempUsage; + { + MutexLocker lockDatabase(m_databaseGuard); + Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager()); - SecurityOrigin* origin = context->securityOrigin(); + if (!canCreateDatabase(origin, name)) + return false; - // Since we're imminently opening a database within this context's origin, make sure this origin is being tracked by the QuotaTracker - // by fetching it's current usage now - unsigned long long usage = usageForOrigin(origin); + recordCreatingDatabase(origin, name); - // If a database already exists, ignore the passed-in estimated size and say it's OK. - if (hasEntryForDatabase(origin, name)) - return true; + // Since we're imminently opening a database within this context's origin, make sure this origin is being tracked by the QuotaTracker + // by fetching its current usage now. + unsigned long long usage = usageForOriginNoLock(origin); - // If the database will fit, allow its creation. - unsigned long long requirement = usage + max(1UL, estimatedSize); - if (requirement < usage) - return false; // If the estimated size is so big it causes an overflow, don't allow creation. - if (requirement <= quotaForOrigin(origin)) - return true; + // If a database already exists, ignore the passed-in estimated size and say it's OK. + if (hasEntryForDatabase(origin, name)) + return true; - // Give the chrome client a chance to increase the quota. - // Temporarily make the details of the proposed database available, so the client can get at them. - pair<SecurityOrigin*, DatabaseDetails> details(origin, DatabaseDetails(name, displayName, estimatedSize, 0)); - m_proposedDatabase = &details; + // If the database will fit, allow its creation. + requirement = usage + max(1UL, estimatedSize); + tempUsage = usage; + if (requirement < usage) { + doneCreatingDatabase(origin, name); + return false; // If the estimated size is so big it causes an overflow, don't allow creation. + } + if (requirement <= quotaForOriginNoLock(origin)) + return true; + + // Give the chrome client a chance to increase the quota. + // Temporarily make the details of the proposed database available, so the client can get at them. + // FIXME: We should really just pass the details into this call, rather than using m_proposedDatabases. + details = ProposedDatabase(origin->threadsafeCopy(), DatabaseDetails(name.threadsafeCopy(), displayName.threadsafeCopy(), estimatedSize, 0)); + m_proposedDatabases.add(&details); + } + // Drop all locks before calling out; we don't know what they'll do. context->databaseExceededQuota(name); - m_proposedDatabase = 0; + + MutexLocker lockDatabase(m_databaseGuard); + + m_proposedDatabases.remove(&details); // If the database will fit now, allow its creation. - return requirement <= quotaForOrigin(origin); + if (requirement <= quotaForOriginNoLock(origin)) + return true; + + doneCreatingDatabase(origin, name); + + return false; } -bool DatabaseTracker::hasEntryForOrigin(SecurityOrigin* origin) +bool DatabaseTracker::hasEntryForOriginNoLock(SecurityOrigin* origin) { - ASSERT(currentThread() == m_thread); - populateOrigins(); - MutexLocker lockQuotaMap(m_quotaMapGuard); + ASSERT(!m_databaseGuard.tryLock()); + ASSERT(m_quotaMap); return m_quotaMap->contains(origin); } +bool DatabaseTracker::hasEntryForOrigin(SecurityOrigin* origin) +{ + MutexLocker lockDatabase(m_databaseGuard); + return hasEntryForOriginNoLock(origin); +} + bool DatabaseTracker::hasEntryForDatabase(SecurityOrigin* origin, const String& databaseIdentifier) { - ASSERT(currentThread() == m_thread); + ASSERT(!m_databaseGuard.tryLock()); openTrackerDatabase(false); if (!m_database.isOpen()) return false; @@ -186,23 +211,31 @@ unsigned long long DatabaseTracker::getMaxSizeForDatabase(const Database* databa ASSERT(currentThread() == database->scriptExecutionContext()->databaseThread()->getThreadID()); // The maximum size for a database is the full quota for its origin, minus the current usage within the origin, // plus the current usage of the given database - Locker<OriginQuotaManager> locker(originQuotaManager()); + MutexLocker lockDatabase(m_databaseGuard); + Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager()); SecurityOrigin* origin = database->securityOrigin(); - return quotaForOrigin(origin) - originQuotaManager().diskUsage(origin) + SQLiteFileSystem::getDatabaseFileSize(database->fileName()); + return quotaForOriginNoLock(origin) - originQuotaManager().diskUsage(origin) + SQLiteFileSystem::getDatabaseFileSize(database->fileName()); +} + +void DatabaseTracker::databaseChanged(Database* database) +{ + Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager()); + originQuotaManager().markDatabase(database); } String DatabaseTracker::originPath(SecurityOrigin* origin) const { - ASSERT(currentThread() == m_thread); - return SQLiteFileSystem::appendDatabaseFileNameToPath(m_databaseDirectoryPath, origin->databaseIdentifier()); + return SQLiteFileSystem::appendDatabaseFileNameToPath(m_databaseDirectoryPath.threadsafeCopy(), origin->databaseIdentifier()); } -String DatabaseTracker::fullPathForDatabase(SecurityOrigin* origin, const String& name, bool createIfNotExists) +String DatabaseTracker::fullPathForDatabaseNoLock(SecurityOrigin* origin, const String& name, bool createIfNotExists) { - ASSERT(currentThread() == m_thread); + ASSERT(!m_databaseGuard.tryLock()); + ASSERT(!originQuotaManager().tryLock()); - if (m_proposedDatabase && m_proposedDatabase->first == origin && m_proposedDatabase->second.name() == name) - return String(); + for (HashSet<ProposedDatabase*>::iterator iter = m_proposedDatabases.begin(); iter != m_proposedDatabases.end(); ++iter) + if ((*iter)->second.name() == name && (*iter)->first->equal(origin)) + return String(); String originIdentifier = origin->databaseIdentifier(); String originPath = this->originPath(origin); @@ -212,7 +245,6 @@ String DatabaseTracker::fullPathForDatabase(SecurityOrigin* origin, const String return String(); // See if we have a path for this database yet - openTrackerDatabase(false); if (!m_database.isOpen()) return String(); SQLiteStatement statement(m_database, "SELECT path FROM Databases WHERE origin=? AND name=?;"); @@ -231,36 +263,39 @@ String DatabaseTracker::fullPathForDatabase(SecurityOrigin* origin, const String return String(); if (result != SQLResultDone) { - LOG_ERROR("Failed to retrieve filename from Database Tracker for origin %s, name %s", origin->databaseIdentifier().ascii().data(), name.ascii().data()); + LOG_ERROR("Failed to retrieve filename from Database Tracker for origin %s, name %s", originIdentifier.ascii().data(), name.ascii().data()); return String(); } statement.finalize(); - String fileName = SQLiteFileSystem::getFileNameForNewDatabase(originPath, name, origin->databaseIdentifier(), &m_database); + String fileName = SQLiteFileSystem::getFileNameForNewDatabase(originPath, name, originIdentifier, &m_database); if (!addDatabase(origin, name, fileName)) return String(); // If this origin's quota is being tracked (open handle to a database in this origin), add this new database // to the quota manager now String fullFilePath = SQLiteFileSystem::appendDatabaseFileNameToPath(originPath, fileName); - { - Locker<OriginQuotaManager> locker(originQuotaManager()); - if (originQuotaManager().tracksOrigin(origin)) - originQuotaManager().addDatabase(origin, name, fullFilePath); - } + if (originQuotaManager().tracksOrigin(origin)) + originQuotaManager().addDatabase(origin, name, fullFilePath); return fullFilePath; } +String DatabaseTracker::fullPathForDatabase(SecurityOrigin* origin, const String& name, bool createIfNotExists) +{ + MutexLocker lockDatabase(m_databaseGuard); + Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager()); + + return fullPathForDatabaseNoLock(origin, name, createIfNotExists).threadsafeCopy(); +} + void DatabaseTracker::populateOrigins() { + ASSERT(!m_databaseGuard.tryLock()); if (m_quotaMap) return; - ASSERT(currentThread() == m_thread); - m_quotaMap.set(new QuotaMap); - m_quotaManager.set(new OriginQuotaManager); openTrackerDatabase(false); if (!m_database.isOpen()) @@ -268,30 +303,31 @@ void DatabaseTracker::populateOrigins() SQLiteStatement statement(m_database, "SELECT origin, quota FROM Origins"); - if (statement.prepare() != SQLResultOk) + if (statement.prepare() != SQLResultOk) { + LOG_ERROR("Failed to prepare statement."); return; + } int result; while ((result = statement.step()) == SQLResultRow) { RefPtr<SecurityOrigin> origin = SecurityOrigin::createFromDatabaseIdentifier(statement.getColumnText(0)); - m_quotaMap->set(origin.get(), statement.getColumnInt64(1)); + m_quotaMap->set(origin.get()->threadsafeCopy(), statement.getColumnInt64(1)); } if (result != SQLResultDone) - LOG_ERROR("Failed to read in all origins from the database"); + LOG_ERROR("Failed to read in all origins from the database."); } void DatabaseTracker::origins(Vector<RefPtr<SecurityOrigin> >& result) { - ASSERT(currentThread() == m_thread); - populateOrigins(); - MutexLocker lockQuotaMap(m_quotaMapGuard); + MutexLocker lockDatabase(m_databaseGuard); + ASSERT(m_quotaMap); copyKeysToVector(*m_quotaMap, result); } -bool DatabaseTracker::databaseNamesForOrigin(SecurityOrigin* origin, Vector<String>& resultVector) +bool DatabaseTracker::databaseNamesForOriginNoLock(SecurityOrigin* origin, Vector<String>& resultVector) { - ASSERT(currentThread() == m_thread); + ASSERT(!m_databaseGuard.tryLock()); openTrackerDatabase(false); if (!m_database.isOpen()) return false; @@ -315,44 +351,67 @@ bool DatabaseTracker::databaseNamesForOrigin(SecurityOrigin* origin, Vector<Stri return true; } -DatabaseDetails DatabaseTracker::detailsForNameAndOrigin(const String& name, SecurityOrigin* origin) +bool DatabaseTracker::databaseNamesForOrigin(SecurityOrigin* origin, Vector<String>& resultVector) { - ASSERT(currentThread() == m_thread); + Vector<String> temp; + { + MutexLocker lockDatabase(m_databaseGuard); + if (!databaseNamesForOriginNoLock(origin, temp)) + return false; + } - if (m_proposedDatabase && m_proposedDatabase->first == origin && m_proposedDatabase->second.name() == name) - return m_proposedDatabase->second; + for (Vector<String>::iterator iter = temp.begin(); iter != temp.end(); ++iter) + resultVector.append(iter->threadsafeCopy()); + return true; +} +DatabaseDetails DatabaseTracker::detailsForNameAndOrigin(const String& name, SecurityOrigin* origin) +{ String originIdentifier = origin->databaseIdentifier(); + String displayName; + int64_t expectedUsage; - openTrackerDatabase(false); - if (!m_database.isOpen()) - return DatabaseDetails(); - SQLiteStatement statement(m_database, "SELECT displayName, estimatedSize FROM Databases WHERE origin=? AND name=?"); - if (statement.prepare() != SQLResultOk) - return DatabaseDetails(); + { + MutexLocker lockDatabase(m_databaseGuard); - statement.bindText(1, originIdentifier); - statement.bindText(2, name); + for (HashSet<ProposedDatabase*>::iterator iter = m_proposedDatabases.begin(); iter != m_proposedDatabases.end(); ++iter) + if ((*iter)->second.name() == name && (*iter)->first->equal(origin)) { + ASSERT((*iter)->second.thread() == currentThread()); + return (*iter)->second; + } - int result = statement.step(); - if (result == SQLResultDone) - return DatabaseDetails(); + openTrackerDatabase(false); + if (!m_database.isOpen()) + return DatabaseDetails(); + SQLiteStatement statement(m_database, "SELECT displayName, estimatedSize FROM Databases WHERE origin=? AND name=?"); + if (statement.prepare() != SQLResultOk) + return DatabaseDetails(); - if (result != SQLResultRow) { - LOG_ERROR("Error retrieving details for database %s in origin %s from tracker database", name.ascii().data(), originIdentifier.ascii().data()); - return DatabaseDetails(); + statement.bindText(1, originIdentifier); + statement.bindText(2, name); + + int result = statement.step(); + if (result == SQLResultDone) + return DatabaseDetails(); + + if (result != SQLResultRow) { + LOG_ERROR("Error retrieving details for database %s in origin %s from tracker database", name.ascii().data(), originIdentifier.ascii().data()); + return DatabaseDetails(); + } + displayName = statement.getColumnText(0); + expectedUsage = statement.getColumnInt64(1); } - return DatabaseDetails(name, statement.getColumnText(0), statement.getColumnInt64(1), usageForDatabase(name, origin)); + return DatabaseDetails(name, displayName, expectedUsage, usageForDatabase(name, origin)); } void DatabaseTracker::setDatabaseDetails(SecurityOrigin* origin, const String& name, const String& displayName, unsigned long estimatedSize) { - ASSERT(currentThread() == m_thread); - String originIdentifier = origin->databaseIdentifier(); int64_t guid = 0; + MutexLocker lockDatabase(m_databaseGuard); + openTrackerDatabase(true); if (!m_database.isOpen()) return; @@ -400,7 +459,6 @@ void DatabaseTracker::setDatabaseDetails(SecurityOrigin* origin, const String& n unsigned long long DatabaseTracker::usageForDatabase(const String& name, SecurityOrigin* origin) { - ASSERT(currentThread() == m_thread); String path = fullPathForDatabase(origin, name, false); if (path.isEmpty()) return 0; @@ -413,27 +471,32 @@ void DatabaseTracker::addOpenDatabase(Database* database) if (!database) return; - MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard); + { + MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard); - if (!m_openDatabaseMap) - m_openDatabaseMap.set(new DatabaseOriginMap); + if (!m_openDatabaseMap) + m_openDatabaseMap.set(new DatabaseOriginMap); - String name(database->stringIdentifier()); - DatabaseNameMap* nameMap = m_openDatabaseMap->get(database->securityOrigin()); - if (!nameMap) { - nameMap = new DatabaseNameMap; - m_openDatabaseMap->set(database->securityOrigin(), nameMap); - } + String name(database->stringIdentifier()); + DatabaseNameMap* nameMap = m_openDatabaseMap->get(database->securityOrigin()); + if (!nameMap) { + nameMap = new DatabaseNameMap; + m_openDatabaseMap->set(database->securityOrigin()->threadsafeCopy(), nameMap); + } - DatabaseSet* databaseSet = nameMap->get(name); - if (!databaseSet) { - databaseSet = new DatabaseSet; - nameMap->set(name, databaseSet); - } + DatabaseSet* databaseSet = nameMap->get(name); + if (!databaseSet) { + databaseSet = new DatabaseSet; + nameMap->set(name.threadsafeCopy(), databaseSet); + } + + databaseSet->add(database); - databaseSet->add(database); + LOG(StorageAPI, "Added open Database %s (%p)\n", database->stringIdentifier().ascii().data(), database); + } - LOG(StorageAPI, "Added open Database %s (%p)\n", database->stringIdentifier().ascii().data(), database); + MutexLocker lockDatabase(m_databaseGuard); + doneCreatingDatabase(database->securityOrigin(), database->stringIdentifier()); } void DatabaseTracker::removeOpenDatabase(Database* database) @@ -441,41 +504,46 @@ void DatabaseTracker::removeOpenDatabase(Database* database) if (!database) return; - MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard); + { + MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard); - if (!m_openDatabaseMap) { - ASSERT_NOT_REACHED(); - return; - } + if (!m_openDatabaseMap) { + ASSERT_NOT_REACHED(); + return; + } - String name(database->stringIdentifier()); - DatabaseNameMap* nameMap = m_openDatabaseMap->get(database->securityOrigin()); - if (!nameMap) { - ASSERT_NOT_REACHED(); - return; - } + String name(database->stringIdentifier()); + DatabaseNameMap* nameMap = m_openDatabaseMap->get(database->securityOrigin()); + if (!nameMap) { + ASSERT_NOT_REACHED(); + return; + } - DatabaseSet* databaseSet = nameMap->get(name); - if (!databaseSet) { - ASSERT_NOT_REACHED(); - return; - } + DatabaseSet* databaseSet = nameMap->get(name); + if (!databaseSet) { + ASSERT_NOT_REACHED(); + return; + } - databaseSet->remove(database); + databaseSet->remove(database); - LOG(StorageAPI, "Removed open Database %s (%p)\n", database->stringIdentifier().ascii().data(), database); + LOG(StorageAPI, "Removed open Database %s (%p)\n", database->stringIdentifier().ascii().data(), database); - if (!databaseSet->isEmpty()) - return; + if (!databaseSet->isEmpty()) + return; - nameMap->remove(name); - delete databaseSet; + nameMap->remove(name); + delete databaseSet; - if (!nameMap->isEmpty()) - return; + if (!nameMap->isEmpty()) + return; - m_openDatabaseMap->remove(database->securityOrigin()); - delete nameMap; + m_openDatabaseMap->remove(database->securityOrigin()); + delete nameMap; + } + + Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager()); + originQuotaManager().removeOrigin(database->securityOrigin()); } void DatabaseTracker::getOpenDatabases(SecurityOrigin* origin, const String& name, HashSet<RefPtr<Database> >* databases) @@ -496,10 +564,9 @@ void DatabaseTracker::getOpenDatabases(SecurityOrigin* origin, const String& nam databases->add(*it); } -unsigned long long DatabaseTracker::usageForOrigin(SecurityOrigin* origin) +unsigned long long DatabaseTracker::usageForOriginNoLock(SecurityOrigin* origin) { - ASSERT(currentThread() == m_thread); - Locker<OriginQuotaManager> locker(originQuotaManager()); + ASSERT(!originQuotaManager().tryLock()); // Use the OriginQuotaManager mechanism to calculate the usage if (originQuotaManager().tracksOrigin(origin)) @@ -509,79 +576,89 @@ unsigned long long DatabaseTracker::usageForOrigin(SecurityOrigin* origin) originQuotaManager().trackOrigin(origin); Vector<String> names; - databaseNamesForOrigin(origin, names); + databaseNamesForOriginNoLock(origin, names); for (unsigned i = 0; i < names.size(); ++i) - originQuotaManager().addDatabase(origin, names[i], fullPathForDatabase(origin, names[i], false)); + originQuotaManager().addDatabase(origin, names[i], fullPathForDatabaseNoLock(origin, names[i], false)); if (!originQuotaManager().tracksOrigin(origin)) return 0; return originQuotaManager().diskUsage(origin); } -unsigned long long DatabaseTracker::quotaForOrigin(SecurityOrigin* origin) +unsigned long long DatabaseTracker::usageForOrigin(SecurityOrigin* origin) { - ASSERT(currentThread() == m_thread || m_quotaMap); - populateOrigins(); - MutexLocker lockQuotaMap(m_quotaMapGuard); + MutexLocker lockDatabase(m_databaseGuard); + Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager()); + return usageForOriginNoLock(origin); +} + +unsigned long long DatabaseTracker::quotaForOriginNoLock(SecurityOrigin* origin) +{ + ASSERT(!m_databaseGuard.tryLock()); + ASSERT(m_quotaMap); return m_quotaMap->get(origin); } +unsigned long long DatabaseTracker::quotaForOrigin(SecurityOrigin* origin) +{ + MutexLocker lockDatabase(m_databaseGuard); + return quotaForOriginNoLock(origin); +} + void DatabaseTracker::setQuota(SecurityOrigin* origin, unsigned long long quota) { - ASSERT(currentThread() == m_thread); - if (quotaForOrigin(origin) == quota) + MutexLocker lockDatabase(m_databaseGuard); + + if (quotaForOriginNoLock(origin) == quota) return; openTrackerDatabase(true); if (!m_database.isOpen()) return; - { - MutexLocker lockQuotaMap(m_quotaMapGuard); - - if (!m_quotaMap->contains(origin)) { - SQLiteStatement statement(m_database, "INSERT INTO Origins VALUES (?, ?)"); - if (statement.prepare() != SQLResultOk) { - LOG_ERROR("Unable to establish origin %s in the tracker", origin->databaseIdentifier().ascii().data()); - } else { - statement.bindText(1, origin->databaseIdentifier()); - statement.bindInt64(2, quota); - - if (statement.step() != SQLResultDone) - LOG_ERROR("Unable to establish origin %s in the tracker", origin->databaseIdentifier().ascii().data()); - } + if (!m_quotaMap->contains(origin)) { + SQLiteStatement statement(m_database, "INSERT INTO Origins VALUES (?, ?)"); + if (statement.prepare() != SQLResultOk) { + LOG_ERROR("Unable to establish origin %s in the tracker", origin->databaseIdentifier().ascii().data()); } else { - SQLiteStatement statement(m_database, "UPDATE Origins SET quota=? WHERE origin=?"); - bool error = statement.prepare() != SQLResultOk; - if (!error) { - statement.bindInt64(1, quota); - statement.bindText(2, origin->databaseIdentifier()); - - error = !statement.executeCommand(); - } + statement.bindText(1, origin->databaseIdentifier()); + statement.bindInt64(2, quota); - if (error) - LOG_ERROR("Failed to set quota %llu in tracker database for origin %s", quota, origin->databaseIdentifier().ascii().data()); + if (statement.step() != SQLResultDone) + LOG_ERROR("Unable to establish origin %s in the tracker", origin->databaseIdentifier().ascii().data()); + } + } else { + SQLiteStatement statement(m_database, "UPDATE Origins SET quota=? WHERE origin=?"); + bool error = statement.prepare() != SQLResultOk; + if (!error) { + statement.bindInt64(1, quota); + statement.bindText(2, origin->databaseIdentifier()); + + error = !statement.executeCommand(); } - // FIXME: Is it really OK to update the quota in memory if we failed to update it on disk? - m_quotaMap->set(origin, quota); + if (error) + LOG_ERROR("Failed to set quota %llu in tracker database for origin %s", quota, origin->databaseIdentifier().ascii().data()); } + // FIXME: Is it really OK to update the quota in memory if we failed to update it on disk? + m_quotaMap->set(origin->threadsafeCopy(), quota); + if (m_client) m_client->dispatchDidModifyOrigin(origin); } bool DatabaseTracker::addDatabase(SecurityOrigin* origin, const String& name, const String& path) { - ASSERT(currentThread() == m_thread); + ASSERT(!m_databaseGuard.tryLock()); + ASSERT(m_quotaMap); openTrackerDatabase(true); if (!m_database.isOpen()) return false; // New database should never be added until the origin has been established - ASSERT(hasEntryForOrigin(origin)); + ASSERT(hasEntryForOriginNoLock(origin)); SQLiteStatement statement(m_database, "INSERT INTO Databases (origin, name, path) VALUES (?, ?, ?);"); @@ -605,8 +682,6 @@ bool DatabaseTracker::addDatabase(SecurityOrigin* origin, const String& name, co void DatabaseTracker::deleteAllDatabases() { - ASSERT(currentThread() == m_thread); - Vector<RefPtr<SecurityOrigin> > originsCopy; origins(originsCopy); @@ -614,19 +689,30 @@ void DatabaseTracker::deleteAllDatabases() deleteOrigin(originsCopy[i].get()); } -void DatabaseTracker::deleteOrigin(SecurityOrigin* origin) +// 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 +// taking place. +bool DatabaseTracker::deleteOrigin(SecurityOrigin* origin) { - ASSERT(currentThread() == m_thread); - openTrackerDatabase(false); - if (!m_database.isOpen()) - return; - Vector<String> databaseNames; - if (!databaseNamesForOrigin(origin, databaseNames)) { - LOG_ERROR("Unable to retrieve list of database names for origin %s", origin->databaseIdentifier().ascii().data()); - return; + { + MutexLocker lockDatabase(m_databaseGuard); + openTrackerDatabase(false); + if (!m_database.isOpen()) + return false; + + if (!databaseNamesForOriginNoLock(origin, databaseNames)) { + LOG_ERROR("Unable to retrieve list of database names for origin %s", origin->databaseIdentifier().ascii().data()); + return false; + } + if (!canDeleteOrigin(origin)) { + LOG_ERROR("Tried to delete an origin (%s) while either creating database in it or already deleting it", origin->databaseIdentifier().ascii().data()); + ASSERT(false); + return false; + } + recordDeletingOrigin(origin); } + // We drop the lock here because holding locks during a call to deleteDatabaseFile will deadlock. for (unsigned i = 0; i < databaseNames.size(); ++i) { if (!deleteDatabaseFile(origin, databaseNames[i])) { // Even if the file can't be deleted, we want to try and delete the rest, don't return early here. @@ -634,41 +720,45 @@ void DatabaseTracker::deleteOrigin(SecurityOrigin* origin) } } - SQLiteStatement statement(m_database, "DELETE FROM Databases WHERE origin=?"); - if (statement.prepare() != SQLResultOk) { - LOG_ERROR("Unable to prepare deletion of databases from origin %s from tracker", origin->databaseIdentifier().ascii().data()); - return; - } + { + MutexLocker lockDatabase(m_databaseGuard); + doneDeletingOrigin(origin); - statement.bindText(1, origin->databaseIdentifier()); + SQLiteStatement statement(m_database, "DELETE FROM Databases WHERE origin=?"); + if (statement.prepare() != SQLResultOk) { + LOG_ERROR("Unable to prepare deletion of databases from origin %s from tracker", origin->databaseIdentifier().ascii().data()); + return false; + } - if (!statement.executeCommand()) { - LOG_ERROR("Unable to execute deletion of databases from origin %s from tracker", origin->databaseIdentifier().ascii().data()); - return; - } + statement.bindText(1, origin->databaseIdentifier()); - SQLiteStatement originStatement(m_database, "DELETE FROM Origins WHERE origin=?"); - if (originStatement.prepare() != SQLResultOk) { - LOG_ERROR("Unable to prepare deletion of origin %s from tracker", origin->databaseIdentifier().ascii().data()); - return; - } + if (!statement.executeCommand()) { + LOG_ERROR("Unable to execute deletion of databases from origin %s from tracker", origin->databaseIdentifier().ascii().data()); + return false; + } - originStatement.bindText(1, origin->databaseIdentifier()); + SQLiteStatement originStatement(m_database, "DELETE FROM Origins WHERE origin=?"); + if (originStatement.prepare() != SQLResultOk) { + LOG_ERROR("Unable to prepare deletion of origin %s from tracker", origin->databaseIdentifier().ascii().data()); + return false; + } - if (!originStatement.executeCommand()) { - LOG_ERROR("Unable to execute deletion of databases from origin %s from tracker", origin->databaseIdentifier().ascii().data()); - return; - } + originStatement.bindText(1, origin->databaseIdentifier()); - SQLiteFileSystem::deleteEmptyDatabaseDirectory(originPath(origin)); + if (!originStatement.executeCommand()) { + LOG_ERROR("Unable to execute deletion of databases from origin %s from tracker", origin->databaseIdentifier().ascii().data()); + return false; + } - RefPtr<SecurityOrigin> originPossiblyLastReference = origin; - { - MutexLocker lockQuotaMap(m_quotaMapGuard); + SQLiteFileSystem::deleteEmptyDatabaseDirectory(originPath(origin)); + + RefPtr<SecurityOrigin> originPossiblyLastReference = origin; m_quotaMap->remove(origin); - Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager()); - originQuotaManager().removeOrigin(origin); + { + Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager()); + originQuotaManager().removeOrigin(origin); + } // If we removed the last origin, do some additional deletion. if (m_quotaMap->isEmpty()) { @@ -677,31 +767,159 @@ void DatabaseTracker::deleteOrigin(SecurityOrigin* origin) SQLiteFileSystem::deleteDatabaseFile(trackerDatabasePath()); SQLiteFileSystem::deleteEmptyDatabaseDirectory(m_databaseDirectoryPath); } + + if (m_client) { + m_client->dispatchDidModifyOrigin(origin); + for (unsigned i = 0; i < databaseNames.size(); ++i) + m_client->dispatchDidModifyDatabase(origin, databaseNames[i]); + } } + return true; +} - if (m_client) { - m_client->dispatchDidModifyOrigin(origin); - for (unsigned i = 0; i < databaseNames.size(); ++i) - m_client->dispatchDidModifyDatabase(origin, databaseNames[i]); +bool DatabaseTracker::canCreateDatabase(SecurityOrigin *origin, const String& name) +{ + ASSERT(!m_databaseGuard.tryLock()); + // Can't create a database while someone else is deleting it; there's a risk of leaving untracked database debris on the disk. + return !deletingDatabase(origin, name) && !deletingOrigin(origin); +} + +void DatabaseTracker::recordCreatingDatabase(SecurityOrigin *origin, const String& name) +{ + ASSERT(!m_databaseGuard.tryLock()); + NameCountMap* nameMap = m_beingCreated.get(origin); + if (!nameMap) { + nameMap = new NameCountMap(); + m_beingCreated.set(origin->threadsafeCopy(), nameMap); } + long count = nameMap->get(name); + nameMap->set(name.threadsafeCopy(), count + 1); } -void DatabaseTracker::deleteDatabase(SecurityOrigin* origin, const String& name) +void DatabaseTracker::doneCreatingDatabase(SecurityOrigin *origin, const String& name) { - ASSERT(currentThread() == m_thread); - openTrackerDatabase(false); - if (!m_database.isOpen()) - return; + ASSERT(!m_databaseGuard.tryLock()); + NameCountMap* nameMap = m_beingCreated.get(origin); + if (nameMap) { + long count = nameMap->get(name); + ASSERT(count > 0); + if (count <= 1) { + nameMap->remove(name); + if (nameMap->isEmpty()) { + m_beingCreated.remove(origin); + delete nameMap; + } + } else + nameMap->set(name, count - 1); + } else + ASSERT(false); +} + +bool DatabaseTracker::creatingDatabase(SecurityOrigin *origin, const String& name) +{ + ASSERT(!m_databaseGuard.tryLock()); + NameCountMap* nameMap = m_beingCreated.get(origin); + return nameMap && nameMap->get(name); +} + +bool DatabaseTracker::canDeleteDatabase(SecurityOrigin *origin, const String& name) +{ + ASSERT(!m_databaseGuard.tryLock()); + return !creatingDatabase(origin, name) && !deletingDatabase(origin, name); +} + +void DatabaseTracker::recordDeletingDatabase(SecurityOrigin *origin, const String& name) +{ + ASSERT(!m_databaseGuard.tryLock()); + ASSERT(canDeleteDatabase(origin, name)); + NameSet* nameSet = m_beingDeleted.get(origin); + if (!nameSet) { + nameSet = new NameSet(); + m_beingDeleted.set(origin->threadsafeCopy(), nameSet); + } + ASSERT(!nameSet->contains(name)); + nameSet->add(name.threadsafeCopy()); +} + +void DatabaseTracker::doneDeletingDatabase(SecurityOrigin *origin, const String& name) +{ + ASSERT(!m_databaseGuard.tryLock()); + NameSet* nameSet = m_beingDeleted.get(origin); + if (nameSet) { + ASSERT(nameSet->contains(name)); + nameSet->remove(name); + if (nameSet->isEmpty()) { + m_beingDeleted.remove(origin); + delete nameSet; + } + } else { + ASSERT(false); + } +} + +bool DatabaseTracker::deletingDatabase(SecurityOrigin *origin, const String& name) +{ + ASSERT(!m_databaseGuard.tryLock()); + NameSet* nameSet = m_beingDeleted.get(origin); + return nameSet && nameSet->contains(name); +} + +bool DatabaseTracker::canDeleteOrigin(SecurityOrigin *origin) +{ + ASSERT(!m_databaseGuard.tryLock()); + return !(deletingOrigin(origin) || m_beingCreated.get(origin)); +} + +bool DatabaseTracker::deletingOrigin(SecurityOrigin *origin) +{ + ASSERT(!m_databaseGuard.tryLock()); + return m_originsBeingDeleted.contains(origin); +} + +void DatabaseTracker::recordDeletingOrigin(SecurityOrigin *origin) +{ + ASSERT(!m_databaseGuard.tryLock()); + ASSERT(!deletingOrigin(origin)); + m_originsBeingDeleted.add(origin->threadsafeCopy()); +} + +void DatabaseTracker::doneDeletingOrigin(SecurityOrigin *origin) +{ + ASSERT(!m_databaseGuard.tryLock()); + ASSERT(deletingOrigin(origin)); + m_originsBeingDeleted.remove(origin); +} +bool DatabaseTracker::deleteDatabase(SecurityOrigin* origin, const String& name) +{ + { + MutexLocker lockDatabase(m_databaseGuard); + openTrackerDatabase(false); + if (!m_database.isOpen()) + return false; + + if (!canDeleteDatabase(origin, name)) { + ASSERT(FALSE); + return false; + } + recordDeletingDatabase(origin, name); + } + + // We drop the lock here because holding locks during a call to deleteDatabaseFile will deadlock. if (!deleteDatabaseFile(origin, name)) { LOG_ERROR("Unable to delete file for database %s in origin %s", name.ascii().data(), origin->databaseIdentifier().ascii().data()); - return; + MutexLocker lockDatabase(m_databaseGuard); + doneDeletingDatabase(origin, name); + return false; } + MutexLocker lockDatabase(m_databaseGuard); + SQLiteStatement statement(m_database, "DELETE FROM Databases WHERE origin=? AND name=?"); if (statement.prepare() != SQLResultOk) { LOG_ERROR("Unable to prepare deletion of database %s from origin %s from tracker", name.ascii().data(), origin->databaseIdentifier().ascii().data()); - return; + doneDeletingDatabase(origin, name); + return false; } statement.bindText(1, origin->databaseIdentifier()); @@ -709,7 +927,8 @@ void DatabaseTracker::deleteDatabase(SecurityOrigin* origin, const String& name) if (!statement.executeCommand()) { LOG_ERROR("Unable to execute deletion of database %s from origin %s from tracker", name.ascii().data(), origin->databaseIdentifier().ascii().data()); - return; + doneDeletingDatabase(origin, name); + return false; } { @@ -721,28 +940,38 @@ void DatabaseTracker::deleteDatabase(SecurityOrigin* origin, const String& name) m_client->dispatchDidModifyOrigin(origin); m_client->dispatchDidModifyDatabase(origin, name); } + doneDeletingDatabase(origin, name); + + return true; } +// deleteDatabaseFile has to release locks between looking up the list of databases to close and closing them. While this is in progress, the caller +// is responsible for making sure no new databases are opened in the file to be deleted. bool DatabaseTracker::deleteDatabaseFile(SecurityOrigin* origin, const String& name) { - ASSERT(currentThread() == m_thread); String fullPath = fullPathForDatabase(origin, name, false); if (fullPath.isEmpty()) return true; +#ifndef NDEBUG + { + MutexLocker lockDatabase(m_databaseGuard); + ASSERT(deletingDatabase(origin, name) || deletingOrigin(origin)); + } +#endif + Vector<RefPtr<Database> > deletedDatabases; - // Make sure not to hold the m_openDatabaseMapGuard mutex when calling + // Make sure not to hold the any locks when calling // Database::markAsDeletedAndClose(), since that can cause a deadlock // during the synchronous DatabaseThread call it triggers. - { MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard); if (m_openDatabaseMap) { // There are some open databases, lets check if they are for this origin. DatabaseNameMap* nameMap = m_openDatabaseMap->get(origin); if (nameMap && nameMap->size()) { - // There are some open databases for this origin, lets check + // There are some open databases for this origin, let's check // if they are this database by name. DatabaseSet* databaseSet = nameMap->get(name); if (databaseSet && databaseSet->size()) { @@ -763,7 +992,6 @@ bool DatabaseTracker::deleteDatabaseFile(SecurityOrigin* origin, const String& n void DatabaseTracker::setClient(DatabaseTrackerClient* client) { - ASSERT(currentThread() == m_thread); m_client = client; } @@ -773,7 +1001,7 @@ static Mutex& notificationMutex() return mutex; } -typedef Vector<pair<SecurityOrigin*, String> > NotificationQueue; +typedef Vector<pair<RefPtr<SecurityOrigin>, String> > NotificationQueue; static NotificationQueue& notificationQueue() { @@ -785,7 +1013,7 @@ void DatabaseTracker::scheduleNotifyDatabaseChanged(SecurityOrigin* origin, cons { MutexLocker locker(notificationMutex()); - notificationQueue().append(pair<SecurityOrigin*, String>(origin, name.crossThreadString())); + notificationQueue().append(pair<RefPtr<SecurityOrigin>, String>(origin->threadsafeCopy(), name.crossThreadString())); scheduleForNotification(); } @@ -820,7 +1048,7 @@ void DatabaseTracker::notifyDatabasesChanged(void*) return; for (unsigned i = 0; i < notifications.size(); ++i) - theTracker.m_client->dispatchDidModifyDatabase(notifications[i].first, notifications[i].second); + theTracker.m_client->dispatchDidModifyDatabase(notifications[i].first.get(), notifications[i].second); } |