Changeset 30172 in webkit
- Timestamp:
- Feb 12, 2008 11:37:03 AM (16 years ago)
- Location:
- trunk/WebCore
- Files:
-
- 3 added
- 13 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/WebCore/ChangeLog
r30171 r30172 1 2008-02-12 Alexey Proskuryakov <ap@webkit.org> and Brady Eidson <beidson@apple.com> 2 3 Reviewed by Brady. 4 5 http://bugs.webkit.org/show_bug.cgi?id=17177 6 <rdar://problem/5729619> Storage tasks are getting lost 7 8 <rdar://problem/5729445> REGRESSION: Cannot schedule more than one transaction at a time 9 10 <rdar://problem/5729446> Major thread safety issue in Database code 11 12 * platform/MessageQueue.h: Added a thread-safe queue abstraction. 13 14 * WebCore.vcproj/WebCore.vcproj: 15 * WebCore.xcodeproj/project.pbxproj: 16 Added MessageQueue.h. 17 18 * dom/Document.cpp: 19 (WebCore::Document::~Document): Fixed a race condition resulting in a hanging reference. 20 21 * storage/Database.idl: Fixed parameter declarations to actually match implementation 22 (which is custom, so it got out of sync). 23 24 * storage/DatabaseTask.h: 25 (WebCore::DatabaseTask::database): 26 (WebCore::DatabaseTransactionTask::transaction): 27 Changed tasks to hold more information internally. Added helpers for better debug logging. 28 29 * storage/DatabaseTask.cpp: 30 (WebCore::DatabaseTask::DatabaseTask): 31 (WebCore::DatabaseTask::performTask): 32 (WebCore::DatabaseOpenTask::DatabaseOpenTask): 33 (WebCore::DatabaseOpenTask::doPerformTask): 34 (WebCore::DatabaseOpenTask::debugTaskName): 35 (WebCore::DatabaseTransactionTask::DatabaseTransactionTask): 36 (WebCore::DatabaseTransactionTask::~DatabaseTransactionTask): 37 (WebCore::DatabaseTransactionTask::doPerformTask): 38 (WebCore::DatabaseTransactionTask::debugTaskName): 39 (WebCore::DatabaseTableNamesTask::DatabaseTableNamesTask): 40 (WebCore::DatabaseTableNamesTask::doPerformTask): 41 (WebCore::DatabaseTableNamesTask::debugTaskName): 42 Implementation for the above. 43 44 (WebCore::DatabaseTask::lockForSynchronousScheduling): 45 (WebCore::DatabaseTask::waitForSynchronousCompletion): 46 Fixed a potential race condition: if the task completed before we entered a wait, we'd never 47 wake up. There was an assertion guarding against this, but no actual guarantee that I could see. 48 49 * storage/DatabaseThread.cpp: 50 (WebCore::DatabaseThread::DatabaseThread): 51 (WebCore::DatabaseThread::requestTermination): 52 (WebCore::DatabaseThread::databaseThread): 53 (WebCore::DatabaseThread::scheduleTask): 54 (WebCore::DatabaseThread::scheduleImmediateTask): 55 (WebCore::DatabaseThread::unscheduleDatabaseTasks): 56 * storage/DatabaseThread.h: 57 Changed to use MessageQueue. 58 59 * storage/Database.cpp: 60 (WebCore::guidMutex): 61 (WebCore::guidToVersionMap): 62 (WebCore::guidToDatabaseMap): 63 (WebCore::Database::openDatabase): 64 (WebCore::Database::Database): 65 (WebCore::Database::~Database): 66 (WebCore::Database::openAndVerifyVersion): 67 (WebCore::guidForOriginAndName): 68 (WebCore::Database::changeVersion): 69 (WebCore::Database::transaction): 70 (WebCore::Database::scheduleTransaction): 71 (WebCore::Database::scheduleTransactionStep): 72 (WebCore::Database::scheduleTransactionCallback): 73 (WebCore::Database::version): 74 (WebCore::Database::deliverPendingCallback): 75 (WebCore::Database::tableNames): 76 * storage/Database.h: 77 Changed m_transactionQueue to a MessageQueue. 78 Got rid of callback tracking - these can take care of themselves. 79 Got rid of a DatabaseThread member, as the Document can be asked for it. 80 Moved private static members and helpers out of the header. 81 Lost CurrentThreadSetter debug helper on the way. We may need to re-add something like that later. 82 83 * storage/SQLTransaction.h: 84 * storage/SQLTransaction.cpp: Added a lot of debug logging. 85 (WebCore::SQLTransaction::scheduleToRunStatements): Removed "m_currentStatement = 0" assignment, 86 as it created a race condition. Everything seems to work better without it, although a real fix 87 would be to get rid of this variable - it's evil shared data that isn't even protected in any way. 88 89 * manual-tests/database-threading-stress-test-2.html: Added. 90 * manual-tests/database-threading-stress-test.html: Added. 91 1 92 2008-02-12 Adam Roben <aroben@apple.com> 2 93 -
trunk/WebCore/WebCore.vcproj/WebCore.vcproj
r30059 r30172 3240 3240 </File> 3241 3241 <File 3242 RelativePath="..\platform\MessageQueue.h" 3243 > 3244 </File> 3245 <File 3242 3246 RelativePath="..\platform\MIMETypeRegistry.cpp" 3243 3247 > -
trunk/WebCore/WebCore.xcodeproj/project.pbxproj
r30168 r30172 3718 3718 E1BE512D0CF6C512002EA959 /* XSLTUnicodeSort.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E1BE512B0CF6C512002EA959 /* XSLTUnicodeSort.cpp */; }; 3719 3719 E1BE512E0CF6C512002EA959 /* XSLTUnicodeSort.h in Headers */ = {isa = PBXBuildFile; fileRef = E1BE512C0CF6C512002EA959 /* XSLTUnicodeSort.h */; }; 3720 E1DE1C080D5CE4CF0034C38F /* MessageQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = E1DE1C070D5CE4CF0034C38F /* MessageQueue.h */; }; 3720 3721 E1E6EEA40B628DA8005F2F70 /* JSHTMLSelectElement.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E1E6EEA30B628DA8005F2F70 /* JSHTMLSelectElement.cpp */; }; 3721 3722 E1E6EEA80B628DB3005F2F70 /* JSHTMLSelectElement.h in Headers */ = {isa = PBXBuildFile; fileRef = E1E6EEA70B628DB3005F2F70 /* JSHTMLSelectElement.h */; }; … … 7817 7818 E1BE512B0CF6C512002EA959 /* XSLTUnicodeSort.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = XSLTUnicodeSort.cpp; sourceTree = "<group>"; }; 7818 7819 E1BE512C0CF6C512002EA959 /* XSLTUnicodeSort.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XSLTUnicodeSort.h; sourceTree = "<group>"; }; 7820 E1DE1C070D5CE4CF0034C38F /* MessageQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MessageQueue.h; sourceTree = "<group>"; }; 7819 7821 E1E6EEA30B628DA8005F2F70 /* JSHTMLSelectElement.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSHTMLSelectElement.cpp; sourceTree = "<group>"; }; 7820 7822 E1E6EEA70B628DB3005F2F70 /* JSHTMLSelectElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = JSHTMLSelectElement.h; sourceTree = "<group>"; }; … … 11732 11734 A8239DFE09B3CF8A00B60641 /* Logging.cpp */, 11733 11735 A8239DFF09B3CF8A00B60641 /* Logging.h */, 11736 E1DE1C070D5CE4CF0034C38F /* MessageQueue.h */, 11734 11737 BC772C4C0C4EB3040083285F /* MIMETypeRegistry.cpp */, 11735 11738 BC772C4D0C4EB3040083285F /* MIMETypeRegistry.h */, … … 14228 14231 5DCF836D0D59159800953BC6 /* PluginInfoStore.h in Headers */, 14229 14232 C0294DF40D5A6FD800CC7D6B /* UserStyleSheetLoader.h in Headers */, 14233 E1DE1C080D5CE4CF0034C38F /* MessageQueue.h in Headers */, 14230 14234 ); 14231 14235 runOnlyForDeploymentPostprocessing = 0; -
trunk/WebCore/dom/Document.cpp
r30147 r30172 445 445 #if ENABLE(DATABASE) 446 446 if (m_databaseThread) { 447 m_databaseThread->documentGoingAway();447 RefPtr<DatabaseThread> databaseThread = m_databaseThread; 448 448 m_databaseThread = 0; 449 databaseThread->requestTermination(); 449 450 } 450 451 #endif -
trunk/WebCore/storage/Database.cpp
r30104 r30172 50 50 51 51 namespace WebCore { 52 53 #ifndef NDEBUG 54 class CurrentThreadSetter { 55 public: 56 CurrentThreadSetter(ThreadIdentifier& thread) 57 : threadIdentifierStorage(thread) 58 { 59 ASSERT(!thread); 60 thread = currentThread(); 61 } 62 63 ~CurrentThreadSetter() 64 { 65 ASSERT(threadIdentifierStorage == currentThread()); 66 threadIdentifierStorage = 0; 67 } 68 69 private: 70 ThreadIdentifier& threadIdentifierStorage; 71 }; 72 #endif 73 74 Mutex& Database::globalCallbackMutex() 75 { 52 53 static Mutex& guidMutex() 54 { 55 // FIXME: this is not a thread-safe way to initialize a shared global. 76 56 static Mutex mutex; 77 57 return mutex; 78 58 } 79 59 80 HashSet<RefPtr<Database> >& Database::globalCallbackSet() 81 { 82 static HashSet<RefPtr<Database> > set; 83 return set; 84 } 85 86 bool Database::s_globalCallbackScheduled = false; 87 88 Mutex& Database::guidMutex() 89 { 90 static Mutex mutex; 91 return mutex; 92 } 93 94 HashMap<int, String>& Database::guidToVersionMap() 60 static HashMap<int, String>& guidToVersionMap() 95 61 { 96 62 static HashMap<int, String> map; … … 98 64 } 99 65 100 HashMap<int, HashSet<Database*>*>& Database::guidToDatabaseMap()66 static HashMap<int, HashSet<Database*>*>& guidToDatabaseMap() 101 67 { 102 68 static HashMap<int, HashSet<Database*>*> map; … … 116 82 } 117 83 84 static int guidForOriginAndName(const String& origin, const String& name); 85 118 86 PassRefPtr<Database> Database::openDatabase(Document* document, const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize, ExceptionCode& e) 119 87 { 120 88 if (!DatabaseTracker::tracker().canEstablishDatabase(document, name, displayName, estimatedSize)) { 121 // There should be an exception raised here in addition to returning a null Database object. The question has been raised with the WHATWG89 // FIXME: There should be an exception raised here in addition to returning a null Database object. The question has been raised with the WHATWG. 122 90 LOG(StorageAPI, "Database %s for origin %s not allowed to be established", name.ascii().data(), document->securityOrigin()->toString().ascii().data()); 123 91 return 0; … … 142 110 143 111 Database::Database(Document* document, const String& name, const String& expectedVersion) 144 : m_document(document) 112 : m_transactionInProgress(false) 113 , m_document(document) 145 114 , m_name(name.copy()) 146 115 , m_guid(0) 147 116 , m_expectedVersion(expectedVersion) 148 117 , m_deleted(0) 149 , m_databaseThread(0)150 #ifndef NDEBUG151 , m_transactionStepThread(0)152 #endif153 118 { 154 119 ASSERT(document); … … 174 139 } 175 140 176 m_databaseThread = document->databaseThread(); 177 ASSERT(m_databaseThread); 141 ASSERT(m_document->databaseThread()); 178 142 179 143 m_filename = DatabaseTracker::tracker().fullPathForDatabase(m_securityOrigin.get(), m_name); … … 184 148 Database::~Database() 185 149 { 186 MutexLocker locker(guidMutex()); 187 188 HashSet<Database*>* hashSet = guidToDatabaseMap().get(m_guid); 189 ASSERT(hashSet); 190 ASSERT(hashSet->contains(this)); 191 hashSet->remove(this); 192 if (hashSet->isEmpty()) { 193 guidToDatabaseMap().remove(m_guid); 194 delete hashSet; 195 guidToVersionMap().remove(m_guid); 196 } 150 { 151 MutexLocker locker(guidMutex()); 152 153 HashSet<Database*>* hashSet = guidToDatabaseMap().get(m_guid); 154 ASSERT(hashSet); 155 ASSERT(hashSet->contains(this)); 156 hashSet->remove(this); 157 if (hashSet->isEmpty()) { 158 guidToDatabaseMap().remove(m_guid); 159 delete hashSet; 160 guidToVersionMap().remove(m_guid); 161 } 162 } 163 164 if (m_document->databaseThread()) 165 m_document->databaseThread()->unscheduleDatabaseTasks(this); 197 166 198 167 DatabaseTracker::tracker().removeOpenDatabase(this); … … 203 172 m_databaseAuthorizer = new DatabaseAuthorizer(); 204 173 205 RefPtr<DatabaseOpenTask> task = new DatabaseOpenTask( );174 RefPtr<DatabaseOpenTask> task = new DatabaseOpenTask(this); 206 175 207 176 task->lockForSynchronousScheduling(); 208 m_d atabaseThread->scheduleImmediateTask(this,task.get());177 m_document->databaseThread()->scheduleImmediateTask(task.get()); 209 178 task->waitForSynchronousCompletion(); 210 179 … … 326 295 } 327 296 328 void Database::databaseThreadGoingAway()329 {330 // FIXME: We might not need this anymore331 332 LOG(StorageAPI, "Database thread is going away for database %p\n", this);333 }334 335 297 void Database::disableAuthorizer() 336 298 { … … 345 307 } 346 308 347 int Database::guidForOriginAndName(const String& origin, const String& name)309 static int guidForOriginAndName(const String& origin, const String& name) 348 310 { 349 311 static int currentNewGUID = 1; … … 459 421 } 460 422 461 void Database::performTransactionStep()462 {463 #ifndef NDEBUG464 CurrentThreadSetter threadSetter(m_transactionStepThread);465 #endif466 467 // Do not perform a transaction if a global callback is scheduled.468 MutexLocker locker(globalCallbackMutex());469 if (s_globalCallbackScheduled)470 return;471 472 {473 MutexLocker locker(m_transactionMutex);474 475 if (!m_currentTransaction) {476 ASSERT(m_transactionQueue.size());477 m_currentTransaction = m_transactionQueue.first();478 m_transactionQueue.removeFirst();479 }480 }481 482 // If this step completes the entire transaction, clear the working transaction483 // and schedule the next one if necessary484 if (!m_currentTransaction->performNextStep())485 return;486 487 {488 MutexLocker locker(m_transactionMutex);489 ASSERT(!m_sqliteDatabase.transactionInProgress());490 m_currentTransaction = 0;491 492 if (m_transactionQueue.size())493 m_databaseThread->scheduleTask(this, new DatabaseTransactionTask);494 }495 }496 497 423 void Database::changeVersion(const String& oldVersion, const String& newVersion, 498 424 PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, 499 425 PassRefPtr<VoidCallback> successCallback) 500 426 { 501 scheduleTransaction(new SQLTransaction(this, callback, errorCallback, new ChangeVersionWrapper(oldVersion, newVersion))); 427 // FIXME: pass successCallback, too. 428 m_transactionQueue.append(new SQLTransaction(this, callback, errorCallback, new ChangeVersionWrapper(oldVersion, newVersion))); 429 MutexLocker locker(m_transactionInProgressMutex); 430 if (!m_transactionInProgress) 431 scheduleTransaction(); 502 432 } 503 433 … … 505 435 PassRefPtr<VoidCallback> successCallback) 506 436 { 507 scheduleTransaction(new SQLTransaction(this, callback, errorCallback, 0)); 508 } 509 510 void Database::scheduleTransaction(PassRefPtr<SQLTransaction> transaction) 511 { 512 MutexLocker locker(m_transactionMutex); 513 m_transactionQueue.append(transaction); 514 if (!m_currentTransaction) 515 m_databaseThread->scheduleTask(this, new DatabaseTransactionTask); 516 } 517 518 void Database::scheduleTransactionStep() 519 { 520 MutexLocker locker(m_transactionMutex); 521 m_databaseThread->scheduleTask(this, new DatabaseTransactionTask); 437 // FIXME: pass successCallback, too. 438 m_transactionQueue.append(new SQLTransaction(this, callback, errorCallback, 0)); 439 MutexLocker locker(m_transactionInProgressMutex); 440 if (!m_transactionInProgress) 441 scheduleTransaction(); 442 } 443 444 void Database::scheduleTransaction() 445 { 446 ASSERT(!m_transactionInProgressMutex.tryLock()); // Locked by caller. 447 RefPtr<SQLTransaction> transaction; 448 if (m_transactionQueue.tryGetMessage(transaction) && m_document->databaseThread()) { 449 DatabaseTransactionTask* task = new DatabaseTransactionTask(transaction); 450 LOG(StorageAPI, "Scheduling DatabaseTransactionTask %p for transaction %p\n", task, task->transaction()); 451 m_transactionInProgress = true; 452 m_document->databaseThread()->scheduleTask(task); 453 } else 454 m_transactionInProgress = false; 455 } 456 457 void Database::scheduleTransactionStep(SQLTransaction* transaction) 458 { 459 if (m_document->databaseThread()) { 460 DatabaseTransactionTask* task = new DatabaseTransactionTask(transaction); 461 LOG(StorageAPI, "Scheduling DatabaseTransactionTask %p for the transaction step\n", task); 462 m_document->databaseThread()->scheduleTask(task); 463 } 522 464 } 523 465 524 466 void Database::scheduleTransactionCallback(SQLTransaction* transaction) 525 467 { 526 ASSERT(m_transactionStepThread == currentThread()); 527 528 MutexLocker locker(m_callbackMutex); 529 530 ASSERT(!m_transactionPendingCallback); 531 m_transactionPendingCallback = transaction; 532 533 globalCallbackSet().add(this); 534 535 if (!s_globalCallbackScheduled) { 536 callOnMainThread(deliverAllPendingCallbacks, 0); 537 s_globalCallbackScheduled = true; 538 } 468 transaction->ref(); 469 callOnMainThread(deliverPendingCallback, transaction); 539 470 } 540 471 … … 575 506 return guidToVersionMap().get(m_guid).copy(); 576 507 } 577 578 void Database::deliverAllPendingCallbacks(void*) 579 { 580 Vector<RefPtr<Database> > databases; 581 { 582 MutexLocker locker(globalCallbackMutex()); 583 copyToVector(globalCallbackSet(), databases); 584 globalCallbackSet().clear(); 585 s_globalCallbackScheduled = false; 586 } 587 588 LOG(StorageAPI, "Having %zu databases deliver their pending callbacks", databases.size()); 589 for (unsigned i = 0; i < databases.size(); ++i) 590 databases[i]->deliverPendingCallback(); 591 } 592 593 void Database::deliverPendingCallback() 594 { 595 RefPtr<SQLTransaction> transaction; 596 { 597 MutexLocker locker(m_callbackMutex); 598 599 ASSERT(m_transactionPendingCallback); 600 transaction = m_transactionPendingCallback.release(); 601 } 602 508 509 void Database::deliverPendingCallback(void* context) 510 { 511 SQLTransaction* transaction = static_cast<SQLTransaction*>(context); 603 512 transaction->performPendingCallback(); 513 transaction->deref(); // Was ref'd in scheduleTransactionCallback(). 604 514 } 605 515 606 516 Vector<String> Database::tableNames() 607 517 { 608 RefPtr<DatabaseTableNamesTask> task = new DatabaseTableNamesTask( );518 RefPtr<DatabaseTableNamesTask> task = new DatabaseTableNamesTask(this); 609 519 610 520 task->lockForSynchronousScheduling(); 611 m_d atabaseThread->scheduleImmediateTask(this,task.get());521 m_document->databaseThread()->scheduleImmediateTask(task.get()); 612 522 task->waitForSynchronousCompletion(); 613 523 -
trunk/WebCore/storage/Database.h
r30104 r30172 30 30 #define Database_h 31 31 32 #include "MessageQueue.h" 32 33 #include "PlatformString.h" 33 34 #include "SecurityOrigin.h" … … 35 36 #include "SQLTransaction.h" 36 37 #include "StringHash.h" 37 #include "Threading.h"38 38 #include "Timer.h" 39 39 #include "VoidCallback.h" … … 59 59 60 60 class Database : public ThreadSafeShared<Database> { 61 friend class DatabaseTransactionTask; 61 62 friend class SQLStatement; 62 63 friend class SQLTransaction; … … 74 75 75 76 // Internal engine support 76 void databaseThreadGoingAway();77 77 static const String& databaseInfoTableName(); 78 78 … … 103 103 bool performOpenAndVerify(ExceptionCode&); 104 104 105 void performTransactionStep();106 107 105 Vector<String> performGetTableNames(); 108 106 … … 112 110 bool openAndVerifyVersion(ExceptionCode&); 113 111 114 void scheduleTransaction( PassRefPtr<SQLTransaction>);112 void scheduleTransaction(); 115 113 void scheduleTransactionCallback(SQLTransaction*); 116 void scheduleTransactionStep( );114 void scheduleTransactionStep(SQLTransaction* transaction); 117 115 118 M utex m_transactionMutex;119 Deque<RefPtr<SQLTransaction> > m_transactionQueue;120 RefPtr<SQLTransaction> m_currentTransaction;116 MessageQueue<RefPtr<SQLTransaction> > m_transactionQueue; 117 Mutex m_transactionInProgressMutex; 118 bool m_transactionInProgress; 121 119 122 static void deliverAllPendingCallbacks(void*); 123 void deliverPendingCallback(); 120 static void deliverPendingCallback(void*); 124 121 125 122 Document* m_document; … … 135 132 RefPtr<DatabaseAuthorizer> m_databaseAuthorizer; 136 133 137 DatabaseThread* m_databaseThread;138 139 Mutex m_callbackMutex;140 RefPtr<SQLTransaction> m_transactionPendingCallback;141 142 134 #ifndef NDEBUG 143 ThreadIdentifier m_transactionStepThread;144 145 135 String databaseDebugName() const { return m_securityOrigin->toString() + "::" + m_name; } 146 136 #endif 147 148 static Mutex& globalCallbackMutex();149 static HashSet<RefPtr<Database> >& globalCallbackSet();150 static bool s_globalCallbackScheduled;151 152 static int guidForOriginAndName(const String&, const String&);153 154 static Mutex& guidMutex();155 static HashMap<int, String>& guidToVersionMap();156 static HashMap<int, HashSet<Database*>*>& guidToDatabaseMap();157 137 }; 158 138 -
trunk/WebCore/storage/Database.idl
r29672 r30172 31 31 interface Database { 32 32 readonly attribute DOMString version; 33 [Custom] void changeVersion(in DOMString oldVersion, in DOMString newVersion, in SQLTransactionCallback callback, in SQLTransactionErrorCallback errorCallback );34 [Custom] void transaction(in SQLTransactionCallback callback, in SQLTransactionErrorCallback errorCallback );33 [Custom] void changeVersion(in DOMString oldVersion, in DOMString newVersion, in SQLTransactionCallback callback, in SQLTransactionErrorCallback errorCallback, in VoidCallback successCallback); 34 [Custom] void transaction(in SQLTransactionCallback callback, in SQLTransactionErrorCallback errorCallback, in VoidCallback successCallback); 35 35 }; 36 36 -
trunk/WebCore/storage/DatabaseTask.cpp
r29663 r30172 1 1 /* 2 * Copyright (C) 2007 Apple Inc. All rights reserved.2 * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. 3 3 * 4 4 * Redistribution and use in source and binary forms, with or without … … 31 31 #include "Database.h" 32 32 #include "Logging.h" 33 #include "SQLValue.h"34 33 35 34 namespace WebCore { 36 35 37 DatabaseTask::DatabaseTask() 38 : m_complete(false) 36 DatabaseTask::DatabaseTask(Database* database) 37 : m_database(database) 38 , m_complete(false) 39 39 { 40 40 } … … 44 44 } 45 45 46 void DatabaseTask::performTask( Database* db)46 void DatabaseTask::performTask() 47 47 { 48 // Database tasks are meant to be used only once, so make sure this one hasn't been performed before 48 // Database tasks are meant to be used only once, so make sure this one hasn't been performed before. 49 49 ASSERT(!m_complete); 50 50 51 LOG(StorageAPI, "Performing DatabaseTask %p\n", this); 51 LOG(StorageAPI, "Performing %s %p\n", debugTaskName(), this); 52 53 m_database->resetAuthorizer(); 54 doPerformTask(); 55 m_database->performPolicyChecks(); 52 56 53 57 if (m_synchronousMutex) 54 58 m_synchronousMutex->lock(); 55 56 57 db->resetAuthorizer();58 doPerformTask(db);59 db->performPolicyChecks();60 59 61 60 m_complete = true; 62 61 63 62 if (m_synchronousMutex) { 64 ASSERT(m_synchronousCondition);65 63 m_synchronousCondition->signal(); 66 64 m_synchronousMutex->unlock(); 67 65 } 68 69 66 } 70 67 71 68 void DatabaseTask::lockForSynchronousScheduling() 72 69 { 70 // Called from main thread. 73 71 ASSERT(!m_synchronousMutex); 72 ASSERT(!m_synchronousCondition); 74 73 m_synchronousMutex.set(new Mutex); 75 m_synchronous Mutex->lock();74 m_synchronousCondition.set(new ThreadCondition); 76 75 } 77 76 78 77 void DatabaseTask::waitForSynchronousCompletion() 79 78 { 80 // Calle r of this method must lock this object beforehand81 ASSERT(m_synchronousMutex && m_synchronousMutex->tryLock() == false);82 m_synchronousCondition.set(new ThreadCondition);83 m_synchronousCondition->wait(*m_synchronousMutex.get());79 // Called from main thread. 80 m_synchronousMutex->lock(); 81 if (!m_complete) 82 m_synchronousCondition->wait(*m_synchronousMutex); 84 83 m_synchronousMutex->unlock(); 85 84 } 86 85 87 86 // *** DatabaseOpenTask *** 88 // Opens the database file and verifies the version matches the expected version 87 // Opens the database file and verifies the version matches the expected version. 89 88 90 DatabaseOpenTask::DatabaseOpenTask( )91 : DatabaseTask( )89 DatabaseOpenTask::DatabaseOpenTask(Database* database) 90 : DatabaseTask(database) 92 91 , m_code(0) 93 92 , m_success(false) … … 95 94 } 96 95 97 void DatabaseOpenTask::doPerformTask( Database* db)96 void DatabaseOpenTask::doPerformTask() 98 97 { 99 m_success = d b->performOpenAndVerify(m_code);98 m_success = database()->performOpenAndVerify(m_code); 100 99 } 101 100 102 // *** DatabaseExecuteSqlTask *** 103 // Runs the passed in sql query along with the arguments, and calls the callback with the results 101 const char* DatabaseOpenTask::debugTaskName() const 102 { 103 return "DatabaseOpenTask"; 104 } 104 105 105 DatabaseTransactionTask::DatabaseTransactionTask() 106 : DatabaseTask() 106 // *** DatabaseTransactionTask *** 107 // Starts a transaction that will report its results via a callback. 108 109 DatabaseTransactionTask::DatabaseTransactionTask(PassRefPtr<SQLTransaction> transaction) 110 : DatabaseTask(transaction->database()) 111 , m_transaction(transaction) 107 112 { 108 113 } 109 114 110 void DatabaseTransactionTask::doPerformTask(Database* db)115 DatabaseTransactionTask::~DatabaseTransactionTask() 111 116 { 112 db->performTransactionStep(); 117 } 118 119 void DatabaseTransactionTask::doPerformTask() 120 { 121 if (m_transaction->performNextStep()) { 122 // The transaction is complete, we can move on to the next one. 123 MutexLocker locker(m_transaction->database()->m_transactionInProgressMutex); 124 m_transaction->database()->scheduleTransaction(); 125 } 126 } 127 128 const char* DatabaseTransactionTask::debugTaskName() const 129 { 130 return "DatabaseTransactionTask"; 113 131 } 114 132 115 133 // *** DatabaseTableNamesTask *** 116 // Retrieves a list of all tables in the database - for WebInspector support 134 // Retrieves a list of all tables in the database - for WebInspector support. 117 135 118 DatabaseTableNamesTask::DatabaseTableNamesTask() 136 DatabaseTableNamesTask::DatabaseTableNamesTask(Database* database) 137 : DatabaseTask(database) 119 138 { 120 139 } 121 140 122 void DatabaseTableNamesTask::doPerformTask( Database* db)141 void DatabaseTableNamesTask::doPerformTask() 123 142 { 124 m_tableNames = db->performGetTableNames(); 143 m_tableNames = database()->performGetTableNames(); 144 } 145 146 const char* DatabaseTableNamesTask::debugTaskName() const 147 { 148 return "DatabaseTableNamesTask"; 125 149 } 126 150 -
trunk/WebCore/storage/DatabaseTask.h
r29663 r30172 1 1 /* 2 * Copyright (C) 2007 Apple Inc. All rights reserved.2 * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. 3 3 * 4 4 * Redistribution and use in source and binary forms, with or without … … 51 51 virtual ~DatabaseTask(); 52 52 53 void performTask( Database*);53 void performTask(); 54 54 55 Database* database() const { return m_database; } 55 56 bool isComplete() const { return m_complete; } 57 56 58 protected: 57 DatabaseTask(); 58 virtual void doPerformTask(Database* db) = 0; 59 DatabaseTask(Database*); 59 60 60 61 private: 62 virtual void doPerformTask() = 0; 63 #ifndef NDEBUG 64 virtual const char* debugTaskName() const = 0; 65 #endif 66 61 67 void lockForSynchronousScheduling(); 62 68 void waitForSynchronousCompletion(); 69 70 Database* m_database; 63 71 64 72 bool m_complete; … … 71 79 { 72 80 public: 73 DatabaseOpenTask( );81 DatabaseOpenTask(Database*); 74 82 75 83 ExceptionCode exceptionCode() const { return m_code; } 76 84 bool openSuccessful() const { return m_success; } 77 85 78 protected: 79 virtual void doPerformTask(Database* db); 86 private: 87 virtual void doPerformTask(); 88 #ifndef NDEBUG 89 virtual const char* debugTaskName() const; 90 #endif 80 91 81 private:82 92 ExceptionCode m_code; 83 93 bool m_success; … … 87 97 { 88 98 public: 89 DatabaseTransactionTask(); 99 DatabaseTransactionTask(PassRefPtr<SQLTransaction>); 100 SQLTransaction* transaction() const { return m_transaction.get(); } 90 101 91 protected: 92 virtual void doPerformTask(Database* db); 102 virtual ~DatabaseTransactionTask(); 103 private: 104 virtual void doPerformTask(); 105 #ifndef NDEBUG 106 virtual const char* debugTaskName() const; 107 #endif 108 109 RefPtr<SQLTransaction> m_transaction; 93 110 }; 94 111 … … 96 113 { 97 114 public: 98 DatabaseTableNamesTask( );115 DatabaseTableNamesTask(Database*); 99 116 100 117 Vector<String>& tableNames() { return m_tableNames; } 101 protected: 102 virtual void doPerformTask(Database* db); 118 103 119 private: 120 virtual void doPerformTask(); 121 #ifndef NDEBUG 122 virtual const char* debugTaskName() const; 123 #endif 124 104 125 Vector<String> m_tableNames; 105 126 }; -
trunk/WebCore/storage/DatabaseThread.cpp
r29663 r30172 1 1 /* 2 * Copyright (C) 2007 Apple Inc. All rights reserved.2 * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. 3 3 * 4 4 * Redistribution and use in source and binary forms, with or without … … 38 38 DatabaseThread::DatabaseThread(Document* document) 39 39 : m_threadID(0) 40 , m_terminationRequested(false)41 40 { 42 41 m_selfRef = this; … … 58 57 } 59 58 60 void DatabaseThread:: documentGoingAway()59 void DatabaseThread::requestTermination() 61 60 { 62 // This document is going away, so this thread is going away.63 // -Clear records of all Databases out64 // -Leave the thread main loop65 // -Detach the thread so the thread itself runs out and releases all thread resources66 // -Clear the RefPtr<> self so if noone else is hanging on the thread, the object itself is deleted.67 68 61 LOG(StorageAPI, "Document owning DatabaseThread %p is going away - starting thread shutdown", this); 69 70 // Clear all database records out, and let Databases know that this DatabaseThread is going away 71 { 72 MutexLocker locker(m_databaseWorkMutex); 73 74 // FIXME - The policy we're enforcing right here is that when a document goes away, any pending 75 // sql queries that were queued up also go away. Is this appropriate? 76 77 HashSet<RefPtr<Database> >::iterator i = m_activeDatabases.begin(); 78 HashSet<RefPtr<Database> >::iterator end = m_activeDatabases.end(); 79 80 for (; i != end; ++i) { 81 (*i)->databaseThreadGoingAway(); 82 Deque<RefPtr<DatabaseTask> >* databaseQueue = m_databaseTaskQueues.get((*i).get()); 83 ASSERT(databaseQueue); 84 m_databaseTaskQueues.remove((*i).get()); 85 delete databaseQueue; 86 } 87 ASSERT(m_databaseTaskQueues.isEmpty()); 88 m_activeDatabases.clear(); 89 } 90 91 // Request the thread to cleanup and shutdown 92 m_terminationRequested = true; 93 wakeWorkThread(); 94 } 95 96 void DatabaseThread::databaseGoingAway(Database* database) 97 { 98 MutexLocker locker(m_databaseWorkMutex); 99 if (!m_activeDatabases.contains(database)) 100 return; 101 102 // FIXME - The policy we're enforcing right here is that when a document goes away, any pending 103 // sql queries that were queued up also go away. Is this appropriate? 104 Deque<RefPtr<DatabaseTask> >* databaseQueue = m_databaseTaskQueues.get(database); 105 ASSERT(databaseQueue); 106 m_databaseTaskQueues.remove(database); 107 delete databaseQueue; 108 109 m_activeDatabases.remove(database); 62 m_queue.kill(); 110 63 } 111 64 … … 120 73 LOG(StorageAPI, "Starting DatabaseThread %p", this); 121 74 122 m_threadMutex.lock(); 123 while (!m_terminationRequested) { 124 m_threadMutex.unlock(); 125 AutodrainedPool pool; 126 127 LOG(StorageAPI, "Iteration of main loop for DatabaseThread %p", this); 128 129 bool result; 130 do { 131 result = dispatchNextTaskIdentifier(); 132 if (m_terminationRequested) 133 break; 134 } while(result); 135 136 if (m_terminationRequested) 75 AutodrainedPool pool; 76 while (true) { 77 RefPtr<DatabaseTask> task; 78 if (!m_queue.waitForMessage(task)) 137 79 break; 138 80 81 task->performTask(); 82 139 83 pool.cycle(); 140 m_threadMutex.lock();141 m_threadCondition.wait(m_threadMutex);142 84 } 143 m_threadMutex.unlock();144 85 145 86 LOG(StorageAPI, "About to detach thread %i and clear the ref to DatabaseThread %p, which currently has %i ref(s)", m_threadID, this, refCount()); … … 154 95 } 155 96 156 bool DatabaseThread::dispatchNextTaskIdentifier()97 void DatabaseThread::scheduleTask(DatabaseTask* task) 157 98 { 158 RefPtr<Database> workDatabase; 99 m_queue.append(task); 100 } 101 102 void DatabaseThread::scheduleImmediateTask(DatabaseTask* task) 103 { 104 m_queue.prepend(task); 105 } 106 107 void DatabaseThread::unscheduleDatabaseTasks(Database* database) 108 { 109 // Note that the thread loop is running, so some tasks for the database 110 // may still be executed. This is unavoidable. 111 112 Deque<RefPtr<DatabaseTask> > filteredReverseQueue; 159 113 RefPtr<DatabaseTask> task; 160 161 { 162 MutexLocker locker(m_databaseWorkMutex); 163 while (!task && m_globalQueue.size()) { 164 Database* database = m_globalQueue.first(); 165 m_globalQueue.removeFirst(); 166 167 Deque<RefPtr<DatabaseTask> >* databaseQueue = m_databaseTaskQueues.get(database); 168 ASSERT(databaseQueue || !m_activeDatabases.contains(database)); 169 if (!databaseQueue) 170 continue; 171 172 ASSERT(databaseQueue->size()); 173 task = databaseQueue->first(); 174 workDatabase = database; 175 databaseQueue->removeFirst(); 176 } 114 while (m_queue.tryGetMessage(task)) { 115 if (task->database() != database) 116 filteredReverseQueue.append(task); 177 117 } 178 118 179 if (task) { 180 ASSERT(workDatabase); 181 workDatabase->resetAuthorizer(); 182 task->performTask(workDatabase.get()); 183 return true; 119 while (!filteredReverseQueue.isEmpty()) { 120 m_queue.append(filteredReverseQueue.first()); 121 filteredReverseQueue.removeFirst(); 184 122 } 185 186 return false;187 }188 189 void DatabaseThread::scheduleTask(Database* database, DatabaseTask* task)190 {191 if (m_terminationRequested) {192 LOG(StorageAPI, "Attempted to schedule task %p from database %p on DatabaseThread %p after it was requested to terminate", task, database, this);193 return;194 }195 196 MutexLocker locker(m_databaseWorkMutex);197 198 Deque<RefPtr<DatabaseTask> >* databaseQueue = 0;199 200 if (!m_activeDatabases.contains(database)) {201 m_activeDatabases.add(database);202 databaseQueue = new Deque<RefPtr<DatabaseTask> >;203 m_databaseTaskQueues.set(database, databaseQueue);204 }205 206 if (!databaseQueue)207 databaseQueue = m_databaseTaskQueues.get(database);208 209 ASSERT(databaseQueue);210 211 databaseQueue->append(task);212 m_globalQueue.append(database);213 214 wakeWorkThread();215 }216 217 void DatabaseThread::scheduleImmediateTask(Database* database, DatabaseTask* task)218 {219 if (m_terminationRequested) {220 LOG_ERROR("Attempted to schedule immediate task %p from database %p on DatabaseThread %p after it was requested to terminate", task, database, this);221 return;222 }223 MutexLocker locker(m_databaseWorkMutex);224 225 Deque<RefPtr<DatabaseTask> >* databaseQueue = 0;226 227 if (!m_activeDatabases.contains(database)) {228 m_activeDatabases.add(database);229 databaseQueue = new Deque<RefPtr<DatabaseTask> >;230 m_databaseTaskQueues.set(database, databaseQueue);231 }232 233 if (!databaseQueue)234 databaseQueue = m_databaseTaskQueues.get(database);235 236 ASSERT(databaseQueue);237 238 databaseQueue->prepend(task);239 m_globalQueue.prepend(database);240 241 wakeWorkThread();242 }243 244 void DatabaseThread::wakeWorkThread()245 {246 MutexLocker locker(m_threadMutex);247 m_threadCondition.signal();248 123 } 249 124 -
trunk/WebCore/storage/DatabaseThread.h
r29663 r30172 1 1 /* 2 * Copyright (C) 2007 Apple Inc. All rights reserved.2 * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. 3 3 * 4 4 * Redistribution and use in source and binary forms, with or without … … 29 29 #define DatabaseThread_h 30 30 31 #include " Threading.h"31 #include "MessageQueue.h" 32 32 33 33 #include <wtf/HashMap.h> … … 49 49 50 50 bool start(); 51 void documentGoingAway(); 52 void databaseGoingAway(Database*); 51 void requestTermination(); 53 52 54 void scheduleTask(Database*, DatabaseTask*); 55 void scheduleImmediateTask(Database*, DatabaseTask*); 53 void scheduleTask(DatabaseTask*); 54 void scheduleImmediateTask(DatabaseTask*); // This just adds the task to the front of the queue - the caller needs to be extremely careful not to create deadlocks when waiting for completion. 55 void unscheduleDatabaseTasks(Database*); 56 56 57 private: 57 58 static void* databaseThreadStart(void*); 58 59 void* databaseThread(); 59 void wakeWorkThread();60 bool dispatchNextTaskIdentifier();61 60 62 61 ThreadIdentifier m_threadID; 63 62 RefPtr<DatabaseThread> m_selfRef; 64 63 65 // For waking/sleeping the work thread 66 ThreadCondition m_threadCondition; 67 Mutex m_threadMutex; 68 bool m_terminationRequested; 69 70 // FIXME: Need a much better queue structure than the linked-list based queue being used 71 72 // Work unit and Database management 73 Mutex m_databaseWorkMutex; 74 HashMap<Database*, Deque<RefPtr<DatabaseTask> >*> m_databaseTaskQueues; 75 HashSet<RefPtr<Database> > m_activeDatabases; 76 Deque<Database*> m_globalQueue; 64 MessageQueue<RefPtr<DatabaseTask> > m_queue; 77 65 }; 78 66 -
trunk/WebCore/storage/SQLTransaction.cpp
r30104 r30172 71 71 } 72 72 73 SQLTransaction::~SQLTransaction() 74 { 75 LOG(StorageAPI, "Transaction %p destructor\n", this); 76 } 77 73 78 void SQLTransaction::executeSQL(const String& sqlStatement, const Vector<SQLValue>& arguments, PassRefPtr<SQLStatementCallback> callback, PassRefPtr<SQLStatementErrorCallback> callbackError, ExceptionCode& e) 74 79 { … … 95 100 } 96 101 102 #ifndef NDEBUG 103 const char* SQLTransaction::debugStepName(SQLTransaction::TransactionStepMethod step) 104 { 105 if (step == &SQLTransaction::openTransactionAndPreflight) 106 return "openTransactionAndPreflight"; 107 else if (step == &SQLTransaction::runStatements) 108 return "runStatements"; 109 else if (step == &SQLTransaction::postflightAndCommit) 110 return "postflightAndCommit"; 111 else if (step == &SQLTransaction::cleanupAfterTransactionErrorCallback) 112 return "cleanupAfterTransactionErrorCallback"; 113 else if (step == &SQLTransaction::deliverTransactionCallback) 114 return "deliverTransactionCallback"; 115 else if (step == &SQLTransaction::deliverTransactionErrorCallback) 116 return "deliverTransactionErrorCallback"; 117 else if (step == &SQLTransaction::deliverStatementCallback) 118 return "deliverStatementCallback"; 119 else if (step == &SQLTransaction::deliverQuotaIncreaseCallback) 120 return "deliverQuotaIncreaseCallback"; 121 else 122 return "UNKNOWN"; 123 } 124 #endif 125 97 126 bool SQLTransaction::performNextStep() 98 127 { 128 LOG(StorageAPI, "Step %s\n", debugStepName(m_nextStep)); 129 99 130 ASSERT(m_nextStep == &SQLTransaction::openTransactionAndPreflight || 100 131 m_nextStep == &SQLTransaction::runStatements || … … 110 141 void SQLTransaction::performPendingCallback() 111 142 { 143 LOG(StorageAPI, "Callback %s\n", debugStepName(m_nextStep)); 144 112 145 ASSERT(m_nextStep == &SQLTransaction::deliverTransactionCallback || 113 146 m_nextStep == &SQLTransaction::deliverTransactionErrorCallback || … … 164 197 // Transaction Step 4 - Invoke the transaction callback with the new SQLTransaction object 165 198 m_nextStep = &SQLTransaction::deliverTransactionCallback; 199 LOG(StorageAPI, "Scheduling deliverTransactionCallback for transaction %p\n", this); 166 200 m_database->scheduleTransactionCallback(this); 167 201 } … … 188 222 void SQLTransaction::scheduleToRunStatements() 189 223 { 190 m_currentStatement = 0;191 224 m_nextStep = &SQLTransaction::runStatements; 192 m_database->scheduleTransactionStep(); 225 LOG(StorageAPI, "Scheduling runStatements for transaction %p\n", this); 226 m_database->scheduleTransactionStep(this); 193 227 } 194 228 … … 256 290 if (m_currentStatement->hasStatementCallback()) { 257 291 m_nextStep = &SQLTransaction::deliverStatementCallback; 292 LOG(StorageAPI, "Scheduling deliverStatementCallback for transaction %p\n", this); 258 293 m_database->scheduleTransactionCallback(this); 259 294 return false; … … 264 299 if (m_currentStatement->lastExecutionFailedDueToQuota()) { 265 300 m_nextStep = &SQLTransaction::deliverQuotaIncreaseCallback; 301 LOG(StorageAPI, "Scheduling deliverQuotaIncreaseCallback for transaction %p\n", this); 266 302 m_database->scheduleTransactionCallback(this); 267 303 return false; … … 279 315 if (m_currentStatement->hasStatementErrorCallback()) { 280 316 m_nextStep = &SQLTransaction::deliverStatementCallback; 317 LOG(StorageAPI, "Scheduling deliverStatementCallback for transaction %p\n", this); 281 318 m_database->scheduleTransactionCallback(this); 282 319 } else { … … 324 361 325 362 m_nextStep = &SQLTransaction::runStatements; 326 m_database->scheduleTransactionStep(); 363 LOG(StorageAPI, "Scheduling runStatements for transaction %p\n", this); 364 m_database->scheduleTransactionStep(this); 327 365 } 328 366 … … 343 381 m_database->m_databaseAuthorizer->disable(); 344 382 m_sqliteTransaction->commit(); 345 m_database->m_databaseAuthorizer->enable(); 346 383 m_database->m_databaseAuthorizer->enable(); 384 347 385 // If the commit failed, the transaction will still be marked as "in progress" 348 386 if (m_sqliteTransaction->inProgress()) { … … 359 397 // Transaction Step 10 - End transaction steps 360 398 // There is no next step 399 LOG(StorageAPI, "Transaction %p is complete\n", this); 361 400 ASSERT(!m_database->m_sqliteDatabase.transactionInProgress()); 362 401 m_nextStep = 0; … … 374 413 else { 375 414 m_nextStep = &SQLTransaction::deliverTransactionErrorCallback; 415 LOG(StorageAPI, "Scheduling deliverTransactionErrorCallback for transaction %p\n", this); 376 416 m_database->scheduleTransactionCallback(this); 377 417 } … … 383 423 if (inCallback) { 384 424 m_nextStep = &SQLTransaction::cleanupAfterTransactionErrorCallback; 385 m_database->scheduleTransactionStep(); 425 LOG(StorageAPI, "Scheduling cleanupAfterTransactionErrorCallback for transaction %p\n", this); 426 m_database->scheduleTransactionStep(this); 386 427 } else { 387 428 cleanupAfterTransactionErrorCallback(); … … 399 440 400 441 m_nextStep = &SQLTransaction::cleanupAfterTransactionErrorCallback; 401 m_database->scheduleTransactionStep(); 442 LOG(StorageAPI, "Scheduling cleanupAfterTransactionErrorCallback for transaction %p\n", this); 443 m_database->scheduleTransactionStep(this); 402 444 } 403 445 … … 432 474 433 475 // Transaction is complete! There is no next step 476 LOG(StorageAPI, "Transaction %p is complete with an error\n", this); 434 477 ASSERT(!m_database->m_sqliteDatabase.transactionInProgress()); 435 478 m_nextStep = 0; -
trunk/WebCore/storage/SQLTransaction.h
r29663 r30172 65 65 public: 66 66 SQLTransaction(Database*, PassRefPtr<SQLTransactionCallback>, PassRefPtr<SQLTransactionErrorCallback>, PassRefPtr<SQLTransactionWrapper>); 67 ~SQLTransaction(); 67 68 68 69 void executeSQL(const String& sqlStatement, const Vector<SQLValue>& arguments, … … 73 74 74 75 Database* database() { return m_database; } 75 76 76 77 private: 77 78 typedef void (SQLTransaction::*TransactionStepMethod)(); … … 93 94 void deliverTransactionErrorCallback(); 94 95 void cleanupAfterTransactionErrorCallback(); 96 97 #ifndef NDEBUG 98 static const char* debugStepName(TransactionStepMethod); 99 #endif 95 100 96 101 RefPtr<SQLStatement> m_currentStatement;
Note: See TracChangeset
for help on using the changeset viewer.