Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(62)

Side by Side Diff: content/browser/indexed_db/indexed_db_transaction.cc

Issue 15564008: Migrate the IndexedDB backend from Blink to Chromium (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Accessor naming, use LevelDBSlice ctor explicitly Created 7 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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/logging.h"
8 #include "base/utf_string_conversions.h"
9 #include "content/browser/indexed_db/indexed_db_backing_store.h"
10 #include "content/browser/indexed_db/indexed_db_cursor_impl.h"
11 #include "content/browser/indexed_db/indexed_db_database_callbacks_wrapper.h"
12 #include "content/browser/indexed_db/indexed_db_database_impl.h"
13 #include "content/browser/indexed_db/indexed_db_tracing.h"
14 #include "content/browser/indexed_db/indexed_db_transaction_coordinator.h"
15 #include "third_party/WebKit/Source/Platform/chromium/public/WebIDBDatabaseExcep tion.h"
16
17 namespace content {
18
19 IndexedDBTransaction::TaskQueue::TaskQueue() {}
20 IndexedDBTransaction::TaskQueue::~TaskQueue() { clear(); }
21
22 void IndexedDBTransaction::TaskQueue::clear() {
23 while (!queue_.empty())
24 scoped_ptr<Operation> task(pop());
25 }
26
27 scoped_ptr<IndexedDBTransaction::Operation>
28 IndexedDBTransaction::TaskQueue::pop() {
29 DCHECK(!queue_.empty());
30 scoped_ptr<Operation> task(queue_.front());
31 queue_.pop();
32 return task.Pass();
33 }
34
35 IndexedDBTransaction::TaskStack::TaskStack() {}
36 IndexedDBTransaction::TaskStack::~TaskStack() { clear(); }
37
38 void IndexedDBTransaction::TaskStack::clear() {
39 while (!stack_.empty())
40 scoped_ptr<Operation> task(pop());
41 }
42
43 scoped_ptr<IndexedDBTransaction::Operation>
44 IndexedDBTransaction::TaskStack::pop() {
45 DCHECK(!stack_.empty());
46 scoped_ptr<Operation> task(stack_.top());
47 stack_.pop();
48 return task.Pass();
49 }
50
51 scoped_refptr<IndexedDBTransaction> IndexedDBTransaction::Create(
52 int64_t id,
53 scoped_refptr<IndexedDBDatabaseCallbacksWrapper> callbacks,
54 const std::vector<int64_t>& object_store_ids,
55 IndexedDB::TransactionMode mode,
56 IndexedDBDatabaseImpl* database) {
57 std::set<int64_t> object_store_hash_set;
58 for (size_t i = 0; i < object_store_ids.size(); ++i)
59 object_store_hash_set.insert(object_store_ids[i]);
60
61 return make_scoped_refptr(new IndexedDBTransaction(
62 id, callbacks, object_store_hash_set, mode, database));
63 }
64
65 IndexedDBTransaction::IndexedDBTransaction(
66 int64_t id,
67 scoped_refptr<IndexedDBDatabaseCallbacksWrapper> callbacks,
68 const std::set<int64_t>& object_store_ids,
69 IndexedDB::TransactionMode mode,
70 IndexedDBDatabaseImpl* database)
71 : id_(id),
72 object_store_ids_(object_store_ids),
73 mode_(mode),
74 state_(Unused),
75 commit_pending_(false),
76 callbacks_(callbacks),
77 database_(database),
78 transaction_(database->BackingStore().get()),
79 pending_preemptive_events_(0) {
80 database_->transaction_coordinator().DidCreateTransaction(this);
81 }
82
83 IndexedDBTransaction::~IndexedDBTransaction() {
84 // It shouldn't be possible for this object to get deleted until it's either
85 // complete or aborted.
86 DCHECK(state_ == Finished);
87 DCHECK(preemptive_task_queue_.empty());
88 DCHECK(task_queue_.empty());
89 DCHECK(abort_task_stack_.empty());
90 }
91
92 void IndexedDBTransaction::ScheduleTask(IndexedDBDatabase::TaskType type,
93 Operation* task,
94 Operation* abort_task) {
95 if (state_ == Finished)
96 return;
97
98 if (type == IndexedDBDatabase::NormalTask)
99 task_queue_.push(task);
100 else
101 preemptive_task_queue_.push(task);
102
103 if (abort_task)
104 abort_task_stack_.push(abort_task);
105
106 if (state_ == Unused)
107 Start();
108 else if (state_ == Running && !task_timer_.IsRunning())
109 task_timer_.Start(FROM_HERE,
110 base::TimeDelta::FromSeconds(0),
111 this,
112 &IndexedDBTransaction::TaskTimerFired);
113 }
114
115 void IndexedDBTransaction::Abort() {
116 Abort(IndexedDBDatabaseError::Create(
117 WebKit::WebIDBDatabaseExceptionUnknownError,
118 ASCIIToUTF16("Internal error (unknown cause)")));
119 }
120
121 void IndexedDBTransaction::Abort(scoped_refptr<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 task_timer_.Stop();
135
136 if (was_running)
137 transaction_.Rollback();
138
139 // Run the abort tasks, if any.
140 while (!abort_task_stack_.empty()) {
141 scoped_ptr<Operation> task(abort_task_stack_.pop());
142 task->Perform(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 DCHECK(!database_->transaction_coordinator().IsActive(this));
159 database_->TransactionFinished(this);
160
161 if (callbacks_)
162 callbacks_->OnAbort(id_, error);
163
164 database_->TransactionFinishedAndAbortFired(this);
165
166 database_ = NULL;
167 }
168
169 bool IndexedDBTransaction::IsTaskQueueEmpty() const {
170 return preemptive_task_queue_.empty() && task_queue_.empty();
171 }
172
173 bool IndexedDBTransaction::HasPendingTasks() const {
174 return pending_preemptive_events_ || !IsTaskQueueEmpty();
175 }
176
177 void IndexedDBTransaction::RegisterOpenCursor(IndexedDBCursorImpl* cursor) {
178 open_cursors_.insert(cursor);
179 }
180
181 void IndexedDBTransaction::UnregisterOpenCursor(IndexedDBCursorImpl* cursor) {
182 open_cursors_.erase(cursor);
183 }
184
185 void IndexedDBTransaction::Run() {
186 // TransactionCoordinator has started this transaction. Schedule a timer
187 // to process the first task.
188 DCHECK(state_ == StartPending || state_ == Running);
189 DCHECK(!task_timer_.IsRunning());
190
191 task_timer_.Start(FROM_HERE,
192 base::TimeDelta::FromSeconds(0),
193 this,
194 &IndexedDBTransaction::TaskTimerFired);
195 }
196
197 void IndexedDBTransaction::Start() {
198 DCHECK(state_ == Unused);
199
200 state_ = StartPending;
201 database_->transaction_coordinator().DidStartTransaction(this);
202 database_->TransactionStarted(this);
203 }
204
205 void IndexedDBTransaction::Commit() {
206 IDB_TRACE("IndexedDBTransaction::commit");
207
208 // In multiprocess ports, front-end may have requested a commit but
209 // an abort has already been initiated asynchronously by the
210 // back-end.
211 if (state_ == Finished)
212 return;
213
214 DCHECK(state_ == Unused || state_ == Running);
215 commit_pending_ = true;
216
217 // Front-end has requested a commit, but there may be tasks like
218 // create_index which are considered synchronous by the front-end
219 // but are processed asynchronously.
220 if (HasPendingTasks())
221 return;
222
223 // The last reference to this object may be released while performing the
224 // commit steps below. We therefore take a self reference to keep ourselves
225 // alive while executing this method.
226 scoped_refptr<IndexedDBTransaction> protect(this);
227
228 // TODO(jsbell): Run abort tasks if commit fails? http://crbug.com/241843
229 abort_task_stack_.clear();
230
231 bool unused = state_ == Unused;
232 state_ = Finished;
233
234 bool committed = unused || transaction_.Commit();
235
236 // Backing store resources (held via cursors) must be released
237 // before script callbacks are fired, as the script callbacks may
238 // release references and allow the backing store itself to be
239 // released, and order is critical.
240 CloseOpenCursors();
241 transaction_.Reset();
242
243 // Transactions must also be marked as completed before the
244 // front-end is notified, as the transaction completion unblocks
245 // operations like closing connections.
246 if (!unused)
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::Create(
257 WebKit::WebIDBDatabaseExceptionUnknownError,
258 ASCIIToUTF16("Internal error committing transaction.")));
259 database_->TransactionFinishedAndAbortFired(this);
260 }
261
262 database_ = NULL;
263 }
264
265 void IndexedDBTransaction::TaskTimerFired() {
266 IDB_TRACE("IndexedDBTransaction::task_timer_fired");
267 DCHECK(!IsTaskQueueEmpty());
268
269 if (state_ == StartPending) {
270 transaction_.begin();
271 state_ = Running;
272 }
273
274 // The last reference to this object may be released while performing the
275 // tasks. Take take a self reference to keep this object alive so that
276 // the loop termination conditions can be checked.
277 scoped_refptr<IndexedDBTransaction> protect(this);
278
279 TaskQueue* task_queue =
280 pending_preemptive_events_ ? &preemptive_task_queue_ : &task_queue_;
281 while (!task_queue->empty() && state_ != Finished) {
282 DCHECK(state_ == Running);
283 scoped_ptr<Operation> task(task_queue->pop());
284 task->Perform(this);
285
286 // Event itself may change which queue should be processed next.
287 task_queue =
288 pending_preemptive_events_ ? &preemptive_task_queue_ : &task_queue_;
289 }
290
291 // If there are no pending tasks, we haven't already committed/aborted,
292 // and the front-end requested a commit, it is now safe to do so.
293 if (!HasPendingTasks() && state_ != Finished && commit_pending_)
294 Commit();
295 }
296
297 void IndexedDBTransaction::CloseOpenCursors() {
298 for (std::set<IndexedDBCursorImpl*>::iterator i = open_cursors_.begin();
299 i != open_cursors_.end();
300 ++i)
301 (*i)->Close();
302 open_cursors_.clear();
303 }
304
305 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698