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