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 "modules/webdatabase/DatabaseBackend.h"
28
29#include "platform/Logging.h"
30#include "modules/webdatabase/ChangeVersionData.h"
31#include "modules/webdatabase/ChangeVersionWrapper.h"
32#include "modules/webdatabase/DatabaseContext.h"
33#include "modules/webdatabase/DatabaseTask.h"
34#include "modules/webdatabase/DatabaseThread.h"
35#include "modules/webdatabase/DatabaseTracker.h"
36#include "modules/webdatabase/SQLTransaction.h"
37#include "modules/webdatabase/SQLTransactionBackend.h"
38#include "modules/webdatabase/SQLTransactionClient.h"
39#include "modules/webdatabase/SQLTransactionCoordinator.h"
40
41namespace WebCore {
42
43DatabaseBackend::DatabaseBackend(PassRefPtr<DatabaseContext> databaseContext, const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize)
44    : DatabaseBackendBase(databaseContext, name, expectedVersion, displayName, estimatedSize, DatabaseType::Async)
45    , m_transactionInProgress(false)
46    , m_isTransactionQueueEnabled(true)
47{
48}
49
50bool DatabaseBackend::openAndVerifyVersion(bool setVersionInNewDatabase, DatabaseError& error, String& errorMessage)
51{
52    DatabaseTaskSynchronizer synchronizer;
53    if (!databaseContext()->databaseThread() || databaseContext()->databaseThread()->terminationRequested(&synchronizer))
54        return false;
55
56    DatabaseTracker::tracker().prepareToOpenDatabase(this);
57    bool success = false;
58    OwnPtr<DatabaseOpenTask> task = DatabaseOpenTask::create(this, setVersionInNewDatabase, &synchronizer, error, errorMessage, success);
59    databaseContext()->databaseThread()->scheduleTask(task.release());
60    synchronizer.waitForTaskCompletion();
61
62    return success;
63}
64
65bool DatabaseBackend::performOpenAndVerify(bool setVersionInNewDatabase, DatabaseError& error, String& errorMessage)
66{
67    if (DatabaseBackendBase::performOpenAndVerify(setVersionInNewDatabase, error, errorMessage)) {
68        if (databaseContext()->databaseThread())
69            databaseContext()->databaseThread()->recordDatabaseOpen(this);
70
71        return true;
72    }
73
74    return false;
75}
76
77void DatabaseBackend::close()
78{
79    ASSERT(databaseContext()->databaseThread());
80    ASSERT(databaseContext()->databaseThread()->isDatabaseThread());
81
82    {
83        MutexLocker locker(m_transactionInProgressMutex);
84
85        // Clean up transactions that have not been scheduled yet:
86        // Transaction phase 1 cleanup. See comment on "What happens if a
87        // transaction is interrupted?" at the top of SQLTransactionBackend.cpp.
88        RefPtr<SQLTransactionBackend> transaction;
89        while (!m_transactionQueue.isEmpty()) {
90            transaction = m_transactionQueue.takeFirst();
91            transaction->notifyDatabaseThreadIsShuttingDown();
92        }
93
94        m_isTransactionQueueEnabled = false;
95        m_transactionInProgress = false;
96    }
97
98    closeDatabase();
99    databaseContext()->databaseThread()->recordDatabaseClosed(this);
100}
101
102PassRefPtr<SQLTransactionBackend> DatabaseBackend::runTransaction(PassRefPtr<SQLTransaction> transaction,
103    bool readOnly, const ChangeVersionData* data)
104{
105    MutexLocker locker(m_transactionInProgressMutex);
106    if (!m_isTransactionQueueEnabled)
107        return 0;
108
109    RefPtr<SQLTransactionWrapper> wrapper;
110    if (data)
111        wrapper = ChangeVersionWrapper::create(data->oldVersion(), data->newVersion());
112
113    RefPtr<SQLTransactionBackend> transactionBackend = SQLTransactionBackend::create(this, transaction, wrapper, readOnly);
114    m_transactionQueue.append(transactionBackend);
115    if (!m_transactionInProgress)
116        scheduleTransaction();
117
118    return transactionBackend;
119}
120
121void DatabaseBackend::inProgressTransactionCompleted()
122{
123    MutexLocker locker(m_transactionInProgressMutex);
124    m_transactionInProgress = false;
125    scheduleTransaction();
126}
127
128void DatabaseBackend::scheduleTransaction()
129{
130    ASSERT(!m_transactionInProgressMutex.tryLock()); // Locked by caller.
131    RefPtr<SQLTransactionBackend> transaction;
132
133    if (m_isTransactionQueueEnabled && !m_transactionQueue.isEmpty())
134        transaction = m_transactionQueue.takeFirst();
135
136    if (transaction && databaseContext()->databaseThread()) {
137        OwnPtr<DatabaseTransactionTask> task = DatabaseTransactionTask::create(transaction);
138        WTF_LOG(StorageAPI, "Scheduling DatabaseTransactionTask %p for transaction %p\n", task.get(), task->transaction());
139        m_transactionInProgress = true;
140        databaseContext()->databaseThread()->scheduleTask(task.release());
141    } else
142        m_transactionInProgress = false;
143}
144
145void DatabaseBackend::scheduleTransactionStep(SQLTransactionBackend* transaction)
146{
147    if (!databaseContext()->databaseThread())
148        return;
149
150    OwnPtr<DatabaseTransactionTask> task = DatabaseTransactionTask::create(transaction);
151    WTF_LOG(StorageAPI, "Scheduling DatabaseTransactionTask %p for the transaction step\n", task.get());
152    databaseContext()->databaseThread()->scheduleTask(task.release());
153}
154
155SQLTransactionClient* DatabaseBackend::transactionClient() const
156{
157    return databaseContext()->databaseThread()->transactionClient();
158}
159
160SQLTransactionCoordinator* DatabaseBackend::transactionCoordinator() const
161{
162    return databaseContext()->databaseThread()->transactionCoordinator();
163}
164
165} // namespace WebCore
166