1 /* 2 * Copyright (C) 2007, 2008, 2013 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 14 * its contributors may be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include "config.h" 30 #include "modules/webdatabase/DatabaseThread.h" 31 32 #include "modules/webdatabase/Database.h" 33 #include "modules/webdatabase/DatabaseTask.h" 34 #include "modules/webdatabase/SQLTransactionClient.h" 35 #include "modules/webdatabase/SQLTransactionCoordinator.h" 36 #include "platform/Logging.h" 37 #include "platform/heap/glue/MessageLoopInterruptor.h" 38 #include "platform/heap/glue/PendingGCRunner.h" 39 #include "public/platform/Platform.h" 40 41 namespace WebCore { 42 43 DatabaseThread::DatabaseThread() 44 : m_transactionClient(adoptPtr(new SQLTransactionClient())) 45 , m_transactionCoordinator(adoptPtrWillBeNoop(new SQLTransactionCoordinator())) 46 , m_cleanupSync(0) 47 , m_terminationRequested(false) 48 { 49 } 50 51 DatabaseThread::~DatabaseThread() 52 { 53 ASSERT(m_openDatabaseSet.isEmpty()); 54 // Oilpan: The database thread must have finished its cleanup tasks before 55 // the following clear(). Otherwise, WebThread destructor blocks the caller 56 // thread, and causes a deadlock with ThreadState cleanup. 57 // DatabaseContext::stop() asks the database thread to close all of 58 // databases, and wait until GC heap cleanup of the database thread. So we 59 // can safely destruct WebThread here. 60 m_thread.clear(); 61 } 62 63 void DatabaseThread::trace(Visitor* visitor) 64 { 65 visitor->trace(m_openDatabaseSet); 66 visitor->trace(m_transactionCoordinator); 67 } 68 69 void DatabaseThread::start() 70 { 71 if (m_thread) 72 return; 73 m_thread = adoptPtr(blink::Platform::current()->createThread("WebCore: Database")); 74 m_thread->postTask(new Task(WTF::bind(&DatabaseThread::setupDatabaseThread, this))); 75 } 76 77 void DatabaseThread::setupDatabaseThread() 78 { 79 m_pendingGCRunner = adoptPtr(new PendingGCRunner); 80 m_messageLoopInterruptor = adoptPtr(new MessageLoopInterruptor(m_thread.get())); 81 m_thread->addTaskObserver(m_pendingGCRunner.get()); 82 ThreadState::attach(); 83 ThreadState::current()->addInterruptor(m_messageLoopInterruptor.get()); 84 } 85 86 void DatabaseThread::requestTermination(TaskSynchronizer *cleanupSync) 87 { 88 MutexLocker lock(m_terminationRequestedMutex); 89 ASSERT(!m_terminationRequested); 90 m_terminationRequested = true; 91 m_cleanupSync = cleanupSync; 92 WTF_LOG(StorageAPI, "DatabaseThread %p was asked to terminate\n", this); 93 m_thread->postTask(new Task(WTF::bind(&DatabaseThread::cleanupDatabaseThread, this))); 94 } 95 96 bool DatabaseThread::terminationRequested(TaskSynchronizer* taskSynchronizer) const 97 { 98 #ifndef NDEBUG 99 if (taskSynchronizer) 100 taskSynchronizer->setHasCheckedForTermination(); 101 #endif 102 103 MutexLocker lock(m_terminationRequestedMutex); 104 return m_terminationRequested; 105 } 106 107 void DatabaseThread::cleanupDatabaseThread() 108 { 109 WTF_LOG(StorageAPI, "Cleaning up DatabaseThread %p", this); 110 111 // Clean up the list of all pending transactions on this database thread 112 m_transactionCoordinator->shutdown(); 113 114 // 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 115 // inconsistent or locked state. 116 if (m_openDatabaseSet.size() > 0) { 117 // As the call to close will modify the original set, we must take a copy to iterate over. 118 WillBeHeapHashSet<RefPtrWillBeMember<DatabaseBackend> > openSetCopy; 119 openSetCopy.swap(m_openDatabaseSet); 120 WillBeHeapHashSet<RefPtrWillBeMember<DatabaseBackend> >::iterator end = openSetCopy.end(); 121 for (WillBeHeapHashSet<RefPtrWillBeMember<DatabaseBackend> >::iterator it = openSetCopy.begin(); it != end; ++it) 122 (*it)->close(); 123 } 124 125 m_thread->postTask(new Task(WTF::bind(&DatabaseThread::cleanupDatabaseThreadCompleted, this))); 126 } 127 128 void DatabaseThread::cleanupDatabaseThreadCompleted() 129 { 130 ThreadState::current()->removeInterruptor(m_messageLoopInterruptor.get()); 131 ThreadState::detach(); 132 // We need to unregister PendingGCRunner before finising this task to avoid 133 // PendingGCRunner::didProcessTask accesses dead ThreadState. 134 m_thread->removeTaskObserver(m_pendingGCRunner.get()); 135 136 if (m_cleanupSync) // Someone wanted to know when we were done cleaning up. 137 m_cleanupSync->taskCompleted(); 138 } 139 140 void DatabaseThread::recordDatabaseOpen(DatabaseBackend* database) 141 { 142 ASSERT(isDatabaseThread()); 143 ASSERT(database); 144 ASSERT(!m_openDatabaseSet.contains(database)); 145 m_openDatabaseSet.add(database); 146 } 147 148 void DatabaseThread::recordDatabaseClosed(DatabaseBackend* database) 149 { 150 ASSERT(isDatabaseThread()); 151 ASSERT(database); 152 ASSERT(m_terminationRequested || m_openDatabaseSet.contains(database)); 153 m_openDatabaseSet.remove(database); 154 } 155 156 bool DatabaseThread::isDatabaseOpen(DatabaseBackend* database) 157 { 158 ASSERT(isDatabaseThread()); 159 ASSERT(database); 160 MutexLocker lock(m_terminationRequestedMutex); 161 return !m_terminationRequested && m_openDatabaseSet.contains(database); 162 } 163 164 void DatabaseThread::scheduleTask(PassOwnPtr<DatabaseTask> task) 165 { 166 ASSERT(m_thread); 167 ASSERT(!task->hasSynchronizer() || task->hasCheckedForTermination()); 168 // WebThread takes ownership of the task. 169 m_thread->postTask(task.leakPtr()); 170 } 171 172 } // namespace WebCore 173