| Index: WebCore/storage/DatabaseTracker.cpp
|
| ===================================================================
|
| --- WebCore/storage/DatabaseTracker.cpp (revision 55036)
|
| +++ WebCore/storage/DatabaseTracker.cpp (working copy)
|
| @@ -51,13 +51,19 @@
|
|
|
| namespace WebCore {
|
|
|
| -OriginQuotaManager& DatabaseTracker::originQuotaManager()
|
| +OriginQuotaManager& DatabaseTracker::originQuotaManagerNoLock()
|
| {
|
| - populateOrigins();
|
| ASSERT(m_quotaManager);
|
| return *m_quotaManager;
|
| }
|
|
|
| +OriginQuotaManager& DatabaseTracker::originQuotaManager()
|
| +{
|
| + MutexLocker lockDatabase(m_databaseGuard);
|
| + populateOrigins();
|
| + return originQuotaManagerNoLock();
|
| +}
|
| +
|
| DatabaseTracker& DatabaseTracker::tracker()
|
| {
|
| DEFINE_STATIC_LOCAL(DatabaseTracker, tracker, ());
|
| @@ -66,36 +72,29 @@
|
|
|
| DatabaseTracker::DatabaseTracker()
|
| : m_client(0)
|
| - , m_proposedDatabase(0)
|
| -#ifndef NDEBUG
|
| - , m_thread(currentThread())
|
| -#endif
|
| {
|
| SQLiteFileSystem::registerSQLiteVFS();
|
| }
|
|
|
| void DatabaseTracker::setDatabaseDirectoryPath(const String& path)
|
| {
|
| - ASSERT(currentThread() == m_thread);
|
| ASSERT(!m_database.isOpen());
|
| m_databaseDirectoryPath = path;
|
| }
|
|
|
| -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 +105,87 @@
|
|
|
| if (!m_database.open(databasePath)) {
|
| // FIXME: What do do here?
|
| + LOG_ERROR("Failed to open databasePath %s.", databasePath.ascii().data());
|
| return;
|
| }
|
| + m_database.setSharable(true);
|
| 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;
|
| + {
|
| + Locker<OriginQuotaManager> locker(originQuotaManager());
|
| + MutexLocker lockDatabase(m_databaseGuard);
|
|
|
| - SecurityOrigin* origin = context->securityOrigin();
|
| + populateOrigins();
|
|
|
| - // 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);
|
| + // 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 a database already exists, ignore the passed-in estimated size and say it's OK.
|
| - if (hasEntryForDatabase(origin, name))
|
| - return true;
|
| + // If a database already exists, ignore the passed-in estimated size and say it's OK.
|
| + if (hasEntryForDatabase(origin, name))
|
| + return true;
|
|
|
| - // 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 the database will fit, allow its creation.
|
| + requirement = usage + max(1UL, estimatedSize);
|
| + tempUsage = usage;
|
| + if (requirement < usage)
|
| + 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.
|
| - pair<SecurityOrigin*, DatabaseDetails> details(origin, DatabaseDetails(name, displayName, estimatedSize, 0));
|
| - m_proposedDatabase = &details;
|
| + // 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, DatabaseDetails(name, displayName, 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);
|
| }
|
|
|
| +bool DatabaseTracker::hasEntryForOriginNoLock(SecurityOrigin* origin)
|
| +{
|
| + ASSERT(!m_databaseGuard.tryLock());
|
| + ASSERT(m_quotaMap);
|
| + return m_quotaMap->contains(origin);
|
| +}
|
| +
|
| bool DatabaseTracker::hasEntryForOrigin(SecurityOrigin* origin)
|
| {
|
| - ASSERT(currentThread() == m_thread);
|
| + MutexLocker lockDatabase(m_databaseGuard);
|
| populateOrigins();
|
| - MutexLocker lockQuotaMap(m_quotaMapGuard);
|
| - return m_quotaMap->contains(origin);
|
| + 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;
|
| @@ -193,16 +212,17 @@
|
|
|
| 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(!originQuotaManagerNoLock().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)->first == origin && (*iter)->second.name() == name)
|
| + return String();
|
|
|
| String originIdentifier = origin->databaseIdentifier();
|
| String originPath = this->originPath(origin);
|
| @@ -212,7 +232,7 @@
|
| return String();
|
|
|
| // See if we have a path for this database yet
|
| - openTrackerDatabase(false);
|
| + // TODO: Why don't we try to open the database first?
|
| if (!m_database.isOpen())
|
| return String();
|
| SQLiteStatement statement(m_database, "SELECT path FROM Databases WHERE origin=? AND name=?;");
|
| @@ -231,36 +251,42 @@
|
| 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 (originQuotaManagerNoLock().tracksOrigin(origin))
|
| + originQuotaManagerNoLock().addDatabase(origin, name, fullFilePath);
|
|
|
| return fullFilePath;
|
| }
|
|
|
| +String DatabaseTracker::fullPathForDatabase(SecurityOrigin* origin, const String& name, bool createIfNotExists)
|
| +{
|
| + Locker<OriginQuotaManager> locker(originQuotaManager());
|
| + MutexLocker lockDatabase(m_databaseGuard);
|
| + populateOrigins();
|
| +
|
| + return fullPathForDatabaseNoLock(origin, name, createIfNotExists);
|
| +}
|
| +
|
| 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);
|
| + if (!m_quotaManager)
|
| + m_quotaManager.set(new OriginQuotaManager);
|
|
|
| openTrackerDatabase(false);
|
| if (!m_database.isOpen())
|
| @@ -268,8 +294,10 @@
|
|
|
| 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) {
|
| @@ -278,20 +306,20 @@
|
| }
|
|
|
| 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);
|
| + MutexLocker lockDatabase(m_databaseGuard);
|
| populateOrigins();
|
| - MutexLocker lockQuotaMap(m_quotaMapGuard);
|
| + 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 +343,57 @@
|
| return true;
|
| }
|
|
|
| +bool DatabaseTracker::databaseNamesForOrigin(SecurityOrigin* origin, Vector<String>& resultVector)
|
| +{
|
| + MutexLocker lockDatabase(m_databaseGuard);
|
| + return databaseNamesForOriginNoLock(origin, resultVector);
|
| +}
|
| +
|
| DatabaseDetails DatabaseTracker::detailsForNameAndOrigin(const String& name, SecurityOrigin* origin)
|
| {
|
| - ASSERT(currentThread() == m_thread);
|
| + String originIdentifier = origin->databaseIdentifier();
|
| + String displayName;
|
| + int64_t expectedUsage;
|
|
|
| - if (m_proposedDatabase && m_proposedDatabase->first == origin && m_proposedDatabase->second.name() == name)
|
| - return m_proposedDatabase->second;
|
| + {
|
| + MutexLocker lockDatabase(m_databaseGuard);
|
|
|
| - String originIdentifier = origin->databaseIdentifier();
|
| + for (HashSet<ProposedDatabase*>::iterator iter = m_proposedDatabases.begin(); iter != m_proposedDatabases.end(); ++iter)
|
| + if ((*iter)->first == origin && (*iter)->second.name() == name)
|
| + return (*iter)->second;
|
|
|
| - 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();
|
| + 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();
|
|
|
| - statement.bindText(1, originIdentifier);
|
| - statement.bindText(2, name);
|
| + statement.bindText(1, originIdentifier);
|
| + statement.bindText(2, name);
|
|
|
| - int result = statement.step();
|
| - if (result == SQLResultDone)
|
| - return DatabaseDetails();
|
| + 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();
|
| + 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 +441,6 @@
|
|
|
| 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;
|
| @@ -441,6 +481,7 @@
|
| if (!database)
|
| return;
|
|
|
| + Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager());
|
| MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard);
|
|
|
| if (!m_openDatabaseMap) {
|
| @@ -476,6 +517,10 @@
|
|
|
| m_openDatabaseMap->remove(database->securityOrigin());
|
| delete nameMap;
|
| + originQuotaManagerNoLock().removeOrigin(database->securityOrigin());
|
| +
|
| + // Throw away m_quotaMap whenever we've cleared out an origin--otherwise we never get rid of stale entries. The next call that needs it will populateOrigins().
|
| + m_quotaMap.clear();
|
| }
|
|
|
| void DatabaseTracker::getOpenDatabases(SecurityOrigin* origin, const String& name, HashSet<RefPtr<Database> >* databases)
|
| @@ -496,92 +541,103 @@
|
| 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(!originQuotaManagerNoLock().tryLock());
|
|
|
| // Use the OriginQuotaManager mechanism to calculate the usage
|
| - if (originQuotaManager().tracksOrigin(origin))
|
| - return originQuotaManager().diskUsage(origin);
|
| + if (originQuotaManagerNoLock().tracksOrigin(origin))
|
| + return originQuotaManagerNoLock().diskUsage(origin);
|
|
|
| // If the OriginQuotaManager doesn't track this origin already, prime it to do so
|
| - originQuotaManager().trackOrigin(origin);
|
| + originQuotaManagerNoLock().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));
|
| + originQuotaManagerNoLock().addDatabase(origin, names[i], fullPathForDatabaseNoLock(origin, names[i], false));
|
|
|
| - if (!originQuotaManager().tracksOrigin(origin))
|
| + if (!originQuotaManagerNoLock().tracksOrigin(origin))
|
| return 0;
|
| - return originQuotaManager().diskUsage(origin);
|
| + return originQuotaManagerNoLock().diskUsage(origin);
|
| }
|
|
|
| +unsigned long long DatabaseTracker::usageForOrigin(SecurityOrigin* origin)
|
| +{
|
| + Locker<OriginQuotaManager> locker(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)
|
| {
|
| - ASSERT(currentThread() == m_thread || m_quotaMap);
|
| + MutexLocker lockDatabase(m_databaseGuard);
|
| populateOrigins();
|
| - MutexLocker lockQuotaMap(m_quotaMapGuard);
|
| - return m_quotaMap->get(origin);
|
| + return quotaForOriginNoLock(origin);
|
| }
|
|
|
| void DatabaseTracker::setQuota(SecurityOrigin* origin, unsigned long long quota)
|
| {
|
| - ASSERT(currentThread() == m_thread);
|
| - if (quotaForOrigin(origin) == quota)
|
| + MutexLocker lockDatabase(m_databaseGuard);
|
| +
|
| + populateOrigins();
|
| + 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 (!m_quotaMap->contains(origin)) {
|
| - SQLiteStatement statement(m_database, "INSERT INTO Origins VALUES (?, ?)");
|
| - if (statement.prepare() != SQLResultOk) {
|
| + if (statement.step() != SQLResultDone)
|
| LOG_ERROR("Unable to establish origin %s in the tracker", origin->databaseIdentifier().ascii().data());
|
| - } else {
|
| - statement.bindText(1, origin->databaseIdentifier());
|
| - statement.bindInt64(2, quota);
|
| + }
|
| + } 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());
|
|
|
| - 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();
|
| - }
|
| -
|
| - if (error)
|
| - LOG_ERROR("Failed to set quota %llu in tracker database for origin %s", quota, origin->databaseIdentifier().ascii().data());
|
| + 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, 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 +661,6 @@
|
|
|
| void DatabaseTracker::deleteAllDatabases()
|
| {
|
| - ASSERT(currentThread() == m_thread);
|
| -
|
| Vector<RefPtr<SecurityOrigin> > originsCopy;
|
| origins(originsCopy);
|
|
|
| @@ -614,19 +668,24 @@
|
| deleteOrigin(originsCopy[i].get());
|
| }
|
|
|
| +// 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.
|
| void DatabaseTracker::deleteOrigin(SecurityOrigin* origin)
|
| {
|
| - ASSERT(currentThread() == m_thread);
|
| - openTrackerDatabase(false);
|
| - if (!m_database.isOpen())
|
| - return;
|
| + Vector<String> databaseNames;
|
| + {
|
| + MutexLocker lockDatabase(m_databaseGuard);
|
| + 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;
|
| + if (!databaseNamesForOriginNoLock(origin, databaseNames)) {
|
| + LOG_ERROR("Unable to retrieve list of database names for origin %s", origin->databaseIdentifier().ascii().data());
|
| + return;
|
| + }
|
| }
|
|
|
| + // 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 +693,42 @@
|
| }
|
| }
|
|
|
| - 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;
|
| - }
|
| + {
|
| + // To satisfy the lock hierarchy, we have to lock the originQuotaManager before m_databaseGuard if there's any chance we'll to lock both.
|
| + Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager());
|
| + MutexLocker lockDatabase(m_databaseGuard);
|
| + 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;
|
| + }
|
|
|
| - statement.bindText(1, origin->databaseIdentifier());
|
| + statement.bindText(1, origin->databaseIdentifier());
|
|
|
| - if (!statement.executeCommand()) {
|
| - LOG_ERROR("Unable to execute deletion of databases from 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;
|
| + }
|
|
|
| - 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;
|
| - }
|
| + 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;
|
| + }
|
|
|
| - originStatement.bindText(1, origin->databaseIdentifier());
|
| + originStatement.bindText(1, origin->databaseIdentifier());
|
|
|
| - if (!originStatement.executeCommand()) {
|
| - LOG_ERROR("Unable to execute deletion of databases from origin %s from tracker", origin->databaseIdentifier().ascii().data());
|
| - return;
|
| - }
|
| + if (!originStatement.executeCommand()) {
|
| + LOG_ERROR("Unable to execute deletion of databases from origin %s from tracker", origin->databaseIdentifier().ascii().data());
|
| + return;
|
| + }
|
|
|
| - SQLiteFileSystem::deleteEmptyDatabaseDirectory(originPath(origin));
|
| + SQLiteFileSystem::deleteEmptyDatabaseDirectory(originPath(origin));
|
|
|
| - RefPtr<SecurityOrigin> originPossiblyLastReference = origin;
|
| - {
|
| - MutexLocker lockQuotaMap(m_quotaMapGuard);
|
| + RefPtr<SecurityOrigin> originPossiblyLastReference = origin;
|
| m_quotaMap->remove(origin);
|
|
|
| - Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager());
|
| - originQuotaManager().removeOrigin(origin);
|
| + originQuotaManagerNoLock().removeOrigin(origin);
|
|
|
| // If we removed the last origin, do some additional deletion.
|
| if (m_quotaMap->isEmpty()) {
|
| @@ -677,27 +737,34 @@
|
| 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]);
|
| + if (m_client) {
|
| + m_client->dispatchDidModifyOrigin(origin);
|
| + for (unsigned i = 0; i < databaseNames.size(); ++i)
|
| + m_client->dispatchDidModifyDatabase(origin, databaseNames[i]);
|
| + }
|
| }
|
| }
|
|
|
| void DatabaseTracker::deleteDatabase(SecurityOrigin* origin, const String& name)
|
| {
|
| - ASSERT(currentThread() == m_thread);
|
| - openTrackerDatabase(false);
|
| - if (!m_database.isOpen())
|
| - return;
|
| + {
|
| + MutexLocker lockDatabase(m_databaseGuard);
|
| + openTrackerDatabase(false);
|
| + if (!m_database.isOpen())
|
| + return;
|
| + }
|
|
|
| + // 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;
|
| }
|
|
|
| + // To satisfy the lock hierarchy, we have to lock the originQuotaManager before m_databaseGuard if there's any chance we'll to lock both.
|
| + Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager());
|
| + 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());
|
| @@ -712,10 +779,7 @@
|
| return;
|
| }
|
|
|
| - {
|
| - Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager());
|
| - originQuotaManager().removeDatabase(origin, name);
|
| - }
|
| + originQuotaManagerNoLock().removeDatabase(origin, name);
|
|
|
| if (m_client) {
|
| m_client->dispatchDidModifyOrigin(origin);
|
| @@ -723,26 +787,31 @@
|
| }
|
| }
|
|
|
| +// 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);
|
| + LOG_ERROR("in deleteDatabaseFile(%s,%s)",
|
| + origin->databaseIdentifier().ascii().data(), name.ascii().data());
|
| String fullPath = fullPathForDatabase(origin, name, false);
|
| - if (fullPath.isEmpty())
|
| + if (fullPath.isEmpty()) {
|
| + LOG_ERROR("out [empty] deleteDatabaseFile(%s,%s)",
|
| + origin->databaseIdentifier().ascii().data(), name.ascii().data());
|
| return true;
|
| + }
|
|
|
| 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()) {
|
| @@ -758,12 +827,13 @@
|
| for (unsigned i = 0; i < deletedDatabases.size(); ++i)
|
| deletedDatabases[i]->markAsDeletedAndClose();
|
|
|
| + LOG_ERROR("out [full] deleteDatabaseFile(%s,%s)",
|
| + origin->databaseIdentifier().ascii().data(), name.ascii().data());
|
| return SQLiteFileSystem::deleteDatabaseFile(fullPath);
|
| }
|
|
|
| void DatabaseTracker::setClient(DatabaseTrackerClient* client)
|
| {
|
| - ASSERT(currentThread() == m_thread);
|
| m_client = client;
|
| }
|
|
|
|
|