2 * Copyright (C) 2010 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
14 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
18 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #include "IDBTransactionBackendImpl.h"
29 #if ENABLE(INDEXED_DATABASE)
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"
43 PassRefPtr<IDBTransactionBackendImpl> IDBTransactionBackendImpl::create(IDBDatabaseBackendImpl* databaseBackend, int64_t id, PassRefPtr<IDBDatabaseCallbacks> callbacks, const Vector<int64_t>& objectStoreIds, IndexedDB::TransactionMode mode)
45 HashSet<int64_t> objectStoreHashSet;
46 for (size_t i = 0; i < objectStoreIds.size(); ++i)
47 objectStoreHashSet.add(objectStoreIds[i]);
49 return adoptRef(new IDBTransactionBackendImpl(databaseBackend, id, callbacks, objectStoreHashSet, mode));
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)
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())
65 // We pass a reference of this object before it can be adopted.
66 relaxAdoptionRequirement();
68 m_database->transactionCoordinator()->didCreateTransaction(this);
71 IDBTransactionBackendImpl::~IDBTransactionBackendImpl()
73 // It shouldn't be possible for this object to get deleted until it's either complete or aborted.
74 ASSERT(m_state == Finished);
77 void IDBTransactionBackendImpl::scheduleTask(IDBDatabaseBackendInterface::TaskType type, PassOwnPtr<IDBOperation> task, PassOwnPtr<IDBOperation> abortTask)
79 if (m_state == Finished)
82 if (type == IDBDatabaseBackendInterface::NormalTask)
83 m_taskQueue.append(task);
85 m_preemptiveTaskQueue.append(task);
88 m_abortTaskQueue.prepend(abortTask);
90 if (m_state == Unused)
92 else if (m_state == Running && !m_taskTimer.isActive())
93 m_taskTimer.startOneShot(0);
96 void IDBTransactionBackendImpl::abort()
98 abort(IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error (unknown cause)"));
101 void IDBTransactionBackendImpl::abort(PassRefPtr<IDBDatabaseError> error)
103 LOG(StorageAPI, "IDBTransactionBackendImpl::abort");
104 if (m_state == Finished)
107 bool wasRunning = m_state == Running;
109 // The last reference to this object may be released while performing the
110 // abort steps below. We therefore take a self reference to keep ourselves
111 // alive while executing this method.
112 Ref<IDBTransactionBackendImpl> protect(*this);
118 m_backingStoreTransaction->rollback();
120 // Run the abort tasks, if any.
121 while (!m_abortTaskQueue.isEmpty()) {
122 OwnPtr<IDBOperation> task(m_abortTaskQueue.takeFirst());
126 // Backing store resources (held via cursors) must be released before script callbacks
127 // are fired, as the script callbacks may release references and allow the backing store
128 // itself to be released, and order is critical.
130 m_backingStoreTransaction->resetTransaction();
132 // Transactions must also be marked as completed before the front-end is notified, as
133 // 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);
139 m_callbacks->onAbort(id(), error);
141 m_database->transactionFinishedAndAbortFired(this);
146 bool IDBTransactionBackendImpl::isTaskQueueEmpty() const
148 return m_preemptiveTaskQueue.isEmpty() && m_taskQueue.isEmpty();
151 bool IDBTransactionBackendImpl::hasPendingTasks() const
153 return m_pendingPreemptiveEvents || !isTaskQueueEmpty();
156 void IDBTransactionBackendImpl::registerOpenCursor(IDBCursorBackend* cursor)
158 m_openCursors.add(cursor);
161 void IDBTransactionBackendImpl::unregisterOpenCursor(IDBCursorBackend* cursor)
163 m_openCursors.remove(cursor);
166 void IDBTransactionBackendImpl::run()
168 // TransactionCoordinator has started this transaction. Schedule a timer
169 // to process the first task.
170 ASSERT(m_state == StartPending || m_state == Running);
171 ASSERT(!m_taskTimer.isActive());
173 m_taskTimer.startOneShot(0);
176 void IDBTransactionBackendImpl::start()
178 ASSERT(m_state == Unused);
180 m_state = StartPending;
181 m_database->transactionCoordinator()->didStartTransaction(this);
182 m_database->transactionStarted(this);
185 void IDBTransactionBackendImpl::commit()
187 LOG(StorageAPI, "IDBTransactionBackendImpl::commit");
189 // In multiprocess ports, front-end may have requested a commit but an abort has already
190 // been initiated asynchronously by the back-end.
191 if (m_state == Finished)
194 ASSERT(m_state == Unused || m_state == Running);
195 m_commitPending = true;
197 // Front-end has requested a commit, but there may be tasks like createIndex which
198 // are considered synchronous by the front-end but are processed asynchronously.
199 if (hasPendingTasks())
202 // The last reference to this object may be released while performing the
203 // commit steps below. We therefore take a self reference to keep ourselves
204 // alive while executing this method.
205 Ref<IDBTransactionBackendImpl> protect(*this);
207 bool unused = m_state == Unused;
210 bool committed = unused || m_backingStoreTransaction->commit();
212 // Backing store resources (held via cursors) must be released before script callbacks
213 // are fired, as the script callbacks may release references and allow the backing store
214 // itself to be released, and order is critical.
216 m_backingStoreTransaction->resetTransaction();
218 // Transactions must also be marked as completed before the front-end is notified, as
219 // the transaction completion unblocks operations like closing connections.
221 m_database->transactionCoordinator()->didFinishTransaction(this);
222 m_database->transactionFinished(this);
225 m_callbacks->onComplete(id());
226 m_database->transactionFinishedAndCompleteFired(this);
228 m_callbacks->onAbort(id(), IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error committing transaction."));
229 m_database->transactionFinishedAndAbortFired(this);
235 void IDBTransactionBackendImpl::taskTimerFired(Timer<IDBTransactionBackendImpl>*)
237 LOG(StorageAPI, "IDBTransactionBackendImpl::taskTimerFired");
238 ASSERT(!isTaskQueueEmpty());
240 if (m_state == StartPending) {
241 m_backingStoreTransaction->begin();
245 // The last reference to this object may be released while performing the
246 // tasks. Take take a self reference to keep this object alive so that
247 // the loop termination conditions can be checked.
248 Ref<IDBTransactionBackendImpl> protect(*this);
250 TaskQueue* taskQueue = m_pendingPreemptiveEvents ? &m_preemptiveTaskQueue : &m_taskQueue;
251 while (!taskQueue->isEmpty() && m_state != Finished) {
252 ASSERT(m_state == Running);
253 OwnPtr<IDBOperation> task(taskQueue->takeFirst());
256 // Event itself may change which queue should be processed next.
257 taskQueue = m_pendingPreemptiveEvents ? &m_preemptiveTaskQueue : &m_taskQueue;
260 // If there are no pending tasks, we haven't already committed/aborted,
261 // and the front-end requested a commit, it is now safe to do so.
262 if (!hasPendingTasks() && m_state != Finished && m_commitPending)
266 void IDBTransactionBackendImpl::closeOpenCursors()
268 for (HashSet<IDBCursorBackend*>::iterator i = m_openCursors.begin(); i != m_openCursors.end(); ++i)
270 m_openCursors.clear();
273 void IDBTransactionBackendImpl::scheduleCreateObjectStoreOperation(const IDBObjectStoreMetadata& objectStoreMetadata)
275 scheduleTask(CreateObjectStoreOperation::create(this, m_backingStore.get(), objectStoreMetadata), CreateObjectStoreAbortOperation::create(this, objectStoreMetadata.id));
278 void IDBTransactionBackendImpl::scheduleDeleteObjectStoreOperation(const IDBObjectStoreMetadata& objectStoreMetadata)
280 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)
285 scheduleTask(IDBDatabaseBackendImpl::VersionChangeOperation::create(this, transactionId, requestedVersion, callbacks, databaseCallbacks), IDBDatabaseBackendImpl::VersionChangeAbortOperation::create(this, String::number(metadata.version), metadata.version));
288 void IDBTransactionBackendImpl::scheduleCreateIndexOperation(int64_t objectStoreId, const IDBIndexMetadata& indexMetadata)
290 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)
295 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)
300 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)
305 scheduleTask(PutOperation::create(this, m_backingStore.get(), database().id(), objectStoreMetadata, value, key, putMode, callbacks, indexIds, indexKeys));
308 void IDBTransactionBackendImpl::scheduleSetIndexesReadyOperation(size_t indexCount)
310 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)
315 scheduleTask(OpenCursorOperation::create(this, m_backingStore.get(), database().id(), objectStoreId, indexId, keyRange, direction, cursorType, taskType, callbacks));
318 void IDBTransactionBackendImpl::scheduleCountOperation(int64_t objectStoreId, int64_t indexId, PassRefPtr<IDBKeyRange> keyRange, PassRefPtr<IDBCallbacks> callbacks)
320 scheduleTask(CountOperation::create(this, m_backingStore.get(), database().id(), objectStoreId, indexId, keyRange, callbacks));
323 void IDBTransactionBackendImpl::scheduleDeleteRangeOperation(int64_t objectStoreId, PassRefPtr<IDBKeyRange> keyRange, PassRefPtr<IDBCallbacks> callbacks)
325 scheduleTask(DeleteRangeOperation::create(this, m_backingStore.get(), database().id(), objectStoreId, keyRange, callbacks));
328 void IDBTransactionBackendImpl::scheduleClearOperation(int64_t objectStoreId, PassRefPtr<IDBCallbacks> callbacks)
330 scheduleTask(ClearOperation::create(this, m_backingStore.get(), database().id(), objectStoreId, callbacks));
333 PassRefPtr<IDBCursorBackend> IDBTransactionBackendImpl::createCursorBackend(IDBBackingStoreCursorInterface& cursor, IndexedDB::CursorType cursorType, IDBDatabaseBackendInterface::TaskType taskType, int64_t objectStoreId)
335 return m_database->factoryBackend().createCursorBackend(*this, cursor, cursorType, taskType, objectStoreId);
340 #endif // ENABLE(INDEXED_DATABASE)