Changeset 56293 in webkit
- Timestamp:
- Mar 19, 2010 8:03:37 PM (14 years ago)
- Location:
- trunk/WebCore
- Files:
-
- 13 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/WebCore/ChangeLog
r56292 r56293 1 2010-03-19 Eric Uhrhane <ericu@chromium.org> 2 3 Reviewed by Dmitry Titov. 4 5 Refactor DatabaseTracker.cpp for thread safety 6 https://bugs.webkit.org/show_bug.cgi?id=34991 7 8 This enables calling into DatabaseTracker from multiple context threads, 9 as will happen once Workers can access the Database. It required a fair 10 amount of reshuffling of locks. I ended up splitting the public 11 interface [calls that take locks and call private functions] from the 12 implementations [calls that assert that locks are already held] in order 13 to avoid lock conflicts. I also had to make sure we weren't sharing 14 Strings or SecurityOrigins across threads. 15 16 No new tests. 17 18 Allow access to database handles from multiple threads IFF SQLite is new enough and the user requests it. 19 20 * platform/sql/SQLiteDatabase.cpp: 21 (WebCore::SQLiteDatabase::SQLiteDatabase): 22 (WebCore::SQLiteDatabase::disableThreadingChecks): 23 * platform/sql/SQLiteDatabase.h: 24 (WebCore::SQLiteDatabase::sqlite3Handle): 25 (WebCore::SQLiteDatabase::disableThreadingChecks): 26 27 Remove an asynchronous call from Database::close back to the execution thread, so that cleanup can be more deterministic. 28 29 * storage/Database.cpp: 30 (WebCore::Database::markAsDeletedAndClose): 31 (WebCore::Database::close): 32 * storage/Database.h: 33 (WebCore::Database::): 34 * storage/DatabaseDetails.h: 35 (WebCore::DatabaseDetails::DatabaseDetails): 36 (WebCore::DatabaseDetails::thread): 37 * storage/DatabaseTask.cpp: 38 (WebCore::DatabaseCloseTask::doPerformTask): 39 * storage/DatabaseThread.cpp: 40 (WebCore::DatabaseThread::databaseThread): 41 42 Any Strings that get stored in DatabaseTracker, and any Strings returned from DatabaseTracker, are now threadsafeCopies. 43 Public functions now take all needed locks, then generally call only private functions [there are a few exceptions: deletion functions and origins()]. 44 Private functions no longer take locks. 45 m_quotaMapGuard becomes m_databaseGuard, and now protects m_database, m_quotaMap, m_proposedDatabases, m_databaseDirectoryPath, m_originsBeingDeleted, m_beingCreated, and m_beingDeleted. 46 m_proposedDatabases replaces m_proposedDatabase, to account for reentrancy. 47 48 * storage/DatabaseTracker.h: 49 * storage/DatabaseTracker.cpp: 50 (WebCore::DatabaseTracker::originQuotaManagerNoLock): 51 (WebCore::DatabaseTracker::originQuotaManager): 52 (WebCore::DatabaseTracker::DatabaseTracker): 53 (WebCore::DatabaseTracker::setDatabaseDirectoryPath): 54 (WebCore::DatabaseTracker::databaseDirectoryPath): 55 (WebCore::DatabaseTracker::trackerDatabasePath): 56 (WebCore::DatabaseTracker::openTrackerDatabase): 57 (WebCore::DatabaseTracker::canEstablishDatabase): 58 (WebCore::DatabaseTracker::hasEntryForOriginNoLock): 59 (WebCore::DatabaseTracker::hasEntryForOrigin): 60 (WebCore::DatabaseTracker::hasEntryForDatabase): 61 (WebCore::DatabaseTracker::originPath): 62 (WebCore::DatabaseTracker::fullPathForDatabaseNoLock): 63 (WebCore::DatabaseTracker::fullPathForDatabase): 64 (WebCore::DatabaseTracker::populateOrigins): 65 (WebCore::DatabaseTracker::origins): 66 (WebCore::DatabaseTracker::databaseNamesForOriginNoLock): 67 (WebCore::DatabaseTracker::databaseNamesForOrigin): 68 (WebCore::DatabaseTracker::detailsForNameAndOrigin): 69 (WebCore::DatabaseTracker::setDatabaseDetails): 70 (WebCore::DatabaseTracker::usageForDatabase): 71 (WebCore::DatabaseTracker::addOpenDatabase): 72 (WebCore::DatabaseTracker::removeOpenDatabase): 73 (WebCore::DatabaseTracker::usageForOriginNoLock): 74 (WebCore::DatabaseTracker::usageForOrigin): 75 (WebCore::DatabaseTracker::quotaForOriginNoLock): 76 (WebCore::DatabaseTracker::quotaForOrigin): 77 (WebCore::DatabaseTracker::setQuota): 78 (WebCore::DatabaseTracker::addDatabase): 79 (WebCore::DatabaseTracker::deleteAllDatabases): 80 (WebCore::DatabaseTracker::deleteOrigin): 81 (WebCore::DatabaseTracker::deleteDatabase): 82 (WebCore::DatabaseTracker::deleteDatabaseFile): 83 (WebCore::DatabaseTracker::setClient): 84 (WebCore::DatabaseTracker::scheduleNotifyDatabaseChanged): 85 (WebCore::DatabaseTracker::notifyDatabasesChanged): 86 87 These functions keep track of in-progress deletions and creations, so that we can make sure nobody every deletes a database file while a live database is using it. 88 (WebCore::DatabaseTracker::canCreateDatabase): 89 (WebCore::DatabaseTracker::recordCreatingDatabase): 90 (WebCore::DatabaseTracker::doneCreatingDatabase): 91 (WebCore::DatabaseTracker::creatingDatabase): 92 (WebCore::DatabaseTracker::canDeleteDatabase): 93 (WebCore::DatabaseTracker::recordDeletingDatabase): 94 (WebCore::DatabaseTracker::doneDeletingDatabase): 95 (WebCore::DatabaseTracker::deletingDatabase): 96 (WebCore::DatabaseTracker::canDeleteOrigin): 97 (WebCore::DatabaseTracker::deletingOrigin): 98 (WebCore::DatabaseTracker::recordDeletingOrigin): 99 (WebCore::DatabaseTracker::doneDeletingOrigin): 100 101 Any SecurityOrigins stored in OriginQuotaManager are now threadsafeCopies of inputs. There's a new tryLock() function in addition to the existing lock(). 102 103 * storage/OriginQuotaManager.cpp: 104 (WebCore::OriginQuotaManager::tryLock): 105 (WebCore::OriginQuotaManager::trackOrigin): 106 * storage/OriginQuotaManager.h: 107 108 * page/SecurityOrigin.cpp: 109 (WebCore::SecurityOrigin::databaseIdentifier): 110 Removed DEFINE_STATIC_LOCAL wrapper on a local variable; it appears to have been a small optimization, but it's not thread-safe. 111 1 112 2010-03-19 Luiz Agostini <luiz.agostini@openbossa.org> 2 113 -
trunk/WebCore/page/SecurityOrigin.cpp
r56139 r56293 463 463 String SecurityOrigin::databaseIdentifier() const 464 464 { 465 DEFINE_STATIC_LOCAL(String, separatorString, (&SeparatorCharacter, 1));465 String separatorString(&SeparatorCharacter, 1); 466 466 467 467 if (m_encodedHost.isEmpty()) -
trunk/WebCore/platform/sql/SQLiteDatabase.cpp
r54393 r56293 49 49 , m_pageSize(-1) 50 50 , m_transactionInProgress(false) 51 , m_sharable(false) 51 52 , m_openingThread(0) 52 53 { … … 252 253 return sqlite3_errmsg(m_db); 253 254 } 255 256 #ifndef NDEBUG 257 void SQLiteDatabase::disableThreadingChecks() 258 { 259 // This doesn't guarantee that SQList was compiled with -DTHREADSAFE, or that you haven't turned off the mutexes. 260 #if SQLITE_VERSION_NUMBER >= 3003001 261 m_sharable = true; 262 #else 263 ASSERT(0); // Your SQLite doesn't support sharing handles across threads. 264 #endif 265 } 266 #endif 254 267 255 268 int SQLiteDatabase::authorizerFunction(void* userData, int actionCode, const char* parameter1, const char* parameter2, const char* /*databaseName*/, const char* /*trigger_or_view*/) -
trunk/WebCore/platform/sql/SQLiteDatabase.h
r54393 r56293 98 98 99 99 sqlite3* sqlite3Handle() const { 100 ASSERT( currentThread() == m_openingThread);100 ASSERT(m_sharable || currentThread() == m_openingThread); 101 101 return m_db; 102 102 } … … 108 108 void unlock(); 109 109 bool isAutoCommitOn() const; 110 111 // Set this flag to allow access from multiple threads. Not all multi-threaded accesses are safe! 112 // See http://www.sqlite.org/cvstrac/wiki?p=MultiThreading for more info. 113 #ifndef NDEBUG 114 void disableThreadingChecks(); 115 #else 116 void disableThreadingChecks() {} 117 #endif 110 118 111 119 private: … … 121 129 122 130 bool m_transactionInProgress; 131 bool m_sharable; 123 132 124 133 Mutex m_authorizerLock; -
trunk/WebCore/storage/Database.cpp
r55823 r56293 390 390 m_scriptExecutionContext->databaseThread()->scheduleImmediateTask(task.release()); 391 391 synchronizer.waitForTaskCompletion(); 392 393 // DatabaseCloseTask tells Database::close not to do these two removals so that we can do them here synchronously. 394 m_scriptExecutionContext->removeOpenDatabase(this); 395 DatabaseTracker::tracker().removeOpenDatabase(this); 392 396 } 393 397 … … 416 420 }; 417 421 418 void Database::close( )422 void Database::close(ClosePolicy policy) 419 423 { 420 424 RefPtr<Database> protect = this; … … 445 449 446 450 m_scriptExecutionContext->databaseThread()->unscheduleDatabaseTasks(this); 447 m_scriptExecutionContext->postTask(ContextRemoveOpenDatabaseTask::create(this)); 451 if (policy == RemoveDatabaseFromContext) 452 m_scriptExecutionContext->postTask(ContextRemoveOpenDatabaseTask::create(this)); 448 453 } 449 454 -
trunk/WebCore/storage/Database.h
r55823 r56293 110 110 bool deleted() const { return m_deleted; } 111 111 112 void close(); 112 enum ClosePolicy { DoNotRemoveDatabaseFromContext, RemoveDatabaseFromContext }; 113 void close(ClosePolicy); 113 114 bool opened() const { return m_opened; } 114 115 -
trunk/WebCore/storage/DatabaseDetails.h
r47808 r56293 42 42 , m_currentUsage(0) 43 43 { 44 #ifndef NDEBUG 45 m_thread = currentThread(); 46 #endif 44 47 } 45 48 … … 50 53 , m_currentUsage(currentUsage) 51 54 { 55 #ifndef NDEBUG 56 m_thread = currentThread(); 57 #endif 52 58 } 53 59 … … 56 62 unsigned long long expectedUsage() const { return m_expectedUsage; } 57 63 unsigned long long currentUsage() const { return m_currentUsage; } 64 #ifndef NDEBUG 65 ThreadIdentifier thread() const { return m_thread; } 66 #endif 58 67 59 68 private: … … 62 71 unsigned long long m_expectedUsage; 63 72 unsigned long long m_currentUsage; 64 73 #ifndef NDEBUG 74 ThreadIdentifier m_thread; 75 #endif 65 76 }; 66 77 -
trunk/WebCore/storage/DatabaseTask.cpp
r50360 r56293 118 118 void DatabaseCloseTask::doPerformTask() 119 119 { 120 database()->close(); 120 // Tell the database not to call back to the context thread; we'll handle it. 121 database()->close(Database::DoNotRemoveDatabaseFromContext); 121 122 } 122 123 -
trunk/WebCore/storage/DatabaseThread.cpp
r55429 r56293 114 114 DatabaseSet::iterator end = openSetCopy.end(); 115 115 for (DatabaseSet::iterator it = openSetCopy.begin(); it != end; ++it) 116 (*it)->close( );116 (*it)->close(Database::RemoveDatabaseFromContext); 117 117 } 118 118 -
trunk/WebCore/storage/DatabaseTracker.cpp
r54506 r56293 52 52 namespace WebCore { 53 53 54 OriginQuotaManager& DatabaseTracker::originQuotaManager() 55 { 56 populateOrigins(); 54 OriginQuotaManager& DatabaseTracker::originQuotaManagerNoLock() 55 { 57 56 ASSERT(m_quotaManager); 58 57 return *m_quotaManager; 59 58 } 60 59 60 OriginQuotaManager& DatabaseTracker::originQuotaManager() 61 { 62 MutexLocker lockDatabase(m_databaseGuard); 63 populateOrigins(); 64 return originQuotaManagerNoLock(); 65 } 66 61 67 DatabaseTracker& DatabaseTracker::tracker() 62 68 { … … 67 73 DatabaseTracker::DatabaseTracker() 68 74 : m_client(0) 69 , m_proposedDatabase(0)70 #ifndef NDEBUG71 , m_thread(currentThread())72 #endif73 75 { 74 76 SQLiteFileSystem::registerSQLiteVFS(); … … 77 79 void DatabaseTracker::setDatabaseDirectoryPath(const String& path) 78 80 { 79 ASSERT(currentThread() == m_thread);81 MutexLocker lockDatabase(m_databaseGuard); 80 82 ASSERT(!m_database.isOpen()); 81 m_databaseDirectoryPath = path; 82 } 83 84 const String& DatabaseTracker::databaseDirectoryPath() const 85 { 86 ASSERT(currentThread() == m_thread); 87 return m_databaseDirectoryPath; 83 m_databaseDirectoryPath = path.threadsafeCopy(); 84 } 85 86 String DatabaseTracker::databaseDirectoryPath() const 87 { 88 return m_databaseDirectoryPath.threadsafeCopy(); 88 89 } 89 90 90 91 String DatabaseTracker::trackerDatabasePath() const 91 92 { 92 ASSERT(currentThread() == m_thread);93 93 return SQLiteFileSystem::appendDatabaseFileNameToPath(m_databaseDirectoryPath, "Databases.db"); 94 94 } … … 96 96 void DatabaseTracker::openTrackerDatabase(bool createIfDoesNotExist) 97 97 { 98 ASSERT( currentThread() == m_thread);98 ASSERT(!m_databaseGuard.tryLock()); 99 99 100 100 if (m_database.isOpen()) … … 107 107 if (!m_database.open(databasePath)) { 108 108 // FIXME: What do do here? 109 return; 110 } 109 LOG_ERROR("Failed to open databasePath %s.", databasePath.ascii().data()); 110 return; 111 } 112 m_database.disableThreadingChecks(); 111 113 if (!m_database.tableExists("Origins")) { 112 114 if (!m_database.executeCommand("CREATE TABLE Origins (origin TEXT UNIQUE ON CONFLICT REPLACE, quota INTEGER NOT NULL ON CONFLICT FAIL);")) { 113 115 // FIXME: and here 116 LOG_ERROR("Failed to create Origins table"); 114 117 } 115 118 } … … 117 120 if (!m_database.executeCommand("CREATE TABLE Databases (guid INTEGER PRIMARY KEY AUTOINCREMENT, origin TEXT, name TEXT, displayName TEXT, estimatedSize INTEGER, path TEXT);")) { 118 121 // FIXME: and here 122 LOG_ERROR("Failed to create Databases table"); 119 123 } 120 124 } … … 123 127 bool DatabaseTracker::canEstablishDatabase(ScriptExecutionContext* context, const String& name, const String& displayName, unsigned long estimatedSize) 124 128 { 125 ASSERT(currentThread() == m_thread);126 127 // Populate the origins before we establish a database; this guarantees that quotaForOrigin128 // can run on the database thread later.129 populateOrigins();130 131 129 SecurityOrigin* origin = context->securityOrigin(); 132 133 // Since we're imminently opening a database within this context's origin, make sure this origin is being tracked by the QuotaTracker 134 // by fetching it's current usage now 135 unsigned long long usage = usageForOrigin(origin); 136 137 // If a database already exists, ignore the passed-in estimated size and say it's OK. 138 if (hasEntryForDatabase(origin, name)) 139 return true; 140 141 // If the database will fit, allow its creation. 142 unsigned long long requirement = usage + max(1UL, estimatedSize); 143 if (requirement < usage) 144 return false; // If the estimated size is so big it causes an overflow, don't allow creation. 130 ProposedDatabase details; 131 132 unsigned long long requirement; 133 unsigned long long tempUsage; 134 { 135 Locker<OriginQuotaManager> locker(originQuotaManager()); 136 MutexLocker lockDatabase(m_databaseGuard); 137 138 if (!canCreateDatabase(origin, name)) 139 return false; 140 141 recordCreatingDatabase(origin, name); 142 143 populateOrigins(); 144 145 // Since we're imminently opening a database within this context's origin, make sure this origin is being tracked by the QuotaTracker 146 // by fetching its current usage now. 147 unsigned long long usage = usageForOriginNoLock(origin); 148 149 // If a database already exists, ignore the passed-in estimated size and say it's OK. 150 if (hasEntryForDatabase(origin, name)) 151 return true; 152 153 // If the database will fit, allow its creation. 154 requirement = usage + max(1UL, estimatedSize); 155 tempUsage = usage; 156 if (requirement < usage) { 157 doneCreatingDatabase(origin, name); 158 return false; // If the estimated size is so big it causes an overflow, don't allow creation. 159 } 160 if (requirement <= quotaForOriginNoLock(origin)) 161 return true; 162 163 // Give the chrome client a chance to increase the quota. 164 // Temporarily make the details of the proposed database available, so the client can get at them. 165 // FIXME: We should really just pass the details into this call, rather than using m_proposedDatabases. 166 details = ProposedDatabase(origin->threadsafeCopy(), DatabaseDetails(name.threadsafeCopy(), displayName.threadsafeCopy(), estimatedSize, 0)); 167 m_proposedDatabases.add(&details); 168 } 169 // Drop all locks before calling out; we don't know what they'll do. 170 context->databaseExceededQuota(name); 171 { 172 MutexLocker lockDatabase(m_databaseGuard); 173 m_proposedDatabases.remove(&details); 174 } 175 176 // If the database will fit now, allow its creation. 145 177 if (requirement <= quotaForOrigin(origin)) 146 178 return true; 147 179 148 // Give the chrome client a chance to increase the quota.149 // Temporarily make the details of the proposed database available, so the client can get at them.150 pair<SecurityOrigin*, DatabaseDetails> details(origin, DatabaseDetails(name, displayName, estimatedSize, 0)); 151 m_proposedDatabase = &details; 152 context->databaseExceededQuota(name); 153 m_proposedDatabase = 0; 154 155 // If the database will fit now, allow its creation.156 return requirement <= quotaForOrigin(origin);180 doneCreatingDatabase(origin, name); 181 return false; 182 } 183 184 bool DatabaseTracker::hasEntryForOriginNoLock(SecurityOrigin* origin) 185 { 186 ASSERT(!m_databaseGuard.tryLock()); 187 ASSERT(m_quotaMap); 188 return m_quotaMap->contains(origin); 157 189 } 158 190 159 191 bool DatabaseTracker::hasEntryForOrigin(SecurityOrigin* origin) 160 192 { 161 ASSERT(currentThread() == m_thread);193 MutexLocker lockDatabase(m_databaseGuard); 162 194 populateOrigins(); 163 MutexLocker lockQuotaMap(m_quotaMapGuard); 164 return m_quotaMap->contains(origin); 195 return hasEntryForOriginNoLock(origin); 165 196 } 166 197 167 198 bool DatabaseTracker::hasEntryForDatabase(SecurityOrigin* origin, const String& databaseIdentifier) 168 199 { 169 ASSERT( currentThread() == m_thread);200 ASSERT(!m_databaseGuard.tryLock()); 170 201 openTrackerDatabase(false); 171 202 if (!m_database.isOpen()) … … 194 225 String DatabaseTracker::originPath(SecurityOrigin* origin) const 195 226 { 196 ASSERT(currentThread() == m_thread); 197 return SQLiteFileSystem::appendDatabaseFileNameToPath(m_databaseDirectoryPath, origin->databaseIdentifier()); 198 } 199 200 String DatabaseTracker::fullPathForDatabase(SecurityOrigin* origin, const String& name, bool createIfNotExists) 201 { 202 ASSERT(currentThread() == m_thread); 203 204 if (m_proposedDatabase && m_proposedDatabase->first == origin && m_proposedDatabase->second.name() == name) 205 return String(); 227 return SQLiteFileSystem::appendDatabaseFileNameToPath(m_databaseDirectoryPath.threadsafeCopy(), origin->databaseIdentifier()); 228 } 229 230 String DatabaseTracker::fullPathForDatabaseNoLock(SecurityOrigin* origin, const String& name, bool createIfNotExists) 231 { 232 ASSERT(!m_databaseGuard.tryLock()); 233 ASSERT(!originQuotaManagerNoLock().tryLock()); 234 235 for (HashSet<ProposedDatabase*>::iterator iter = m_proposedDatabases.begin(); iter != m_proposedDatabases.end(); ++iter) 236 if ((*iter)->first == origin && (*iter)->second.name() == name) 237 return String(); 206 238 207 239 String originIdentifier = origin->databaseIdentifier(); … … 213 245 214 246 // See if we have a path for this database yet 215 openTrackerDatabase(false);216 247 if (!m_database.isOpen()) 217 248 return String(); … … 232 263 233 264 if (result != SQLResultDone) { 234 LOG_ERROR("Failed to retrieve filename from Database Tracker for origin %s, name %s", origin ->databaseIdentifier().ascii().data(), name.ascii().data());265 LOG_ERROR("Failed to retrieve filename from Database Tracker for origin %s, name %s", originIdentifier.ascii().data(), name.ascii().data()); 235 266 return String(); 236 267 } 237 268 statement.finalize(); 238 269 239 String fileName = SQLiteFileSystem::getFileNameForNewDatabase(originPath, name, origin ->databaseIdentifier(), &m_database);270 String fileName = SQLiteFileSystem::getFileNameForNewDatabase(originPath, name, originIdentifier, &m_database); 240 271 if (!addDatabase(origin, name, fileName)) 241 272 return String(); … … 244 275 // to the quota manager now 245 276 String fullFilePath = SQLiteFileSystem::appendDatabaseFileNameToPath(originPath, fileName); 246 { 247 Locker<OriginQuotaManager> locker(originQuotaManager()); 248 if (originQuotaManager().tracksOrigin(origin)) 249 originQuotaManager().addDatabase(origin, name, fullFilePath); 250 } 277 if (originQuotaManagerNoLock().tracksOrigin(origin)) 278 originQuotaManagerNoLock().addDatabase(origin, name, fullFilePath); 251 279 252 280 return fullFilePath; 253 281 } 254 282 283 String DatabaseTracker::fullPathForDatabase(SecurityOrigin* origin, const String& name, bool createIfNotExists) 284 { 285 Locker<OriginQuotaManager> locker(originQuotaManager()); 286 MutexLocker lockDatabase(m_databaseGuard); 287 populateOrigins(); 288 289 return fullPathForDatabaseNoLock(origin, name, createIfNotExists).threadsafeCopy(); 290 } 291 255 292 void DatabaseTracker::populateOrigins() 256 293 { 294 ASSERT(!m_databaseGuard.tryLock()); 257 295 if (m_quotaMap) 258 296 return; 259 297 260 ASSERT(currentThread() == m_thread);261 262 298 m_quotaMap.set(new QuotaMap); 263 m_quotaManager.set(new OriginQuotaManager); 299 if (!m_quotaManager) 300 m_quotaManager.set(new OriginQuotaManager); 264 301 265 302 openTrackerDatabase(false); … … 269 306 SQLiteStatement statement(m_database, "SELECT origin, quota FROM Origins"); 270 307 271 if (statement.prepare() != SQLResultOk) 272 return; 308 if (statement.prepare() != SQLResultOk) { 309 LOG_ERROR("Failed to prepare statement."); 310 return; 311 } 273 312 274 313 int result; 275 314 while ((result = statement.step()) == SQLResultRow) { 276 315 RefPtr<SecurityOrigin> origin = SecurityOrigin::createFromDatabaseIdentifier(statement.getColumnText(0)); 277 m_quotaMap->set(origin.get() , statement.getColumnInt64(1));316 m_quotaMap->set(origin.get()->threadsafeCopy(), statement.getColumnInt64(1)); 278 317 } 279 318 280 319 if (result != SQLResultDone) 281 LOG_ERROR("Failed to read in all origins from the database ");320 LOG_ERROR("Failed to read in all origins from the database."); 282 321 } 283 322 284 323 void DatabaseTracker::origins(Vector<RefPtr<SecurityOrigin> >& result) 285 324 { 286 ASSERT(currentThread() == m_thread);325 MutexLocker lockDatabase(m_databaseGuard); 287 326 populateOrigins(); 288 MutexLocker lockQuotaMap(m_quotaMapGuard);327 ASSERT(m_quotaMap); 289 328 copyKeysToVector(*m_quotaMap, result); 290 329 } 291 330 292 bool DatabaseTracker::databaseNamesForOrigin (SecurityOrigin* origin, Vector<String>& resultVector)293 { 294 ASSERT( currentThread() == m_thread);331 bool DatabaseTracker::databaseNamesForOriginNoLock(SecurityOrigin* origin, Vector<String>& resultVector) 332 { 333 ASSERT(!m_databaseGuard.tryLock()); 295 334 openTrackerDatabase(false); 296 335 if (!m_database.isOpen()) … … 316 355 } 317 356 357 bool DatabaseTracker::databaseNamesForOrigin(SecurityOrigin* origin, Vector<String>& resultVector) 358 { 359 MutexLocker lockDatabase(m_databaseGuard); 360 Vector<String> temp; 361 if (!databaseNamesForOriginNoLock(origin, temp)) 362 return false; 363 364 for (Vector<String>::iterator iter = temp.begin(); iter != temp.end(); ++iter) 365 resultVector.append(iter->threadsafeCopy()); 366 return true; 367 } 368 318 369 DatabaseDetails DatabaseTracker::detailsForNameAndOrigin(const String& name, SecurityOrigin* origin) 319 370 { 320 ASSERT(currentThread() == m_thread);321 322 if (m_proposedDatabase && m_proposedDatabase->first == origin && m_proposedDatabase->second.name() == name)323 return m_proposedDatabase->second;324 325 371 String originIdentifier = origin->databaseIdentifier(); 326 327 openTrackerDatabase(false); 328 if (!m_database.isOpen()) 329 return DatabaseDetails(); 330 SQLiteStatement statement(m_database, "SELECT displayName, estimatedSize FROM Databases WHERE origin=? AND name=?"); 331 if (statement.prepare() != SQLResultOk) 332 return DatabaseDetails(); 333 334 statement.bindText(1, originIdentifier); 335 statement.bindText(2, name); 336 337 int result = statement.step(); 338 if (result == SQLResultDone) 339 return DatabaseDetails(); 340 341 if (result != SQLResultRow) { 342 LOG_ERROR("Error retrieving details for database %s in origin %s from tracker database", name.ascii().data(), originIdentifier.ascii().data()); 343 return DatabaseDetails(); 344 } 345 346 return DatabaseDetails(name, statement.getColumnText(0), statement.getColumnInt64(1), usageForDatabase(name, origin)); 372 String displayName; 373 int64_t expectedUsage; 374 375 { 376 MutexLocker lockDatabase(m_databaseGuard); 377 378 for (HashSet<ProposedDatabase*>::iterator iter = m_proposedDatabases.begin(); iter != m_proposedDatabases.end(); ++iter) 379 if ((*iter)->first == origin && (*iter)->second.name() == name) { 380 ASSERT((*iter)->second.thread() == currentThread()); 381 return (*iter)->second; 382 } 383 384 openTrackerDatabase(false); 385 if (!m_database.isOpen()) 386 return DatabaseDetails(); 387 SQLiteStatement statement(m_database, "SELECT displayName, estimatedSize FROM Databases WHERE origin=? AND name=?"); 388 if (statement.prepare() != SQLResultOk) 389 return DatabaseDetails(); 390 391 statement.bindText(1, originIdentifier); 392 statement.bindText(2, name); 393 394 int result = statement.step(); 395 if (result == SQLResultDone) 396 return DatabaseDetails(); 397 398 if (result != SQLResultRow) { 399 LOG_ERROR("Error retrieving details for database %s in origin %s from tracker database", name.ascii().data(), originIdentifier.ascii().data()); 400 return DatabaseDetails(); 401 } 402 displayName = statement.getColumnText(0); 403 expectedUsage = statement.getColumnInt64(1); 404 } 405 406 return DatabaseDetails(name, displayName, expectedUsage, usageForDatabase(name, origin)); 347 407 } 348 408 349 409 void DatabaseTracker::setDatabaseDetails(SecurityOrigin* origin, const String& name, const String& displayName, unsigned long estimatedSize) 350 410 { 351 ASSERT(currentThread() == m_thread);352 353 411 String originIdentifier = origin->databaseIdentifier(); 354 412 int64_t guid = 0; 413 414 MutexLocker lockDatabase(m_databaseGuard); 355 415 356 416 openTrackerDatabase(true); … … 401 461 unsigned long long DatabaseTracker::usageForDatabase(const String& name, SecurityOrigin* origin) 402 462 { 403 ASSERT(currentThread() == m_thread);404 463 String path = fullPathForDatabase(origin, name, false); 405 464 if (path.isEmpty()) … … 423 482 if (!nameMap) { 424 483 nameMap = new DatabaseNameMap; 425 m_openDatabaseMap->set(database->securityOrigin() , nameMap);484 m_openDatabaseMap->set(database->securityOrigin()->threadsafeCopy(), nameMap); 426 485 } 427 486 … … 429 488 if (!databaseSet) { 430 489 databaseSet = new DatabaseSet; 431 nameMap->set(name , databaseSet);490 nameMap->set(name.threadsafeCopy(), databaseSet); 432 491 } 433 492 … … 435 494 436 495 LOG(StorageAPI, "Added open Database %s (%p)\n", database->stringIdentifier().ascii().data(), database); 496 497 MutexLocker lockDatabase(m_databaseGuard); 498 doneCreatingDatabase(database->securityOrigin(), database->stringIdentifier()); 437 499 } 438 500 … … 442 504 return; 443 505 506 Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager()); 507 MutexLocker lockDatabase(m_databaseGuard); 444 508 MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard); 445 509 … … 477 541 m_openDatabaseMap->remove(database->securityOrigin()); 478 542 delete nameMap; 543 originQuotaManagerNoLock().removeOrigin(database->securityOrigin()); 479 544 } 480 545 … … 497 562 } 498 563 564 unsigned long long DatabaseTracker::usageForOriginNoLock(SecurityOrigin* origin) 565 { 566 ASSERT(!originQuotaManagerNoLock().tryLock()); 567 568 // Use the OriginQuotaManager mechanism to calculate the usage 569 if (originQuotaManagerNoLock().tracksOrigin(origin)) 570 return originQuotaManagerNoLock().diskUsage(origin); 571 572 // If the OriginQuotaManager doesn't track this origin already, prime it to do so 573 originQuotaManagerNoLock().trackOrigin(origin); 574 575 Vector<String> names; 576 databaseNamesForOriginNoLock(origin, names); 577 578 for (unsigned i = 0; i < names.size(); ++i) 579 originQuotaManagerNoLock().addDatabase(origin, names[i], fullPathForDatabaseNoLock(origin, names[i], false)); 580 581 if (!originQuotaManagerNoLock().tracksOrigin(origin)) 582 return 0; 583 return originQuotaManagerNoLock().diskUsage(origin); 584 } 585 499 586 unsigned long long DatabaseTracker::usageForOrigin(SecurityOrigin* origin) 500 587 { 501 ASSERT(currentThread() == m_thread);502 588 Locker<OriginQuotaManager> locker(originQuotaManager()); 503 589 504 // Use the OriginQuotaManager mechanism to calculate the usage 505 if (originQuotaManager().tracksOrigin(origin)) 506 return originQuotaManager().diskUsage(origin); 507 508 // If the OriginQuotaManager doesn't track this origin already, prime it to do so 509 originQuotaManager().trackOrigin(origin); 510 511 Vector<String> names; 512 databaseNamesForOrigin(origin, names); 513 514 for (unsigned i = 0; i < names.size(); ++i) 515 originQuotaManager().addDatabase(origin, names[i], fullPathForDatabase(origin, names[i], false)); 516 517 if (!originQuotaManager().tracksOrigin(origin)) 518 return 0; 519 return originQuotaManager().diskUsage(origin); 590 return usageForOriginNoLock(origin); 591 } 592 593 unsigned long long DatabaseTracker::quotaForOriginNoLock(SecurityOrigin* origin) 594 { 595 ASSERT(!m_databaseGuard.tryLock()); 596 ASSERT(m_quotaMap); 597 return m_quotaMap->get(origin); 520 598 } 521 599 522 600 unsigned long long DatabaseTracker::quotaForOrigin(SecurityOrigin* origin) 523 601 { 524 ASSERT(currentThread() == m_thread || m_quotaMap);602 MutexLocker lockDatabase(m_databaseGuard); 525 603 populateOrigins(); 526 MutexLocker lockQuotaMap(m_quotaMapGuard); 527 return m_quotaMap->get(origin); 604 return quotaForOriginNoLock(origin); 528 605 } 529 606 530 607 void DatabaseTracker::setQuota(SecurityOrigin* origin, unsigned long long quota) 531 608 { 532 ASSERT(currentThread() == m_thread); 533 if (quotaForOrigin(origin) == quota) 609 MutexLocker lockDatabase(m_databaseGuard); 610 611 populateOrigins(); 612 if (quotaForOriginNoLock(origin) == quota) 534 613 return; 535 614 … … 538 617 return; 539 618 540 { 541 MutexLocker lockQuotaMap(m_quotaMapGuard); 542 543 if (!m_quotaMap->contains(origin)) { 544 SQLiteStatement statement(m_database, "INSERT INTO Origins VALUES (?, ?)"); 545 if (statement.prepare() != SQLResultOk) { 619 if (!m_quotaMap->contains(origin)) { 620 SQLiteStatement statement(m_database, "INSERT INTO Origins VALUES (?, ?)"); 621 if (statement.prepare() != SQLResultOk) { 622 LOG_ERROR("Unable to establish origin %s in the tracker", origin->databaseIdentifier().ascii().data()); 623 } else { 624 statement.bindText(1, origin->databaseIdentifier()); 625 statement.bindInt64(2, quota); 626 627 if (statement.step() != SQLResultDone) 546 628 LOG_ERROR("Unable to establish origin %s in the tracker", origin->databaseIdentifier().ascii().data()); 547 } else { 548 statement.bindText(1, origin->databaseIdentifier()); 549 statement.bindInt64(2, quota); 550 551 if (statement.step() != SQLResultDone) 552 LOG_ERROR("Unable to establish origin %s in the tracker", origin->databaseIdentifier().ascii().data()); 553 } 554 } else { 555 SQLiteStatement statement(m_database, "UPDATE Origins SET quota=? WHERE origin=?"); 556 bool error = statement.prepare() != SQLResultOk; 557 if (!error) { 558 statement.bindInt64(1, quota); 559 statement.bindText(2, origin->databaseIdentifier()); 560 561 error = !statement.executeCommand(); 562 } 563 564 if (error) 565 LOG_ERROR("Failed to set quota %llu in tracker database for origin %s", quota, origin->databaseIdentifier().ascii().data()); 566 } 567 568 // FIXME: Is it really OK to update the quota in memory if we failed to update it on disk? 569 m_quotaMap->set(origin, quota); 570 } 629 } 630 } else { 631 SQLiteStatement statement(m_database, "UPDATE Origins SET quota=? WHERE origin=?"); 632 bool error = statement.prepare() != SQLResultOk; 633 if (!error) { 634 statement.bindInt64(1, quota); 635 statement.bindText(2, origin->databaseIdentifier()); 636 637 error = !statement.executeCommand(); 638 } 639 640 if (error) 641 LOG_ERROR("Failed to set quota %llu in tracker database for origin %s", quota, origin->databaseIdentifier().ascii().data()); 642 } 643 644 // FIXME: Is it really OK to update the quota in memory if we failed to update it on disk? 645 m_quotaMap->set(origin->threadsafeCopy(), quota); 571 646 572 647 if (m_client) … … 576 651 bool DatabaseTracker::addDatabase(SecurityOrigin* origin, const String& name, const String& path) 577 652 { 578 ASSERT(currentThread() == m_thread); 653 ASSERT(!m_databaseGuard.tryLock()); 654 ASSERT(m_quotaMap); 579 655 openTrackerDatabase(true); 580 656 if (!m_database.isOpen()) … … 582 658 583 659 // New database should never be added until the origin has been established 584 ASSERT(hasEntryForOrigin (origin));660 ASSERT(hasEntryForOriginNoLock(origin)); 585 661 586 662 SQLiteStatement statement(m_database, "INSERT INTO Databases (origin, name, path) VALUES (?, ?, ?);"); … … 606 682 void DatabaseTracker::deleteAllDatabases() 607 683 { 608 ASSERT(currentThread() == m_thread);609 610 684 Vector<RefPtr<SecurityOrigin> > originsCopy; 611 685 origins(originsCopy); … … 615 689 } 616 690 691 // It is the caller's responsibility to make sure that nobody is trying to create, delete, open, or close databases in this origin while the deletion is 692 // taking place. 617 693 void DatabaseTracker::deleteOrigin(SecurityOrigin* origin) 618 694 { 619 ASSERT(currentThread() == m_thread);620 openTrackerDatabase(false);621 if (!m_database.isOpen())622 return;623 624 695 Vector<String> databaseNames; 625 if (!databaseNamesForOrigin(origin, databaseNames)) { 626 LOG_ERROR("Unable to retrieve list of database names for origin %s", origin->databaseIdentifier().ascii().data()); 627 return; 628 } 629 696 { 697 MutexLocker lockDatabase(m_databaseGuard); 698 openTrackerDatabase(false); 699 if (!m_database.isOpen()) 700 return; 701 702 if (!databaseNamesForOriginNoLock(origin, databaseNames)) { 703 LOG_ERROR("Unable to retrieve list of database names for origin %s", origin->databaseIdentifier().ascii().data()); 704 return; 705 } 706 if (!canDeleteOrigin(origin)) { 707 LOG_ERROR("Tried to delete an origin (%s) while either creating database in it or already deleting it", origin->databaseIdentifier().ascii().data()); 708 ASSERT(false); 709 return; 710 } 711 recordDeletingOrigin(origin); 712 } 713 714 // We drop the lock here because holding locks during a call to deleteDatabaseFile will deadlock. 630 715 for (unsigned i = 0; i < databaseNames.size(); ++i) { 631 716 if (!deleteDatabaseFile(origin, databaseNames[i])) { … … 635 720 } 636 721 637 SQLiteStatement statement(m_database, "DELETE FROM Databases WHERE origin=?");638 if (statement.prepare() != SQLResultOk) {639 LOG_ERROR("Unable to prepare deletion of databases from origin %s from tracker", origin->databaseIdentifier().ascii().data());640 return;641 }642 643 statement.bindText(1, origin->databaseIdentifier());644 645 if (!statement.executeCommand()) {646 LOG_ERROR("Unable to execute deletion of databases from origin %s from tracker", origin->databaseIdentifier().ascii().data());647 return;648 }649 650 SQLiteStatement originStatement(m_database, "DELETE FROM Origins WHERE origin=?");651 if (originStatement.prepare() != SQLResultOk) {652 LOG_ERROR("Unable to prepare deletion of origin %s from tracker", origin->databaseIdentifier().ascii().data());653 return;654 }655 656 originStatement.bindText(1, origin->databaseIdentifier());657 658 if (!originStatement.executeCommand()) {659 LOG_ERROR("Unable to execute deletion of databases from origin %s from tracker", origin->databaseIdentifier().ascii().data());660 return;661 }662 663 SQLiteFileSystem::deleteEmptyDatabaseDirectory(originPath(origin));664 665 RefPtr<SecurityOrigin> originPossiblyLastReference = origin;666 722 { 667 MutexLocker lockQuotaMap(m_quotaMapGuard); 723 // To satisfy the lock hierarchy, we have to lock the originQuotaManager before m_databaseGuard if there's any chance we'll to lock both. 724 Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager()); 725 MutexLocker lockDatabase(m_databaseGuard); 726 SQLiteStatement statement(m_database, "DELETE FROM Databases WHERE origin=?"); 727 728 doneDeletingOrigin(origin); 729 730 if (statement.prepare() != SQLResultOk) { 731 LOG_ERROR("Unable to prepare deletion of databases from origin %s from tracker", origin->databaseIdentifier().ascii().data()); 732 return; 733 } 734 735 statement.bindText(1, origin->databaseIdentifier()); 736 737 if (!statement.executeCommand()) { 738 LOG_ERROR("Unable to execute deletion of databases from origin %s from tracker", origin->databaseIdentifier().ascii().data()); 739 return; 740 } 741 742 SQLiteStatement originStatement(m_database, "DELETE FROM Origins WHERE origin=?"); 743 if (originStatement.prepare() != SQLResultOk) { 744 LOG_ERROR("Unable to prepare deletion of origin %s from tracker", origin->databaseIdentifier().ascii().data()); 745 return; 746 } 747 748 originStatement.bindText(1, origin->databaseIdentifier()); 749 750 if (!originStatement.executeCommand()) { 751 LOG_ERROR("Unable to execute deletion of databases from origin %s from tracker", origin->databaseIdentifier().ascii().data()); 752 return; 753 } 754 755 SQLiteFileSystem::deleteEmptyDatabaseDirectory(originPath(origin)); 756 757 RefPtr<SecurityOrigin> originPossiblyLastReference = origin; 668 758 m_quotaMap->remove(origin); 669 759 670 Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager()); 671 originQuotaManager().removeOrigin(origin); 760 originQuotaManagerNoLock().removeOrigin(origin); 672 761 673 762 // If we removed the last origin, do some additional deletion. … … 678 767 SQLiteFileSystem::deleteEmptyDatabaseDirectory(m_databaseDirectoryPath); 679 768 } 680 } 681 682 if (m_client) { 683 m_client->dispatchDidModifyOrigin(origin); 684 for (unsigned i = 0; i < databaseNames.size(); ++i) 685 m_client->dispatchDidModifyDatabase(origin, databaseNames[i]); 686 } 769 770 if (m_client) { 771 m_client->dispatchDidModifyOrigin(origin); 772 for (unsigned i = 0; i < databaseNames.size(); ++i) 773 m_client->dispatchDidModifyDatabase(origin, databaseNames[i]); 774 } 775 } 776 } 777 778 bool DatabaseTracker::canCreateDatabase(SecurityOrigin *origin, const String& name) 779 { 780 ASSERT(!m_databaseGuard.tryLock()); 781 // Can't create a database while someone else is deleting it; there's a risk of leaving untracked database debris on the disk. 782 return !deletingDatabase(origin, name) && !deletingOrigin(origin); 783 } 784 785 void DatabaseTracker::recordCreatingDatabase(SecurityOrigin *origin, const String& name) 786 { 787 ASSERT(!m_databaseGuard.tryLock()); 788 NameCountMap* nameMap = m_beingCreated.get(origin); 789 if (!nameMap) { 790 nameMap = new NameCountMap(); 791 m_beingCreated.set(origin->threadsafeCopy(), nameMap); 792 } 793 long count = nameMap->get(name); 794 nameMap->set(name.threadsafeCopy(), count + 1); 795 } 796 797 void DatabaseTracker::doneCreatingDatabase(SecurityOrigin *origin, const String& name) 798 { 799 ASSERT(!m_databaseGuard.tryLock()); 800 NameCountMap* nameMap = m_beingCreated.get(origin); 801 if (nameMap) { 802 long count = nameMap->get(name); 803 ASSERT(count > 0); 804 if (count <= 1) { 805 nameMap->remove(name); 806 if (nameMap->isEmpty()) { 807 m_beingCreated.remove(origin); 808 delete nameMap; 809 } 810 } else 811 nameMap->set(name, count - 1); 812 } else 813 ASSERT(false); 814 } 815 816 bool DatabaseTracker::creatingDatabase(SecurityOrigin *origin, const String& name) 817 { 818 ASSERT(!m_databaseGuard.tryLock()); 819 NameCountMap* nameMap = m_beingCreated.get(origin); 820 return nameMap && nameMap->get(name); 821 } 822 823 bool DatabaseTracker::canDeleteDatabase(SecurityOrigin *origin, const String& name) 824 { 825 ASSERT(!m_databaseGuard.tryLock()); 826 return !creatingDatabase(origin, name) && !deletingDatabase(origin, name); 827 } 828 829 void DatabaseTracker::recordDeletingDatabase(SecurityOrigin *origin, const String& name) 830 { 831 ASSERT(!m_databaseGuard.tryLock()); 832 ASSERT(canDeleteDatabase(origin, name)); 833 NameSet* nameSet = m_beingDeleted.get(origin); 834 if (!nameSet) { 835 nameSet = new NameSet(); 836 m_beingDeleted.set(origin->threadsafeCopy(), nameSet); 837 } 838 ASSERT(!nameSet->contains(name)); 839 nameSet->add(name.threadsafeCopy()); 840 } 841 842 void DatabaseTracker::doneDeletingDatabase(SecurityOrigin *origin, const String& name) 843 { 844 ASSERT(!m_databaseGuard.tryLock()); 845 NameSet* nameSet = m_beingDeleted.get(origin); 846 if (nameSet) { 847 ASSERT(nameSet->contains(name)); 848 nameSet->remove(name); 849 if (nameSet->isEmpty()) { 850 m_beingDeleted.remove(origin); 851 delete nameSet; 852 } 853 } else { 854 ASSERT(false); 855 } 856 } 857 858 bool DatabaseTracker::deletingDatabase(SecurityOrigin *origin, const String& name) 859 { 860 ASSERT(!m_databaseGuard.tryLock()); 861 NameSet* nameSet = m_beingDeleted.get(origin); 862 return nameSet && nameSet->contains(name); 863 } 864 865 bool DatabaseTracker::canDeleteOrigin(SecurityOrigin *origin) 866 { 867 ASSERT(!m_databaseGuard.tryLock()); 868 return !(deletingOrigin(origin) || m_beingCreated.get(origin)); 869 } 870 871 bool DatabaseTracker::deletingOrigin(SecurityOrigin *origin) 872 { 873 ASSERT(!m_databaseGuard.tryLock()); 874 return m_originsBeingDeleted.contains(origin); 875 } 876 877 void DatabaseTracker::recordDeletingOrigin(SecurityOrigin *origin) 878 { 879 ASSERT(!m_databaseGuard.tryLock()); 880 ASSERT(!deletingOrigin(origin)); 881 m_originsBeingDeleted.add(origin->threadsafeCopy()); 882 } 883 884 void DatabaseTracker::doneDeletingOrigin(SecurityOrigin *origin) 885 { 886 ASSERT(!m_databaseGuard.tryLock()); 887 ASSERT(deletingOrigin(origin)); 888 m_originsBeingDeleted.remove(origin); 687 889 } 688 890 689 891 void DatabaseTracker::deleteDatabase(SecurityOrigin* origin, const String& name) 690 892 { 691 ASSERT(currentThread() == m_thread); 692 openTrackerDatabase(false); 693 if (!m_database.isOpen()) 694 return; 695 893 { 894 MutexLocker lockDatabase(m_databaseGuard); 895 openTrackerDatabase(false); 896 if (!m_database.isOpen()) 897 return; 898 899 if (!canDeleteDatabase(origin, name)) { 900 ASSERT(FALSE); 901 return; 902 } 903 recordDeletingDatabase(origin, name); 904 } 905 906 // We drop the lock here because holding locks during a call to deleteDatabaseFile will deadlock. 696 907 if (!deleteDatabaseFile(origin, name)) { 697 908 LOG_ERROR("Unable to delete file for database %s in origin %s", name.ascii().data(), origin->databaseIdentifier().ascii().data()); 698 return; 699 } 909 MutexLocker lockDatabase(m_databaseGuard); 910 doneDeletingDatabase(origin, name); 911 return; 912 } 913 914 // To satisfy the lock hierarchy, we have to lock the originQuotaManager before m_databaseGuard if there's any chance we'll to lock both. 915 Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager()); 916 MutexLocker lockDatabase(m_databaseGuard); 700 917 701 918 SQLiteStatement statement(m_database, "DELETE FROM Databases WHERE origin=? AND name=?"); 702 919 if (statement.prepare() != SQLResultOk) { 703 920 LOG_ERROR("Unable to prepare deletion of database %s from origin %s from tracker", name.ascii().data(), origin->databaseIdentifier().ascii().data()); 921 doneDeletingDatabase(origin, name); 704 922 return; 705 923 } … … 710 928 if (!statement.executeCommand()) { 711 929 LOG_ERROR("Unable to execute deletion of database %s from origin %s from tracker", name.ascii().data(), origin->databaseIdentifier().ascii().data()); 712 return; 713 } 714 715 { 716 Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager()); 717 originQuotaManager().removeDatabase(origin, name); 718 } 930 doneDeletingDatabase(origin, name); 931 return; 932 } 933 934 originQuotaManagerNoLock().removeDatabase(origin, name); 719 935 720 936 if (m_client) { … … 722 938 m_client->dispatchDidModifyDatabase(origin, name); 723 939 } 724 } 725 940 doneDeletingDatabase(origin, name); 941 } 942 943 // deleteDatabaseFile has to release locks between looking up the list of databases to close and closing them. While this is in progress, the caller 944 // is responsible for making sure no new databases are opened in the file to be deleted. 726 945 bool DatabaseTracker::deleteDatabaseFile(SecurityOrigin* origin, const String& name) 727 946 { 728 ASSERT(currentThread() == m_thread);729 947 String fullPath = fullPathForDatabase(origin, name, false); 730 948 if (fullPath.isEmpty()) 731 949 return true; 732 950 951 #ifndef NDEBUG 952 { 953 MutexLocker lockDatabase(m_databaseGuard); 954 ASSERT(deletingDatabase(origin, name) || deletingOrigin(origin)); 955 } 956 #endif 957 733 958 Vector<RefPtr<Database> > deletedDatabases; 734 959 735 // Make sure not to hold the m_openDatabaseMapGuard mutexwhen calling960 // Make sure not to hold the any locks when calling 736 961 // Database::markAsDeletedAndClose(), since that can cause a deadlock 737 962 // during the synchronous DatabaseThread call it triggers. 738 739 963 { 740 964 MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard); … … 743 967 DatabaseNameMap* nameMap = m_openDatabaseMap->get(origin); 744 968 if (nameMap && nameMap->size()) { 745 // There are some open databases for this origin, let s check969 // There are some open databases for this origin, let's check 746 970 // if they are this database by name. 747 971 DatabaseSet* databaseSet = nameMap->get(name); … … 764 988 void DatabaseTracker::setClient(DatabaseTrackerClient* client) 765 989 { 766 ASSERT(currentThread() == m_thread);767 990 m_client = client; 768 991 } … … 774 997 } 775 998 776 typedef Vector<pair< SecurityOrigin*, String> > NotificationQueue;999 typedef Vector<pair<RefPtr<SecurityOrigin>, String> > NotificationQueue; 777 1000 778 1001 static NotificationQueue& notificationQueue() … … 786 1009 MutexLocker locker(notificationMutex()); 787 1010 788 notificationQueue().append(pair< SecurityOrigin*, String>(origin, name.crossThreadString()));1011 notificationQueue().append(pair<RefPtr<SecurityOrigin>, String>(origin->threadsafeCopy(), name.crossThreadString())); 789 1012 scheduleForNotification(); 790 1013 } … … 821 1044 822 1045 for (unsigned i = 0; i < notifications.size(); ++i) 823 theTracker.m_client->dispatchDidModifyDatabase(notifications[i].first , notifications[i].second);1046 theTracker.m_client->dispatchDidModifyDatabase(notifications[i].first.get(), notifications[i].second); 824 1047 } 825 1048 -
trunk/WebCore/storage/DatabaseTracker.h
r54506 r56293 61 61 public: 62 62 static DatabaseTracker& tracker(); 63 // FIXME: Due to workers having multiple threads in a single process sharing 64 // a DatabaseTracker, this singleton will have to be synchronized or moved 65 // to TLS. 63 // This singleton will potentially be used from multiple worker threads and the page's context thread simultaneously. To keep this safe, it's 64 // currently using 4 locks. In order to avoid deadlock when taking multiple locks, you must take them in the correct order: 65 // originQuotaManager() before m_databaseGuard or m_openDatabaseMapGuard 66 // m_databaseGuard before m_openDatabaseMapGuard 67 // notificationMutex() is currently independent of the other locks. 66 68 67 69 bool canEstablishDatabase(ScriptExecutionContext*, const String& name, const String& displayName, unsigned long estimatedSize); … … 88 90 public: 89 91 void setDatabaseDirectoryPath(const String&); 90 const String&databaseDirectoryPath() const;92 String databaseDirectoryPath() const; 91 93 92 94 void origins(Vector<RefPtr<SecurityOrigin> >& result); … … 115 117 116 118 private: 119 OriginQuotaManager& originQuotaManagerNoLock(); 120 bool hasEntryForOriginNoLock(SecurityOrigin* origin); 121 String fullPathForDatabaseNoLock(SecurityOrigin*, const String& name, bool createIfDoesNotExist); 122 bool databaseNamesForOriginNoLock(SecurityOrigin* origin, Vector<String>& resultVector); 123 unsigned long long usageForOriginNoLock(SecurityOrigin* origin); 124 unsigned long long quotaForOriginNoLock(SecurityOrigin* origin); 125 117 126 String trackerDatabasePath() const; 118 127 void openTrackerDatabase(bool createIfDoesNotExist); … … 127 136 bool deleteDatabaseFile(SecurityOrigin*, const String& name); 128 137 138 // This lock protects m_database, m_quotaMap, m_proposedDatabases, m_databaseDirectoryPath, m_originsBeingDeleted, m_beingCreated, and m_beingDeleted. 139 Mutex m_databaseGuard; 129 140 SQLiteDatabase m_database; 130 141 131 142 typedef HashMap<RefPtr<SecurityOrigin>, unsigned long long, SecurityOriginHash> QuotaMap; 132 Mutex m_quotaMapGuard;133 143 mutable OwnPtr<QuotaMap> m_quotaMap; 134 144 … … 139 149 DatabaseTrackerClient* m_client; 140 150 141 std::pair<SecurityOrigin*, DatabaseDetails>* m_proposedDatabase; 151 typedef std::pair<RefPtr<SecurityOrigin>, DatabaseDetails> ProposedDatabase; 152 HashSet<ProposedDatabase*> m_proposedDatabases; 142 153 143 #ifndef NDEBUG 144 ThreadIdentifier m_thread; 145 #endif 154 typedef HashMap<String, long> NameCountMap; 155 typedef HashMap<RefPtr<SecurityOrigin>, NameCountMap*, SecurityOriginHash> CreateSet; 156 CreateSet m_beingCreated; 157 typedef HashSet<String> NameSet; 158 HashMap<RefPtr<SecurityOrigin>, NameSet*, SecurityOriginHash> m_beingDeleted; 159 HashSet<RefPtr<SecurityOrigin>, SecurityOriginHash> m_originsBeingDeleted; 160 bool canCreateDatabase(SecurityOrigin *origin, const String& name); 161 void recordCreatingDatabase(SecurityOrigin *origin, const String& name); 162 void doneCreatingDatabase(SecurityOrigin *origin, const String& name); 163 bool creatingDatabase(SecurityOrigin *origin, const String& name); 164 bool canDeleteDatabase(SecurityOrigin *origin, const String& name); 165 void recordDeletingDatabase(SecurityOrigin *origin, const String& name); 166 void doneDeletingDatabase(SecurityOrigin *origin, const String& name); 167 bool deletingDatabase(SecurityOrigin *origin, const String& name); 168 bool canDeleteOrigin(SecurityOrigin *origin); 169 bool deletingOrigin(SecurityOrigin *origin); 170 void recordDeletingOrigin(SecurityOrigin *origin); 171 void doneDeletingOrigin(SecurityOrigin *origin); 146 172 147 173 static void scheduleForNotification(); -
trunk/WebCore/storage/OriginQuotaManager.cpp
r50169 r56293 43 43 } 44 44 45 bool OriginQuotaManager::tryLock() 46 { 47 bool locked = m_usageRecordGuard.tryLock(); 48 #ifndef NDEBUG 49 if (locked) 50 m_usageRecordGuardLocked = true; 51 else 52 ASSERT(m_usageRecordGuardLocked); 53 #endif 54 return locked; 55 } 56 45 57 void OriginQuotaManager::lock() 46 58 { … … 64 76 ASSERT(!m_usageMap.contains(origin.get())); 65 77 66 m_usageMap.set(origin , new OriginUsageRecord);78 m_usageMap.set(origin->threadsafeCopy(), new OriginUsageRecord); 67 79 } 68 80 -
trunk/WebCore/storage/OriginQuotaManager.h
r43283 r56293 46 46 OriginQuotaManager(); 47 47 48 bool tryLock(); 48 49 void lock(); 49 50 void unlock();
Note: See TracChangeset
for help on using the changeset viewer.