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