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 "IDBBackingStore.h"
32
#include "IDBCursorBackendImpl.h"
33
#include "IDBDatabaseBackendImpl.h"
34
#include "IDBDatabaseException.h"
35
#include "IDBObjectStoreBackendImpl.h"
36
#include "IDBTracing.h"
37
#include "IDBTransactionCoordinator.h"
38
#include "ScriptExecutionContext.h"
42
PassRefPtr<IDBTransactionBackendImpl> IDBTransactionBackendImpl::create(int64_t id, const Vector<int64_t>& objectStoreIds, unsigned short mode, IDBDatabaseBackendImpl* database)
44
return adoptRef(new IDBTransactionBackendImpl(id, objectStoreIds, mode, database));
47
IDBTransactionBackendImpl::IDBTransactionBackendImpl(int64_t id, const Vector<int64_t>& objectStoreIds, unsigned short mode, IDBDatabaseBackendImpl* database)
49
, m_objectStoreIds(objectStoreIds)
52
, m_commitPending(false)
53
, m_database(database)
54
, m_transaction(database->backingStore().get())
55
, m_taskTimer(this, &IDBTransactionBackendImpl::taskTimerFired)
56
, m_pendingPreemptiveEvents(0)
58
m_database->transactionCoordinator()->didCreateTransaction(this);
61
IDBTransactionBackendImpl::~IDBTransactionBackendImpl()
63
// It shouldn't be possible for this object to get deleted until it's either complete or aborted.
64
ASSERT(m_state == Finished);
67
PassRefPtr<IDBObjectStoreBackendInterface> IDBTransactionBackendImpl::objectStore(int64_t id, ExceptionCode& ec)
69
if (m_state == Finished) {
70
ec = IDBDatabaseException::IDB_INVALID_STATE_ERR;
74
RefPtr<IDBObjectStoreBackendImpl> objectStore = m_database->objectStore(id);
76
return objectStore.release();
79
bool IDBTransactionBackendImpl::scheduleTask(TaskType type, PassOwnPtr<ScriptExecutionContext::Task> task, PassOwnPtr<ScriptExecutionContext::Task> abortTask)
81
if (m_state == Finished)
84
if (type == NormalTask)
85
m_taskQueue.append(task);
87
m_preemptiveTaskQueue.append(task);
90
m_abortTaskQueue.prepend(abortTask);
92
if (m_state == Unused)
94
else if (m_state == Running && !m_taskTimer.isActive())
95
m_taskTimer.startOneShot(0);
100
void IDBTransactionBackendImpl::abort()
102
abort(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Internal error."));
105
void IDBTransactionBackendImpl::abort(PassRefPtr<IDBDatabaseError> error)
107
IDB_TRACE("IDBTransactionBackendImpl::abort");
108
if (m_state == Finished)
111
bool wasRunning = m_state == Running;
113
// The last reference to this object may be released while performing the
114
// abort steps below. We therefore take a self reference to keep ourselves
115
// alive while executing this method.
116
RefPtr<IDBTransactionBackendImpl> protect(this);
122
m_transaction.rollback();
124
// Run the abort tasks, if any.
125
while (!m_abortTaskQueue.isEmpty()) {
126
OwnPtr<ScriptExecutionContext::Task> task(m_abortTaskQueue.takeFirst());
127
task->performTask(0);
130
// Backing store resources (held via cursors) must be released before script callbacks
131
// are fired, as the script callbacks may release references and allow the backing store
132
// itself to be released, and order is critical.
134
m_transaction.reset();
136
// Transactions must also be marked as completed before the front-end is notified, as
137
// the transaction completion unblocks operations like closing connections.
138
m_database->transactionCoordinator()->didFinishTransaction(this);
139
ASSERT(!m_database->transactionCoordinator()->isActive(this));
140
m_database->transactionFinished(this);
143
m_callbacks->onAbort(error);
145
m_database->transactionFinishedAndAbortFired(this);
150
bool IDBTransactionBackendImpl::isTaskQueueEmpty() const
152
return m_preemptiveTaskQueue.isEmpty() && m_taskQueue.isEmpty();
155
bool IDBTransactionBackendImpl::hasPendingTasks() const
157
return m_pendingPreemptiveEvents || !isTaskQueueEmpty();
160
void IDBTransactionBackendImpl::registerOpenCursor(IDBCursorBackendImpl* cursor)
162
m_openCursors.add(cursor);
165
void IDBTransactionBackendImpl::unregisterOpenCursor(IDBCursorBackendImpl* cursor)
167
m_openCursors.remove(cursor);
170
void IDBTransactionBackendImpl::run()
172
// TransactionCoordinator has started this transaction. Schedule a timer
173
// to process the first task.
174
ASSERT(m_state == StartPending || m_state == Running);
175
ASSERT(!m_taskTimer.isActive());
177
m_taskTimer.startOneShot(0);
180
void IDBTransactionBackendImpl::start()
182
ASSERT(m_state == Unused);
184
m_state = StartPending;
185
m_database->transactionCoordinator()->didStartTransaction(this);
186
m_database->transactionStarted(this);
189
void IDBTransactionBackendImpl::commit()
191
IDB_TRACE("IDBTransactionBackendImpl::commit");
193
// In multiprocess ports, front-end may have requested a commit but an abort has already
194
// been initiated asynchronously by the back-end.
195
if (m_state == Finished)
198
ASSERT(m_state == Unused || m_state == Running);
199
m_commitPending = true;
201
// Front-end has requested a commit, but there may be tasks like createIndex which
202
// are considered synchronous by the front-end but are processed asynchronously.
203
if (hasPendingTasks())
206
// The last reference to this object may be released while performing the
207
// commit steps below. We therefore take a self reference to keep ourselves
208
// alive while executing this method.
209
RefPtr<IDBTransactionBackendImpl> protect(this);
211
bool unused = m_state == Unused;
214
bool committed = unused || m_transaction.commit();
216
// Backing store resources (held via cursors) must be released before script callbacks
217
// are fired, as the script callbacks may release references and allow the backing store
218
// itself to be released, and order is critical.
220
m_transaction.reset();
222
// Transactions must also be marked as completed before the front-end is notified, as
223
// the transaction completion unblocks operations like closing connections.
225
m_database->transactionCoordinator()->didFinishTransaction(this);
226
m_database->transactionFinished(this);
229
m_callbacks->onComplete();
230
m_database->transactionFinishedAndCompleteFired(this);
232
m_callbacks->onAbort(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Internal error."));
233
m_database->transactionFinishedAndAbortFired(this);
239
void IDBTransactionBackendImpl::taskTimerFired(Timer<IDBTransactionBackendImpl>*)
241
IDB_TRACE("IDBTransactionBackendImpl::taskTimerFired");
242
ASSERT(!isTaskQueueEmpty());
244
if (m_state == StartPending) {
245
m_transaction.begin();
249
// The last reference to this object may be released while performing the
250
// tasks. Take take a self reference to keep this object alive so that
251
// the loop termination conditions can be checked.
252
RefPtr<IDBTransactionBackendImpl> protect(this);
254
TaskQueue* taskQueue = m_pendingPreemptiveEvents ? &m_preemptiveTaskQueue : &m_taskQueue;
255
while (!taskQueue->isEmpty() && m_state != Finished) {
256
ASSERT(m_state == Running);
257
OwnPtr<ScriptExecutionContext::Task> task(taskQueue->takeFirst());
258
task->performTask(0);
260
// Event itself may change which queue should be processed next.
261
taskQueue = m_pendingPreemptiveEvents ? &m_preemptiveTaskQueue : &m_taskQueue;
264
// If there are no pending tasks, we haven't already committed/aborted,
265
// and the front-end requested a commit, it is now safe to do so.
266
if (!hasPendingTasks() && m_state != Finished && m_commitPending)
270
void IDBTransactionBackendImpl::closeOpenCursors()
272
for (HashSet<IDBCursorBackendImpl*>::iterator i = m_openCursors.begin(); i != m_openCursors.end(); ++i)
274
m_openCursors.clear();
279
#endif // ENABLE(INDEXED_DATABASE)