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 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 "DatabaseThread.h" 31 32#if ENABLE(SQL_DATABASE) 33 34#include "Database.h" 35#include "DatabaseTask.h" 36#include "Logging.h" 37#include "SQLTransactionClient.h" 38#include "SQLTransactionCoordinator.h" 39#include <wtf/AutodrainedPool.h> 40 41namespace WebCore { 42 43DatabaseThread::DatabaseThread() 44 : m_threadID(0) 45#if PLATFORM(IOS) 46 , m_paused(false) 47#endif 48 , m_transactionClient(std::make_unique<SQLTransactionClient>()) 49 , m_transactionCoordinator(std::make_unique<SQLTransactionCoordinator>()) 50 , m_cleanupSync(0) 51{ 52 m_selfRef = this; 53} 54 55DatabaseThread::~DatabaseThread() 56{ 57 // The DatabaseThread will only be destructed when both its owner 58 // DatabaseContext has deref'ed it, and the databaseThread() thread function 59 // has deref'ed the DatabaseThread object. The DatabaseContext destructor 60 // will take care of ensuring that a termination request has been issued. 61 // The termination request will trigger an orderly shutdown of the thread 62 // function databaseThread(). In shutdown, databaseThread() will deref the 63 // DatabaseThread before returning. 64 ASSERT(terminationRequested()); 65} 66 67bool DatabaseThread::start() 68{ 69 MutexLocker lock(m_threadCreationMutex); 70 71 if (m_threadID) 72 return true; 73 74 m_threadID = createThread(DatabaseThread::databaseThreadStart, this, "WebCore: Database"); 75 76 return m_threadID; 77} 78 79void DatabaseThread::requestTermination(DatabaseTaskSynchronizer *cleanupSync) 80{ 81 m_cleanupSync = cleanupSync; 82 LOG(StorageAPI, "DatabaseThread %p was asked to terminate\n", this); 83#if PLATFORM(IOS) 84 m_pausedQueue.kill(); 85#endif 86 m_queue.kill(); 87} 88 89bool DatabaseThread::terminationRequested(DatabaseTaskSynchronizer* taskSynchronizer) const 90{ 91#ifndef NDEBUG 92 if (taskSynchronizer) 93 taskSynchronizer->setHasCheckedForTermination(); 94#else 95 UNUSED_PARAM(taskSynchronizer); 96#endif 97 98 return m_queue.killed(); 99} 100 101void DatabaseThread::databaseThreadStart(void* vDatabaseThread) 102{ 103 DatabaseThread* dbThread = static_cast<DatabaseThread*>(vDatabaseThread); 104 dbThread->databaseThread(); 105} 106 107#if PLATFORM(IOS) 108class DatabaseUnpauseTask : public DatabaseTask { 109public: 110 static std::unique_ptr<DatabaseUnpauseTask> create(DatabaseThread* thread) 111 { 112 return std::unique_ptr<DatabaseUnpauseTask>(new DatabaseUnpauseTask(thread)); 113 } 114 115 virtual bool shouldPerformWhilePaused() const 116 { 117 // Since we're not locking the DatabaseThread::m_paused in the main database thread loop, it's possible that 118 // a DatabaseUnpauseTask might be added to the m_pausedQueue and performed from within ::handlePausedQueue. 119 // To protect against this, we allow it to be performed even if the database is paused. 120 // If the thread is paused when it is being performed, the tasks from the paused queue will simply be 121 // requeued instead of performed. 122 return true; 123 } 124 125private: 126 DatabaseUnpauseTask(DatabaseThread* thread) 127 : DatabaseTask(0, 0) 128 , m_thread(thread) 129 {} 130 131 virtual void doPerformTask() 132 { 133 m_thread->handlePausedQueue(); 134 } 135#if !LOG_DISABLED 136 virtual const char* debugTaskName() const { return "DatabaseUnpauseTask"; } 137#endif 138 139 DatabaseThread* m_thread; 140}; 141 142 143void DatabaseThread::setPaused(bool paused) 144{ 145 if (m_paused == paused) 146 return; 147 148 MutexLocker pausedLocker(m_pausedMutex); 149 m_paused = paused; 150 if (!m_paused) 151 scheduleTask(DatabaseUnpauseTask::create(this)); 152} 153 154void DatabaseThread::handlePausedQueue() 155{ 156 Vector<std::unique_ptr<DatabaseTask> > pausedTasks; 157 while (auto task = m_pausedQueue.tryGetMessage()) 158 pausedTasks.append(WTF::move(task)); 159 160 for (unsigned i = 0; i < pausedTasks.size(); ++i) { 161 AutodrainedPool pool; 162 163 std::unique_ptr<DatabaseTask> task(pausedTasks[i].release()); 164 { 165 MutexLocker pausedLocker(m_pausedMutex); 166 if (m_paused) { 167 m_pausedQueue.append(WTF::move(task)); 168 continue; 169 } 170 } 171 172 if (terminationRequested()) 173 break; 174 175 task->performTask(); 176 } 177} 178#endif //PLATFORM(IOS) 179 180 181void DatabaseThread::databaseThread() 182{ 183 { 184 // Wait for DatabaseThread::start() to complete. 185 MutexLocker lock(m_threadCreationMutex); 186 LOG(StorageAPI, "Started DatabaseThread %p", this); 187 } 188 189 while (auto task = m_queue.waitForMessage()) { 190 AutodrainedPool pool; 191 192#if PLATFORM(IOS) 193 if (!m_paused || task->shouldPerformWhilePaused()) 194 task->performTask(); 195 else 196 m_pausedQueue.append(WTF::move(task)); 197#else 198 task->performTask(); 199#endif 200 } 201 202 // Clean up the list of all pending transactions on this database thread 203 m_transactionCoordinator->shutdown(); 204 205 LOG(StorageAPI, "About to detach thread %i and clear the ref to DatabaseThread %p, which currently has %i ref(s)", m_threadID, this, refCount()); 206 207 // 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 208 // inconsistent or locked state. 209 if (m_openDatabaseSet.size() > 0) { 210 // As the call to close will modify the original set, we must take a copy to iterate over. 211 DatabaseSet openSetCopy; 212 openSetCopy.swap(m_openDatabaseSet); 213 DatabaseSet::iterator end = openSetCopy.end(); 214 for (DatabaseSet::iterator it = openSetCopy.begin(); it != end; ++it) 215 (*it).get()->close(); 216 } 217 218 // Detach the thread so its resources are no longer of any concern to anyone else 219 detachThread(m_threadID); 220 221 DatabaseTaskSynchronizer* cleanupSync = m_cleanupSync; 222 223 // Clear the self refptr, possibly resulting in deletion 224 m_selfRef = 0; 225 226 if (cleanupSync) // Someone wanted to know when we were done cleaning up. 227 cleanupSync->taskCompleted(); 228} 229 230void DatabaseThread::recordDatabaseOpen(DatabaseBackend* database) 231{ 232 ASSERT(currentThread() == m_threadID); 233 ASSERT(database); 234 ASSERT(!m_openDatabaseSet.contains(database)); 235 m_openDatabaseSet.add(database); 236} 237 238void DatabaseThread::recordDatabaseClosed(DatabaseBackend* database) 239{ 240 ASSERT(currentThread() == m_threadID); 241 ASSERT(database); 242 ASSERT(m_queue.killed() || m_openDatabaseSet.contains(database)); 243 m_openDatabaseSet.remove(database); 244} 245 246void DatabaseThread::scheduleTask(std::unique_ptr<DatabaseTask> task) 247{ 248 ASSERT(!task->hasSynchronizer() || task->hasCheckedForTermination()); 249 m_queue.append(WTF::move(task)); 250} 251 252void DatabaseThread::scheduleImmediateTask(std::unique_ptr<DatabaseTask> task) 253{ 254 ASSERT(!task->hasSynchronizer() || task->hasCheckedForTermination()); 255 m_queue.prepend(WTF::move(task)); 256} 257 258class SameDatabasePredicate { 259public: 260 SameDatabasePredicate(const DatabaseBackend* database) : m_database(database) { } 261 bool operator()(const DatabaseTask& task) const { return task.database() == m_database; } 262private: 263 const DatabaseBackend* m_database; 264}; 265 266void DatabaseThread::unscheduleDatabaseTasks(DatabaseBackend* database) 267{ 268 // Note that the thread loop is running, so some tasks for the database 269 // may still be executed. This is unavoidable. 270 SameDatabasePredicate predicate(database); 271 m_queue.removeIf(predicate); 272} 273} // namespace WebCore 274#endif 275