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