void DatabaseContext::stopDatabases() { stopSyncDatabases(); if (m_isRegistered) { DatabaseManager::manager().unregisterDatabaseContext(this); m_isRegistered = false; } // Though we initiate termination of the DatabaseThread here in // stopDatabases(), we can't clear the m_databaseThread ref till we get to // the destructor. This is because the Databases that are managed by // DatabaseThread still rely on this ref between the context and the thread // to execute the task for closing the database. By the time we get to the // destructor, we're guaranteed that the databases are destructed (which is // why our ref count is 0 then and we're destructing). Then, the // m_databaseThread RefPtr destructor will deref and delete the // DatabaseThread. if (m_databaseThread && !m_hasRequestedTermination) { DatabaseTaskSynchronizer sync; m_databaseThread->requestTermination(&sync); m_hasRequestedTermination = true; sync.waitForTaskCompletion(); } }
virtual void performTask(ScriptExecutionContext *context) { WorkerGlobalScope* workerGlobalScope = toWorkerGlobalScope(context); #if ENABLE(SQL_DATABASE) // FIXME: Should we stop the databases as part of stopActiveDOMObjects() below? DatabaseTaskSynchronizer cleanupSync; DatabaseManager::manager().stopDatabases(workerGlobalScope, &cleanupSync); #endif workerGlobalScope->stopActiveDOMObjects(); workerGlobalScope->notifyObserversOfStop(); // Event listeners would keep DOMWrapperWorld objects alive for too long. Also, they have references to JS objects, // which become dangling once Heap is destroyed. workerGlobalScope->removeAllEventListeners(); #if ENABLE(SQL_DATABASE) // We wait for the database thread to clean up all its stuff so that we // can do more stringent leak checks as we exit. cleanupSync.waitForTaskCompletion(); #endif // Stick a shutdown command at the end of the queue, so that we deal // with all the cleanup tasks the databases post first. workerGlobalScope->postTask(WorkerThreadShutdownFinishTask::create()); }
virtual void performTask(ScriptExecutionContext *context) { ASSERT(context->isWorkerContext()); WorkerContext* workerContext = static_cast<WorkerContext*>(context); #if ENABLE(DATABASE) // We currently ignore any DatabasePolicy used for the document's // databases; if it's actually used anywhere, this should be revisited. DatabaseTaskSynchronizer cleanupSync; workerContext->stopDatabases(&cleanupSync); #endif workerContext->stopActiveDOMObjects(); // Event listeners would keep DOMWrapperWorld objects alive for too long. Also, they have references to JS objects, // which become dangling once Heap is destroyed. workerContext->removeAllEventListeners(); #if ENABLE(DATABASE) // We wait for the database thread to clean up all its stuff so that we // can do more stringent leak checks as we exit. cleanupSync.waitForTaskCompletion(); #endif // Stick a shutdown command at the end of the queue, so that we deal // with all the cleanup tasks the databases post first. workerContext->postTask(WorkerThreadShutdownFinishTask::create()); }
bool Database::openAndVerifyVersion(bool setVersionInNewDatabase, DatabaseError& error, String& errorMessage) { DatabaseTaskSynchronizer synchronizer; if (!databaseContext()->databaseThread() || databaseContext()->databaseThread()->terminationRequested(&synchronizer)) return false; bool success = false; auto task = DatabaseOpenTask::create(this, setVersionInNewDatabase, &synchronizer, error, errorMessage, success); databaseContext()->databaseThread()->scheduleImmediateTask(WTF::move(task)); synchronizer.waitForTaskCompletion(); return success; }
bool Database::openAndVerifyVersion(bool setVersionInNewDatabase, ExceptionCode& e) { DatabaseTaskSynchronizer synchronizer; if (!m_scriptExecutionContext->databaseThread() || m_scriptExecutionContext->databaseThread()->terminationRequested(&synchronizer)) return false; bool success = false; OwnPtr<DatabaseOpenTask> task = DatabaseOpenTask::create(this, setVersionInNewDatabase, &synchronizer, e, success); m_scriptExecutionContext->databaseThread()->scheduleImmediateTask(task.release()); synchronizer.waitForTaskCompletion(); return success; }
bool DatabaseBackend::openAndVerifyVersion(bool setVersionInNewDatabase, DatabaseError& error, String& errorMessage) { DatabaseTaskSynchronizer synchronizer; if (!databaseContext()->databaseThread() || databaseContext()->databaseThread()->terminationRequested(&synchronizer)) return false; DatabaseTracker::tracker().prepareToOpenDatabase(this); bool success = false; OwnPtr<DatabaseOpenTask> task = DatabaseOpenTask::create(this, setVersionInNewDatabase, &synchronizer, error, errorMessage, success); databaseContext()->databaseThread()->scheduleImmediateTask(task.release()); synchronizer.waitForTaskCompletion(); return success; }
Vector<String> Database::tableNames() { // FIXME: Not using isolatedCopy on these strings looks ok since threads take strict turns // in dealing with them. However, if the code changes, this may not be true anymore. Vector<String> result; DatabaseTaskSynchronizer synchronizer; if (!databaseContext()->databaseThread() || databaseContext()->databaseThread()->terminationRequested(&synchronizer)) return result; OwnPtr<DatabaseTableNamesTask> task = DatabaseTableNamesTask::create(this, &synchronizer, result); databaseContext()->databaseThread()->scheduleImmediateTask(task.release()); synchronizer.waitForTaskCompletion(); return result; }
void WorkerThread::stop() { // Mutex protection is necessary because stop() can be called before the context is fully created. MutexLocker lock(m_threadCreationMutex); // Ensure that tasks are being handled by thread event loop. If script execution weren't forbidden, a while(1) loop in JS could keep the thread alive forever. if (m_workerGlobalScope) { m_workerGlobalScope->script()->scheduleExecutionTermination(); #if ENABLE(SQL_DATABASE) DatabaseManager::manager().interruptAllDatabasesForContext(m_workerGlobalScope.get()); #endif m_runLoop.postTaskAndTerminate({ ScriptExecutionContext::Task::CleanupTask, [] (ScriptExecutionContext* context ) { WorkerGlobalScope* workerGlobalScope = toWorkerGlobalScope(context); #if ENABLE(SQL_DATABASE) // FIXME: Should we stop the databases as part of stopActiveDOMObjects() below? DatabaseTaskSynchronizer cleanupSync; DatabaseManager::manager().stopDatabases(workerGlobalScope, &cleanupSync); #endif workerGlobalScope->stopActiveDOMObjects(); workerGlobalScope->notifyObserversOfStop(); // Event listeners would keep DOMWrapperWorld objects alive for too long. Also, they have references to JS objects, // which become dangling once Heap is destroyed. workerGlobalScope->removeAllEventListeners(); #if ENABLE(SQL_DATABASE) // We wait for the database thread to clean up all its stuff so that we // can do more stringent leak checks as we exit. cleanupSync.waitForTaskCompletion(); #endif // Stick a shutdown command at the end of the queue, so that we deal // with all the cleanup tasks the databases post first. workerGlobalScope->postTask({ ScriptExecutionContext::Task::CleanupTask, [] (ScriptExecutionContext* context) { WorkerGlobalScope* workerGlobalScope = toWorkerGlobalScope(context); // It's not safe to call clearScript until all the cleanup tasks posted by functions initiated by WorkerThreadShutdownStartTask have completed. workerGlobalScope->clearScript(); } }); } }); return; } m_runLoop.terminate(); }
void Database::markAsDeletedAndClose() { if (m_deleted || !databaseContext()->databaseThread()) return; LOG(StorageAPI, "Marking %s (%p) as deleted", stringIdentifier().ascii().data(), this); m_deleted = true; DatabaseTaskSynchronizer synchronizer; if (databaseContext()->databaseThread()->terminationRequested(&synchronizer)) { LOG(StorageAPI, "Database handle %p is on a terminated DatabaseThread, cannot be marked for normal closure\n", this); return; } OwnPtr<DatabaseCloseTask> task = DatabaseCloseTask::create(this, &synchronizer); databaseContext()->databaseThread()->scheduleImmediateTask(task.release()); synchronizer.waitForTaskCompletion(); }
void DatabaseThread::databaseThread() { { // Wait for DatabaseThread::start() to complete. LockHolder lock(m_threadCreationMutex); LOG(StorageAPI, "Started DatabaseThread %p", this); } while (auto task = m_queue.waitForMessage()) { AutodrainedPool pool; task->performTask(); } // Clean up the list of all pending transactions on this database thread m_transactionCoordinator->shutdown(); LOG(StorageAPI, "About to detach thread %i and clear the ref to DatabaseThread %p, which currently has %i ref(s)", m_threadID, this, refCount()); // Close the databases that we ran transactions on. This ensures that if any transactions are still open, they are rolled back and we don't leave the database in an // inconsistent or locked state. DatabaseSet openSetCopy; { LockHolder lock(m_openDatabaseSetMutex); if (m_openDatabaseSet.size() > 0) { // As the call to close will modify the original set, we must take a copy to iterate over. openSetCopy.swap(m_openDatabaseSet); } } for (auto& openDatabase : openSetCopy) openDatabase->close(); // Detach the thread so its resources are no longer of any concern to anyone else detachThread(m_threadID); DatabaseTaskSynchronizer* cleanupSync = m_cleanupSync; // Clear the self refptr, possibly resulting in deletion m_selfRef = nullptr; if (cleanupSync) // Someone wanted to know when we were done cleaning up. cleanupSync->taskCompleted(); }
virtual void performTask(ScriptExecutionContext *context) { ASSERT(context->isWorkerContext()); WorkerContext* workerContext = static_cast<WorkerContext*>(context); // We currently ignore any DatabasePolicy used for the document's // databases; if it's actually used anywhere, this should be revisited. DatabaseTaskSynchronizer cleanupSync; workerContext->stopDatabases(&cleanupSync); workerContext->stopActiveDOMObjects(); workerContext->clearScript(); // We wait for the database thread to clean up all its stuff so that we // can do more stringent leak checks as we exit. cleanupSync.waitForTaskCompletion(); // Stick a shutdown command at the end of the queue, so that we deal // with all the cleanup tasks the databases post first. workerContext->postTask(WorkerThreadShutdownFinishTask::create()); }