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