- add sources.
[platform/framework/web/crosswalk.git] / src / content / browser / indexed_db / indexed_db_transaction.cc
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.
4
5 #include "content/browser/indexed_db/indexed_db_transaction.h"
6
7 #include "base/bind.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"
18
19 namespace content {
20
21 IndexedDBTransaction::TaskQueue::TaskQueue() {}
22 IndexedDBTransaction::TaskQueue::~TaskQueue() { clear(); }
23
24 void IndexedDBTransaction::TaskQueue::clear() {
25   while (!queue_.empty())
26     queue_.pop();
27 }
28
29 IndexedDBTransaction::Operation IndexedDBTransaction::TaskQueue::pop() {
30   DCHECK(!queue_.empty());
31   Operation task(queue_.front());
32   queue_.pop();
33   return task;
34 }
35
36 IndexedDBTransaction::TaskStack::TaskStack() {}
37 IndexedDBTransaction::TaskStack::~TaskStack() { clear(); }
38
39 void IndexedDBTransaction::TaskStack::clear() {
40   while (!stack_.empty())
41     stack_.pop();
42 }
43
44 IndexedDBTransaction::Operation IndexedDBTransaction::TaskStack::pop() {
45   DCHECK(!stack_.empty());
46   Operation task(stack_.top());
47   stack_.pop();
48   return task;
49 }
50
51 IndexedDBTransaction::IndexedDBTransaction(
52     int64 id,
53     scoped_refptr<IndexedDBDatabaseCallbacks> callbacks,
54     const std::set<int64>& object_store_ids,
55     indexed_db::TransactionMode mode,
56     IndexedDBDatabase* database)
57     : id_(id),
58       object_store_ids_(object_store_ids),
59       mode_(mode),
60       state_(UNUSED),
61       commit_pending_(false),
62       callbacks_(callbacks),
63       database_(database),
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()),
69       tasks_scheduled_(0),
70       tasks_completed_(0) {
71   database_->transaction_coordinator().DidCreateTransaction(this);
72 }
73
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());
81 }
82
83 void IndexedDBTransaction::ScheduleTask(Operation task, Operation abort_task) {
84   if (state_ == FINISHED)
85     return;
86   task_queue_.push(task);
87   ++tasks_scheduled_;
88   abort_task_stack_.push(abort_task);
89   EnsureTasksRunning();
90 }
91
92 void IndexedDBTransaction::ScheduleTask(IndexedDBDatabase::TaskType type,
93                                         Operation task) {
94   if (state_ == FINISHED)
95     return;
96
97   if (type == IndexedDBDatabase::NORMAL_TASK) {
98     task_queue_.push(task);
99     ++tasks_scheduled_;
100   } else {
101     preemptive_task_queue_.push(task);
102   }
103   EnsureTasksRunning();
104 }
105
106 void IndexedDBTransaction::EnsureTasksRunning() {
107   if (state_ == UNUSED) {
108     Start();
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));
113   }
114 }
115
116 void IndexedDBTransaction::Abort() {
117   Abort(IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError,
118                                "Internal error (unknown cause)"));
119 }
120
121 void IndexedDBTransaction::Abort(const IndexedDBDatabaseError& error) {
122   IDB_TRACE("IndexedDBTransaction::Abort");
123   if (state_ == FINISHED)
124     return;
125
126   bool was_running = state_ == RUNNING;
127
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);
132
133   state_ = FINISHED;
134   should_process_queue_ = false;
135
136   if (was_running)
137     transaction_.Rollback();
138
139   // Run the abort tasks, if any.
140   while (!abort_task_stack_.empty()) {
141     Operation task(abort_task_stack_.pop());
142     task.Run(0);
143   }
144   preemptive_task_queue_.clear();
145   task_queue_.clear();
146
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.
151   CloseOpenCursors();
152   transaction_.Reset();
153
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);
158 #ifndef NDEBUG
159   DCHECK(!database_->transaction_coordinator().IsActive(this));
160 #endif
161   database_->TransactionFinished(this);
162
163   if (callbacks_.get())
164     callbacks_->OnAbort(id_, error);
165
166   database_->TransactionFinishedAndAbortFired(this);
167
168   database_ = NULL;
169 }
170
171 bool IndexedDBTransaction::IsTaskQueueEmpty() const {
172   return preemptive_task_queue_.empty() && task_queue_.empty();
173 }
174
175 bool IndexedDBTransaction::HasPendingTasks() const {
176   return pending_preemptive_events_ || !IsTaskQueueEmpty();
177 }
178
179 void IndexedDBTransaction::RegisterOpenCursor(IndexedDBCursor* cursor) {
180   open_cursors_.insert(cursor);
181 }
182
183 void IndexedDBTransaction::UnregisterOpenCursor(IndexedDBCursor* cursor) {
184   open_cursors_.erase(cursor);
185 }
186
187 void IndexedDBTransaction::Run() {
188   // TransactionCoordinator has started this transaction.
189   DCHECK(state_ == START_PENDING || state_ == RUNNING);
190   DCHECK(!should_process_queue_);
191
192   start_time_ = base::Time::Now();
193   should_process_queue_ = true;
194   base::MessageLoop::current()->PostTask(
195       FROM_HERE, base::Bind(&IndexedDBTransaction::ProcessTaskQueue, this));
196 }
197
198 void IndexedDBTransaction::Start() {
199   DCHECK_EQ(state_, UNUSED);
200
201   state_ = START_PENDING;
202   database_->transaction_coordinator().DidStartTransaction(this);
203   database_->TransactionStarted(this);
204 }
205
206 void IndexedDBTransaction::Commit() {
207   IDB_TRACE("IndexedDBTransaction::Commit");
208
209   // In multiprocess ports, front-end may have requested a commit but
210   // an abort has already been initiated asynchronously by the
211   // back-end.
212   if (state_ == FINISHED)
213     return;
214
215   DCHECK(state_ == UNUSED || state_ == RUNNING);
216   commit_pending_ = true;
217
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())
222     return;
223
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);
228
229   // TODO(jsbell): Run abort tasks if commit fails? http://crbug.com/241843
230   abort_task_stack_.clear();
231
232   bool unused = state_ == UNUSED;
233   state_ = FINISHED;
234
235   bool committed = unused || transaction_.Commit();
236
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.
241   CloseOpenCursors();
242   transaction_.Reset();
243
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);
249
250   if (committed) {
251     callbacks_->OnComplete(id_);
252     database_->TransactionFinishedAndCompleteFired(this);
253   } else {
254     callbacks_->OnAbort(
255         id_,
256         IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError,
257                                "Internal error committing transaction."));
258     database_->TransactionFinishedAndAbortFired(this);
259     database_->TransactionCommitFailed();
260   }
261
262   database_ = NULL;
263 }
264
265 void IndexedDBTransaction::ProcessTaskQueue() {
266   IDB_TRACE("IndexedDBTransaction::ProcessTaskQueue");
267
268   // May have been aborted.
269   if (!should_process_queue_)
270     return;
271
272   DCHECK(!IsTaskQueueEmpty());
273   should_process_queue_ = false;
274
275   if (state_ == START_PENDING) {
276     transaction_.Begin();
277     state_ = RUNNING;
278   }
279
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);
284
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());
290     task.Run(this);
291     if (!pending_preemptive_events_) {
292       DCHECK(tasks_completed_ < tasks_scheduled_);
293       ++tasks_completed_;
294     }
295
296     // Event itself may change which queue should be processed next.
297     task_queue =
298         pending_preemptive_events_ ? &preemptive_task_queue_ : &task_queue_;
299   }
300
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_)
304     Commit();
305 }
306
307 void IndexedDBTransaction::CloseOpenCursors() {
308   for (std::set<IndexedDBCursor*>::iterator i = open_cursors_.begin();
309        i != open_cursors_.end();
310        ++i)
311     (*i)->Close();
312   open_cursors_.clear();
313 }
314
315 }  // namespace content