IDBTransactionBackendImpl.cpp 14.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
/*
 * Copyright (C) 2010 Google Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1.  Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 * 2.  Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

26
#include "config.h"
27
#include "IDBTransactionBackendImpl.h"
28

29
#if ENABLE(INDEXED_DATABASE)
30

31
#include "IDBCursorBackend.h"
32
#include "IDBDatabaseBackendImpl.h"
33
#include "IDBDatabaseCallbacks.h"
34
#include "IDBDatabaseException.h"
35
#include "IDBFactoryBackendInterface.h"
36
#include "IDBKeyRange.h"
37
#include "IDBTransactionBackendOperations.h"
38
#include "IDBTransactionCoordinator.h"
beidson@apple.com's avatar
beidson@apple.com committed
39
#include "Logging.h"
40

41 42
namespace WebCore {

43
PassRefPtr<IDBTransactionBackendImpl> IDBTransactionBackendImpl::create(IDBDatabaseBackendImpl* databaseBackend, int64_t id, PassRefPtr<IDBDatabaseCallbacks> callbacks, const Vector<int64_t>& objectStoreIds, IndexedDB::TransactionMode mode)
44
{
45 46 47 48
    HashSet<int64_t> objectStoreHashSet;
    for (size_t i = 0; i < objectStoreIds.size(); ++i)
        objectStoreHashSet.add(objectStoreIds[i]);

49
    return adoptRef(new IDBTransactionBackendImpl(databaseBackend, id, callbacks, objectStoreHashSet, mode));
50 51
}

52
IDBTransactionBackendImpl::IDBTransactionBackendImpl(IDBDatabaseBackendImpl* databaseBackend, int64_t id, PassRefPtr<IDBDatabaseCallbacks> callbacks, const HashSet<int64_t>& objectStoreIds, IndexedDB::TransactionMode mode)
53
    : IDBTransactionBackendInterface(id)
54
    , m_objectStoreIds(objectStoreIds)
55
    , m_mode(mode)
56
    , m_state(Unused)
57
    , m_commitPending(false)
58
    , m_callbacks(callbacks)
59
    , m_database(databaseBackend)
60
    , m_backingStoreTransaction(databaseBackend->backingStore()->createBackingStoreTransaction())
61
    , m_taskTimer(this, &IDBTransactionBackendImpl::taskTimerFired)
62
    , m_pendingPreemptiveEvents(0)
63
    , m_backingStore(databaseBackend->backingStore())
64
{
65 66 67
    // We pass a reference of this object before it can be adopted.
    relaxAdoptionRequirement();

68 69 70
    m_database->transactionCoordinator()->didCreateTransaction(this);
}

71
IDBTransactionBackendImpl::~IDBTransactionBackendImpl()
72 73 74
{
    // It shouldn't be possible for this object to get deleted until it's either complete or aborted.
    ASSERT(m_state == Finished);
75 76
}

77
void IDBTransactionBackendImpl::scheduleTask(IDBDatabaseBackendInterface::TaskType type, PassOwnPtr<IDBOperation> task, PassOwnPtr<IDBOperation> abortTask)
78
{
79
    if (m_state == Finished)
80
        return;
81

82
    if (type == IDBDatabaseBackendInterface::NormalTask)
83 84 85
        m_taskQueue.append(task);
    else
        m_preemptiveTaskQueue.append(task);
86

87 88 89
    if (abortTask)
        m_abortTaskQueue.prepend(abortTask);

90
    if (m_state == Unused)
91
        start();
92 93
    else if (m_state == Running && !m_taskTimer.isActive())
        m_taskTimer.startOneShot(0);
94 95
}

96
void IDBTransactionBackendImpl::abort()
97
{
98
    abort(IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error (unknown cause)"));
99 100
}

101
void IDBTransactionBackendImpl::abort(PassRefPtr<IDBDatabaseError> error)
102
{
103
    LOG(StorageAPI, "IDBTransactionBackendImpl::abort");
104
    if (m_state == Finished)
105 106
        return;

107 108
    bool wasRunning = m_state == Running;

109 110 111
    // The last reference to this object may be released while performing the
    // abort steps below. We therefore take a self reference to keep ourselves
    // alive while executing this method.
112
    Ref<IDBTransactionBackendImpl> protect(*this);
113

114
    m_state = Finished;
115
    m_taskTimer.stop();
116

117
    if (wasRunning)
118
        m_backingStoreTransaction->rollback();
119 120 121

    // Run the abort tasks, if any.
    while (!m_abortTaskQueue.isEmpty()) {
122
        OwnPtr<IDBOperation> task(m_abortTaskQueue.takeFirst());
123
        task->perform();
124 125
    }

126 127 128 129
    // Backing store resources (held via cursors) must be released before script callbacks
    // are fired, as the script callbacks may release references and allow the backing store
    // itself to be released, and order is critical.
    closeOpenCursors();
130
    m_backingStoreTransaction->resetTransaction();
131 132 133

    // Transactions must also be marked as completed before the front-end is notified, as
    // the transaction completion unblocks operations like closing connections.
134
    m_database->transactionCoordinator()->didFinishTransaction(this);
135
    ASSERT(!m_database->transactionCoordinator()->isActive(this));
136
    m_database->transactionFinished(this);
137 138

    if (m_callbacks)
139
        m_callbacks->onAbort(id(), error);
140

141 142
    m_database->transactionFinishedAndAbortFired(this);

143
    m_database = 0;
144 145
}

146
bool IDBTransactionBackendImpl::isTaskQueueEmpty() const
147 148 149 150
{
    return m_preemptiveTaskQueue.isEmpty() && m_taskQueue.isEmpty();
}

151
bool IDBTransactionBackendImpl::hasPendingTasks() const
152
{
153
    return m_pendingPreemptiveEvents || !isTaskQueueEmpty();
154 155
}

156
void IDBTransactionBackendImpl::registerOpenCursor(IDBCursorBackend* cursor)
157 158 159 160
{
    m_openCursors.add(cursor);
}

161
void IDBTransactionBackendImpl::unregisterOpenCursor(IDBCursorBackend* cursor)
162 163 164 165
{
    m_openCursors.remove(cursor);
}

166
void IDBTransactionBackendImpl::run()
167
{
168 169
    // TransactionCoordinator has started this transaction. Schedule a timer
    // to process the first task.
170
    ASSERT(m_state == StartPending || m_state == Running);
171
    ASSERT(!m_taskTimer.isActive());
172

173
    m_taskTimer.startOneShot(0);
174 175
}

176
void IDBTransactionBackendImpl::start()
177
{
178
    ASSERT(m_state == Unused);
179

180
    m_state = StartPending;
181
    m_database->transactionCoordinator()->didStartTransaction(this);
182
    m_database->transactionStarted(this);
183 184
}

185
void IDBTransactionBackendImpl::commit()
186
{
187
    LOG(StorageAPI, "IDBTransactionBackendImpl::commit");
188

189 190 191 192 193
    // In multiprocess ports, front-end may have requested a commit but an abort has already
    // been initiated asynchronously by the back-end.
    if (m_state == Finished)
        return;

194
    ASSERT(m_state == Unused || m_state == Running);
195
    m_commitPending = true;
196 197 198 199 200 201

    // Front-end has requested a commit, but there may be tasks like createIndex which
    // are considered synchronous by the front-end but are processed asynchronously.
    if (hasPendingTasks())
        return;

202 203 204
    // The last reference to this object may be released while performing the
    // commit steps below. We therefore take a self reference to keep ourselves
    // alive while executing this method.
205
    Ref<IDBTransactionBackendImpl> protect(*this);
206

207
    bool unused = m_state == Unused;
208
    m_state = Finished;
209

210
    bool committed = unused || m_backingStoreTransaction->commit();
211 212 213 214

    // Backing store resources (held via cursors) must be released before script callbacks
    // are fired, as the script callbacks may release references and allow the backing store
    // itself to be released, and order is critical.
215
    closeOpenCursors();
216
    m_backingStoreTransaction->resetTransaction();
217 218 219

    // Transactions must also be marked as completed before the front-end is notified, as
    // the transaction completion unblocks operations like closing connections.
220 221
    if (!unused)
        m_database->transactionCoordinator()->didFinishTransaction(this);
222 223
    m_database->transactionFinished(this);

224
    if (committed) {
225
        m_callbacks->onComplete(id());
226 227
        m_database->transactionFinishedAndCompleteFired(this);
    } else {
228
        m_callbacks->onAbort(id(), IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error committing transaction."));
229 230
        m_database->transactionFinishedAndAbortFired(this);
    }
231

232
    m_database = 0;
233 234
}

235
void IDBTransactionBackendImpl::taskTimerFired(Timer<IDBTransactionBackendImpl>*)
236
{
237
    LOG(StorageAPI, "IDBTransactionBackendImpl::taskTimerFired");
238
    ASSERT(!isTaskQueueEmpty());
239 240

    if (m_state == StartPending) {
241
        m_backingStoreTransaction->begin();
242
        m_state = Running;
243
    }
244

245 246 247
    // The last reference to this object may be released while performing the
    // tasks. Take take a self reference to keep this object alive so that
    // the loop termination conditions can be checked.
248
    Ref<IDBTransactionBackendImpl> protect(*this);
249

250 251
    TaskQueue* taskQueue = m_pendingPreemptiveEvents ? &m_preemptiveTaskQueue : &m_taskQueue;
    while (!taskQueue->isEmpty() && m_state != Finished) {
252
        ASSERT(m_state == Running);
253
        OwnPtr<IDBOperation> task(taskQueue->takeFirst());
254
        task->perform();
255 256 257

        // Event itself may change which queue should be processed next.
        taskQueue = m_pendingPreemptiveEvents ? &m_preemptiveTaskQueue : &m_taskQueue;
258
    }
259

260 261 262
    // If there are no pending tasks, we haven't already committed/aborted,
    // and the front-end requested a commit, it is now safe to do so.
    if (!hasPendingTasks() && m_state != Finished && m_commitPending)
263 264 265
        commit();
}

266
void IDBTransactionBackendImpl::closeOpenCursors()
267
{
268
    for (HashSet<IDBCursorBackend*>::iterator i = m_openCursors.begin(); i != m_openCursors.end(); ++i)
269 270 271 272
        (*i)->close();
    m_openCursors.clear();
}

273
void IDBTransactionBackendImpl::scheduleCreateObjectStoreOperation(const IDBObjectStoreMetadata& objectStoreMetadata)
274 275 276 277
{
    scheduleTask(CreateObjectStoreOperation::create(this, m_backingStore.get(), objectStoreMetadata), CreateObjectStoreAbortOperation::create(this, objectStoreMetadata.id));
}

278
void IDBTransactionBackendImpl::scheduleDeleteObjectStoreOperation(const IDBObjectStoreMetadata& objectStoreMetadata)
279 280 281 282
{
    scheduleTask(DeleteObjectStoreOperation::create(this, m_backingStore.get(), objectStoreMetadata), DeleteObjectStoreAbortOperation::create(this, objectStoreMetadata));
}

283
void IDBTransactionBackendImpl::scheduleVersionChangeOperation(int64_t transactionId, int64_t requestedVersion, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBDatabaseCallbacks> databaseCallbacks, const IDBDatabaseMetadata& metadata)
284
{
285
    scheduleTask(IDBDatabaseBackendImpl::VersionChangeOperation::create(this, transactionId, requestedVersion, callbacks, databaseCallbacks), IDBDatabaseBackendImpl::VersionChangeAbortOperation::create(this, String::number(metadata.version), metadata.version));
286 287
}

288
void IDBTransactionBackendImpl::scheduleCreateIndexOperation(int64_t objectStoreId, const IDBIndexMetadata& indexMetadata)
289 290 291 292
{
    scheduleTask(CreateIndexOperation::create(this, m_backingStore.get(), objectStoreId, indexMetadata), CreateIndexAbortOperation::create(this, objectStoreId, indexMetadata.id));
}

293
void IDBTransactionBackendImpl::scheduleDeleteIndexOperation(int64_t objectStoreId, const IDBIndexMetadata& indexMetadata)
294 295 296 297
{
    scheduleTask(DeleteIndexOperation::create(this, m_backingStore.get(), objectStoreId, indexMetadata), DeleteIndexAbortOperation::create(this, objectStoreId, indexMetadata));
}

298
void IDBTransactionBackendImpl::scheduleGetOperation(const IDBDatabaseMetadata& metadata, int64_t objectStoreId, int64_t indexId, PassRefPtr<IDBKeyRange> keyRange, IndexedDB::CursorType cursorType, PassRefPtr<IDBCallbacks> callbacks)
299 300 301 302
{
    scheduleTask(GetOperation::create(this, m_backingStore.get(), metadata, objectStoreId, indexId, keyRange, cursorType, callbacks));
}

303
void IDBTransactionBackendImpl::schedulePutOperation(const IDBObjectStoreMetadata& objectStoreMetadata, PassRefPtr<SharedBuffer> value, PassRefPtr<IDBKey> key, IDBDatabaseBackendInterface::PutMode putMode, PassRefPtr<IDBCallbacks> callbacks, const Vector<int64_t>& indexIds, const Vector<IndexKeys>& indexKeys)
304
{
305
    scheduleTask(PutOperation::create(this, m_backingStore.get(), database().id(), objectStoreMetadata, value, key, putMode, callbacks, indexIds, indexKeys));
306 307
}

308
void IDBTransactionBackendImpl::scheduleSetIndexesReadyOperation(size_t indexCount)
309 310 311 312
{
    scheduleTask(IDBDatabaseBackendInterface::PreemptiveTask, SetIndexesReadyOperation::create(this, indexCount));
}

313
void IDBTransactionBackendImpl::scheduleOpenCursorOperation(int64_t objectStoreId, int64_t indexId, PassRefPtr<IDBKeyRange> keyRange, IndexedDB::CursorDirection direction, IndexedDB::CursorType cursorType, IDBDatabaseBackendInterface::TaskType taskType, PassRefPtr<IDBCallbacks> callbacks)
314
{
315
    scheduleTask(OpenCursorOperation::create(this, m_backingStore.get(), database().id(), objectStoreId, indexId, keyRange, direction, cursorType, taskType, callbacks));
316 317
}

318
void IDBTransactionBackendImpl::scheduleCountOperation(int64_t objectStoreId, int64_t indexId, PassRefPtr<IDBKeyRange> keyRange, PassRefPtr<IDBCallbacks> callbacks)
319
{
320
    scheduleTask(CountOperation::create(this, m_backingStore.get(), database().id(), objectStoreId, indexId, keyRange, callbacks));
321 322
}

323
void IDBTransactionBackendImpl::scheduleDeleteRangeOperation(int64_t objectStoreId, PassRefPtr<IDBKeyRange> keyRange, PassRefPtr<IDBCallbacks> callbacks)
324
{
325
    scheduleTask(DeleteRangeOperation::create(this, m_backingStore.get(), database().id(), objectStoreId, keyRange, callbacks));
326 327
}

328
void IDBTransactionBackendImpl::scheduleClearOperation(int64_t objectStoreId, PassRefPtr<IDBCallbacks> callbacks)
329
{
330
    scheduleTask(ClearOperation::create(this, m_backingStore.get(), database().id(), objectStoreId, callbacks));
331 332
}

333
PassRefPtr<IDBCursorBackend> IDBTransactionBackendImpl::createCursorBackend(IDBBackingStoreCursorInterface& cursor, IndexedDB::CursorType cursorType, IDBDatabaseBackendInterface::TaskType taskType, int64_t objectStoreId)
334
{
335
    return m_database->factoryBackend().createCursorBackend(*this, cursor, cursorType, taskType, objectStoreId);
336
}
337

338 339
};

340
#endif // ENABLE(INDEXED_DATABASE)