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