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; |
} |