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#include "Database.h"
33#include "DatabaseTask.h"
34#include "Logging.h"
35#include "SQLTransaction.h"
36#include "SQLTransactionCoordinator.h"
37#include <wtf/AutodrainedPool.h>
38
39namespace WebCore {
40
41DatabaseThread::DatabaseThread()
42 : m_transactionCoordinator(std::make_unique<SQLTransactionCoordinator>())
43{
44 m_selfRef = this;
45}
46
47DatabaseThread::~DatabaseThread()
48{
49 // The DatabaseThread will only be destructed when both its owner
50 // DatabaseContext has deref'ed it, and the databaseThread() thread function
51 // has deref'ed the DatabaseThread object. The DatabaseContext destructor
52 // will take care of ensuring that a termination request has been issued.
53 // The termination request will trigger an orderly shutdown of the thread
54 // function databaseThread(). In shutdown, databaseThread() will deref the
55 // DatabaseThread before returning.
56 ASSERT(terminationRequested());
57}
58
59void DatabaseThread::start()
60{
61 LockHolder lock(m_threadCreationMutex);
62
63 if (m_thread)
64 return;
65
66 m_thread = Thread::create("WebCore: Database", [this] {
67 databaseThread();
68 });
69}
70
71void DatabaseThread::requestTermination(DatabaseTaskSynchronizer* cleanupSync)
72{
73 m_cleanupSync = cleanupSync;
74 LOG(StorageAPI, "DatabaseThread %p was asked to terminate\n", this);
75 m_queue.kill();
76}
77
78bool DatabaseThread::terminationRequested(DatabaseTaskSynchronizer* taskSynchronizer) const
79{
80#ifndef NDEBUG
81 if (taskSynchronizer)
82 taskSynchronizer->setHasCheckedForTermination();
83#else
84 UNUSED_PARAM(taskSynchronizer);
85#endif
86
87 return m_queue.killed();
88}
89
90void DatabaseThread::databaseThread()
91{
92 {
93 // Wait for DatabaseThread::start() to complete.
94 LockHolder lock(m_threadCreationMutex);
95 LOG(StorageAPI, "Started DatabaseThread %p", this);
96 }
97
98 while (auto task = m_queue.waitForMessage()) {
99 AutodrainedPool pool;
100
101 task->performTask();
102 }
103
104 // Clean up the list of all pending transactions on this database thread
105 m_transactionCoordinator->shutdown();
106
107 LOG(StorageAPI, "About to detach thread %p and clear the ref to DatabaseThread %p, which currently has %i ref(s)", m_thread.get(), this, refCount());
108
109 // 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
110 // inconsistent or locked state.
111 DatabaseSet openSetCopy;
112 {
113 LockHolder lock(m_openDatabaseSetMutex);
114 if (m_openDatabaseSet.size() > 0) {
115 // As the call to close will modify the original set, we must take a copy to iterate over.
116 openSetCopy.swap(m_openDatabaseSet);
117 }
118 }
119
120 for (auto& openDatabase : openSetCopy)
121 openDatabase->performClose();
122
123 // Detach the thread so its resources are no longer of any concern to anyone else
124 m_thread->detach();
125
126 DatabaseTaskSynchronizer* cleanupSync = m_cleanupSync;
127
128 // Clear the self refptr, possibly resulting in deletion
129 m_selfRef = nullptr;
130
131 if (cleanupSync) // Someone wanted to know when we were done cleaning up.
132 cleanupSync->taskCompleted();
133}
134
135void DatabaseThread::recordDatabaseOpen(Database& database)
136{
137 LockHolder lock(m_openDatabaseSetMutex);
138
139 ASSERT(m_thread == &Thread::current());
140 ASSERT(!m_openDatabaseSet.contains(&database));
141 m_openDatabaseSet.add(&database);
142}
143
144void DatabaseThread::recordDatabaseClosed(Database& database)
145{
146 LockHolder lock(m_openDatabaseSetMutex);
147
148 ASSERT(m_thread == &Thread::current());
149 ASSERT(m_queue.killed() || m_openDatabaseSet.contains(&database));
150 m_openDatabaseSet.remove(&database);
151}
152
153void DatabaseThread::scheduleTask(std::unique_ptr<DatabaseTask>&& task)
154{
155 ASSERT(!task->hasSynchronizer() || task->hasCheckedForTermination());
156 m_queue.append(WTFMove(task));
157}
158
159void DatabaseThread::scheduleImmediateTask(std::unique_ptr<DatabaseTask>&& task)
160{
161 ASSERT(!task->hasSynchronizer() || task->hasCheckedForTermination());
162 m_queue.prepend(WTFMove(task));
163}
164
165void DatabaseThread::unscheduleDatabaseTasks(Database& database)
166{
167 // The thread loop is running, sp some tasks for this database may still be executed. This is unavoidable.
168 m_queue.removeIf([&database] (const DatabaseTask& task) {
169 return &task.database() == &database;
170 });
171}
172
173bool DatabaseThread::hasPendingDatabaseActivity() const
174{
175 LockHolder lock(m_openDatabaseSetMutex);
176 for (auto& database : m_openDatabaseSet) {
177 if (database->hasPendingCreationEvent() || database->hasPendingTransaction())
178 return true;
179 }
180 return false;
181}
182
183} // namespace WebCore
184