1/*
2 * Copyright (C) 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 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "DatabaseBackend.h"
28
29#if ENABLE(SQL_DATABASE)
30
31#include "ChangeVersionData.h"
32#include "ChangeVersionWrapper.h"
33#include "DatabaseBackendContext.h"
34#include "DatabaseTask.h"
35#include "DatabaseThread.h"
36#include "DatabaseTracker.h"
37#include "Logging.h"
38#include "SQLTransaction.h"
39#include "SQLTransactionBackend.h"
40#include "SQLTransactionClient.h"
41#include "SQLTransactionCoordinator.h"
42
43namespace WebCore {
44
45DatabaseBackend::DatabaseBackend(PassRefPtr<DatabaseBackendContext> databaseContext, const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize)
46    : DatabaseBackendBase(databaseContext, name, expectedVersion, displayName, estimatedSize, DatabaseType::Async)
47    , m_transactionInProgress(false)
48    , m_isTransactionQueueEnabled(true)
49{
50}
51
52bool DatabaseBackend::openAndVerifyVersion(bool setVersionInNewDatabase, DatabaseError& error, String& errorMessage)
53{
54    DatabaseTaskSynchronizer synchronizer;
55    if (!databaseContext()->databaseThread() || databaseContext()->databaseThread()->terminationRequested(&synchronizer))
56        return false;
57
58    bool success = false;
59    OwnPtr<DatabaseOpenTask> task = DatabaseOpenTask::create(this, setVersionInNewDatabase, &synchronizer, error, errorMessage, success);
60    databaseContext()->databaseThread()->scheduleImmediateTask(task.release());
61    synchronizer.waitForTaskCompletion();
62
63    return success;
64}
65
66bool DatabaseBackend::performOpenAndVerify(bool setVersionInNewDatabase, DatabaseError& error, String& errorMessage)
67{
68    if (DatabaseBackendBase::performOpenAndVerify(setVersionInNewDatabase, error, errorMessage)) {
69        if (databaseContext()->databaseThread())
70            databaseContext()->databaseThread()->recordDatabaseOpen(this);
71
72        return true;
73    }
74
75    return false;
76}
77
78void DatabaseBackend::close()
79{
80    ASSERT(databaseContext()->databaseThread());
81    ASSERT(currentThread() == databaseContext()->databaseThread()->getThreadID());
82
83    {
84        MutexLocker locker(m_transactionInProgressMutex);
85
86        // Clean up transactions that have not been scheduled yet:
87        // Transaction phase 1 cleanup. See comment on "What happens if a
88        // transaction is interrupted?" at the top of SQLTransactionBackend.cpp.
89        RefPtr<SQLTransactionBackend> transaction;
90        while (!m_transactionQueue.isEmpty()) {
91            transaction = m_transactionQueue.takeFirst();
92            transaction->notifyDatabaseThreadIsShuttingDown();
93        }
94
95        m_isTransactionQueueEnabled = false;
96        m_transactionInProgress = false;
97    }
98
99    closeDatabase();
100
101    // DatabaseThread keeps databases alive by referencing them in its
102    // m_openDatabaseSet. DatabaseThread::recordDatabaseClose() will remove
103    // this database from that set (which effectively deref's it). We hold on
104    // to it with a local pointer here for a liitle longer, so that we can
105    // unschedule any DatabaseTasks that refer to it before the database gets
106    // deleted.
107    RefPtr<DatabaseBackend> protect = this;
108    databaseContext()->databaseThread()->recordDatabaseClosed(this);
109    databaseContext()->databaseThread()->unscheduleDatabaseTasks(this);
110}
111
112PassRefPtr<SQLTransactionBackend> DatabaseBackend::runTransaction(PassRefPtr<SQLTransaction> transaction,
113    bool readOnly, const ChangeVersionData* data)
114{
115    MutexLocker locker(m_transactionInProgressMutex);
116    if (!m_isTransactionQueueEnabled)
117        return 0;
118
119    RefPtr<SQLTransactionWrapper> wrapper;
120    if (data)
121        wrapper = ChangeVersionWrapper::create(data->oldVersion(), data->newVersion());
122
123    RefPtr<SQLTransactionBackend> transactionBackend = SQLTransactionBackend::create(this, transaction, wrapper, readOnly);
124    m_transactionQueue.append(transactionBackend);
125    if (!m_transactionInProgress)
126        scheduleTransaction();
127
128    return transactionBackend;
129}
130
131void DatabaseBackend::inProgressTransactionCompleted()
132{
133    MutexLocker locker(m_transactionInProgressMutex);
134    m_transactionInProgress = false;
135    scheduleTransaction();
136}
137
138void DatabaseBackend::scheduleTransaction()
139{
140    ASSERT(!m_transactionInProgressMutex.tryLock()); // Locked by caller.
141    RefPtr<SQLTransactionBackend> transaction;
142
143    if (m_isTransactionQueueEnabled && !m_transactionQueue.isEmpty())
144        transaction = m_transactionQueue.takeFirst();
145
146    if (transaction && databaseContext()->databaseThread()) {
147        OwnPtr<DatabaseTransactionTask> task = DatabaseTransactionTask::create(transaction);
148        LOG(StorageAPI, "Scheduling DatabaseTransactionTask %p for transaction %p\n", task.get(), task->transaction());
149        m_transactionInProgress = true;
150        databaseContext()->databaseThread()->scheduleTask(task.release());
151    } else
152        m_transactionInProgress = false;
153}
154
155void DatabaseBackend::scheduleTransactionStep(SQLTransactionBackend* transaction)
156{
157    if (!databaseContext()->databaseThread())
158        return;
159
160    OwnPtr<DatabaseTransactionTask> task = DatabaseTransactionTask::create(transaction);
161    LOG(StorageAPI, "Scheduling DatabaseTransactionTask %p for the transaction step\n", task.get());
162    databaseContext()->databaseThread()->scheduleTask(task.release());
163}
164
165SQLTransactionClient* DatabaseBackend::transactionClient() const
166{
167    return databaseContext()->databaseThread()->transactionClient();
168}
169
170SQLTransactionCoordinator* DatabaseBackend::transactionCoordinator() const
171{
172    return databaseContext()->databaseThread()->transactionCoordinator();
173}
174
175} // namespace WebCore
176
177#endif // ENABLE(SQL_DATABASE)
178