1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/indexed_db/indexed_db_transaction.h"
8 #include "base/logging.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "content/browser/indexed_db/indexed_db_backing_store.h"
12 #include "content/browser/indexed_db/indexed_db_cursor.h"
13 #include "content/browser/indexed_db/indexed_db_database.h"
14 #include "content/browser/indexed_db/indexed_db_database_callbacks.h"
15 #include "content/browser/indexed_db/indexed_db_tracing.h"
16 #include "content/browser/indexed_db/indexed_db_transaction_coordinator.h"
17 #include "third_party/WebKit/public/platform/WebIDBDatabaseException.h"
21 IndexedDBTransaction::TaskQueue::TaskQueue() {}
22 IndexedDBTransaction::TaskQueue::~TaskQueue() { clear(); }
24 void IndexedDBTransaction::TaskQueue::clear() {
25 while (!queue_.empty())
29 IndexedDBTransaction::Operation IndexedDBTransaction::TaskQueue::pop() {
30 DCHECK(!queue_.empty());
31 Operation task(queue_.front());
36 IndexedDBTransaction::TaskStack::TaskStack() {}
37 IndexedDBTransaction::TaskStack::~TaskStack() { clear(); }
39 void IndexedDBTransaction::TaskStack::clear() {
40 while (!stack_.empty())
44 IndexedDBTransaction::Operation IndexedDBTransaction::TaskStack::pop() {
45 DCHECK(!stack_.empty());
46 Operation task(stack_.top());
51 IndexedDBTransaction::IndexedDBTransaction(
53 scoped_refptr<IndexedDBDatabaseCallbacks> callbacks,
54 const std::set<int64>& object_store_ids,
55 indexed_db::TransactionMode mode,
56 IndexedDBDatabase* database)
58 object_store_ids_(object_store_ids),
61 commit_pending_(false),
62 callbacks_(callbacks),
64 transaction_(database->backing_store()),
65 should_process_queue_(false),
66 pending_preemptive_events_(0),
67 queue_status_(CREATED),
68 creation_time_(base::Time::Now()),
71 database_->transaction_coordinator().DidCreateTransaction(this);
74 IndexedDBTransaction::~IndexedDBTransaction() {
75 // It shouldn't be possible for this object to get deleted until it's either
76 // complete or aborted.
77 DCHECK_EQ(state_, FINISHED);
78 DCHECK(preemptive_task_queue_.empty());
79 DCHECK(task_queue_.empty());
80 DCHECK(abort_task_stack_.empty());
83 void IndexedDBTransaction::ScheduleTask(Operation task, Operation abort_task) {
84 if (state_ == FINISHED)
86 task_queue_.push(task);
88 abort_task_stack_.push(abort_task);
92 void IndexedDBTransaction::ScheduleTask(IndexedDBDatabase::TaskType type,
94 if (state_ == FINISHED)
97 if (type == IndexedDBDatabase::NORMAL_TASK) {
98 task_queue_.push(task);
101 preemptive_task_queue_.push(task);
103 EnsureTasksRunning();
106 void IndexedDBTransaction::EnsureTasksRunning() {
107 if (state_ == UNUSED) {
109 } else if (state_ == RUNNING && !should_process_queue_) {
110 should_process_queue_ = true;
111 base::MessageLoop::current()->PostTask(
112 FROM_HERE, base::Bind(&IndexedDBTransaction::ProcessTaskQueue, this));
116 void IndexedDBTransaction::Abort() {
117 Abort(IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError,
118 "Internal error (unknown cause)"));
121 void IndexedDBTransaction::Abort(const IndexedDBDatabaseError& error) {
122 IDB_TRACE("IndexedDBTransaction::Abort");
123 if (state_ == FINISHED)
126 bool was_running = state_ == RUNNING;
128 // The last reference to this object may be released while performing the
129 // abort steps below. We therefore take a self reference to keep ourselves
130 // alive while executing this method.
131 scoped_refptr<IndexedDBTransaction> protect(this);
134 should_process_queue_ = false;
137 transaction_.Rollback();
139 // Run the abort tasks, if any.
140 while (!abort_task_stack_.empty()) {
141 Operation task(abort_task_stack_.pop());
144 preemptive_task_queue_.clear();
147 // Backing store resources (held via cursors) must be released
148 // before script callbacks are fired, as the script callbacks may
149 // release references and allow the backing store itself to be
150 // released, and order is critical.
152 transaction_.Reset();
154 // Transactions must also be marked as completed before the
155 // front-end is notified, as the transaction completion unblocks
156 // operations like closing connections.
157 database_->transaction_coordinator().DidFinishTransaction(this);
159 DCHECK(!database_->transaction_coordinator().IsActive(this));
161 database_->TransactionFinished(this);
163 if (callbacks_.get())
164 callbacks_->OnAbort(id_, error);
166 database_->TransactionFinishedAndAbortFired(this);
171 bool IndexedDBTransaction::IsTaskQueueEmpty() const {
172 return preemptive_task_queue_.empty() && task_queue_.empty();
175 bool IndexedDBTransaction::HasPendingTasks() const {
176 return pending_preemptive_events_ || !IsTaskQueueEmpty();
179 void IndexedDBTransaction::RegisterOpenCursor(IndexedDBCursor* cursor) {
180 open_cursors_.insert(cursor);
183 void IndexedDBTransaction::UnregisterOpenCursor(IndexedDBCursor* cursor) {
184 open_cursors_.erase(cursor);
187 void IndexedDBTransaction::Run() {
188 // TransactionCoordinator has started this transaction.
189 DCHECK(state_ == START_PENDING || state_ == RUNNING);
190 DCHECK(!should_process_queue_);
192 start_time_ = base::Time::Now();
193 should_process_queue_ = true;
194 base::MessageLoop::current()->PostTask(
195 FROM_HERE, base::Bind(&IndexedDBTransaction::ProcessTaskQueue, this));
198 void IndexedDBTransaction::Start() {
199 DCHECK_EQ(state_, UNUSED);
201 state_ = START_PENDING;
202 database_->transaction_coordinator().DidStartTransaction(this);
203 database_->TransactionStarted(this);
206 void IndexedDBTransaction::Commit() {
207 IDB_TRACE("IndexedDBTransaction::Commit");
209 // In multiprocess ports, front-end may have requested a commit but
210 // an abort has already been initiated asynchronously by the
212 if (state_ == FINISHED)
215 DCHECK(state_ == UNUSED || state_ == RUNNING);
216 commit_pending_ = true;
218 // Front-end has requested a commit, but there may be tasks like
219 // create_index which are considered synchronous by the front-end
220 // but are processed asynchronously.
221 if (HasPendingTasks())
224 // The last reference to this object may be released while performing the
225 // commit steps below. We therefore take a self reference to keep ourselves
226 // alive while executing this method.
227 scoped_refptr<IndexedDBTransaction> protect(this);
229 // TODO(jsbell): Run abort tasks if commit fails? http://crbug.com/241843
230 abort_task_stack_.clear();
232 bool unused = state_ == UNUSED;
235 bool committed = unused || transaction_.Commit();
237 // Backing store resources (held via cursors) must be released
238 // before script callbacks are fired, as the script callbacks may
239 // release references and allow the backing store itself to be
240 // released, and order is critical.
242 transaction_.Reset();
244 // Transactions must also be marked as completed before the
245 // front-end is notified, as the transaction completion unblocks
246 // operations like closing connections.
247 database_->transaction_coordinator().DidFinishTransaction(this);
248 database_->TransactionFinished(this);
251 callbacks_->OnComplete(id_);
252 database_->TransactionFinishedAndCompleteFired(this);
256 IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError,
257 "Internal error committing transaction."));
258 database_->TransactionFinishedAndAbortFired(this);
259 database_->TransactionCommitFailed();
265 void IndexedDBTransaction::ProcessTaskQueue() {
266 IDB_TRACE("IndexedDBTransaction::ProcessTaskQueue");
268 // May have been aborted.
269 if (!should_process_queue_)
272 DCHECK(!IsTaskQueueEmpty());
273 should_process_queue_ = false;
275 if (state_ == START_PENDING) {
276 transaction_.Begin();
280 // The last reference to this object may be released while performing the
281 // tasks. Take take a self reference to keep this object alive so that
282 // the loop termination conditions can be checked.
283 scoped_refptr<IndexedDBTransaction> protect(this);
285 TaskQueue* task_queue =
286 pending_preemptive_events_ ? &preemptive_task_queue_ : &task_queue_;
287 while (!task_queue->empty() && state_ != FINISHED) {
288 DCHECK_EQ(state_, RUNNING);
289 Operation task(task_queue->pop());
291 if (!pending_preemptive_events_) {
292 DCHECK(tasks_completed_ < tasks_scheduled_);
296 // Event itself may change which queue should be processed next.
298 pending_preemptive_events_ ? &preemptive_task_queue_ : &task_queue_;
301 // If there are no pending tasks, we haven't already committed/aborted,
302 // and the front-end requested a commit, it is now safe to do so.
303 if (!HasPendingTasks() && state_ != FINISHED && commit_pending_)
307 void IndexedDBTransaction::CloseOpenCursors() {
308 for (std::set<IndexedDBCursor*>::iterator i = open_cursors_.begin();
309 i != open_cursors_.end();
312 open_cursors_.clear();
315 } // namespace content