OLD | NEW |
---|---|
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/browser/indexed_db/indexed_db_transaction.h" | 5 #include "content/browser/indexed_db/indexed_db_transaction.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/logging.h" | 8 #include "base/logging.h" |
9 #include "base/message_loop/message_loop.h" | 9 #include "base/message_loop/message_loop.h" |
10 #include "base/strings/utf_string_conversions.h" | 10 #include "base/strings/utf_string_conversions.h" |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
52 int64 id, | 52 int64 id, |
53 scoped_refptr<IndexedDBDatabaseCallbacks> callbacks, | 53 scoped_refptr<IndexedDBDatabaseCallbacks> callbacks, |
54 const std::set<int64>& object_store_ids, | 54 const std::set<int64>& object_store_ids, |
55 indexed_db::TransactionMode mode, | 55 indexed_db::TransactionMode mode, |
56 IndexedDBDatabase* database) | 56 IndexedDBDatabase* database) |
57 : id_(id), | 57 : id_(id), |
58 object_store_ids_(object_store_ids), | 58 object_store_ids_(object_store_ids), |
59 mode_(mode), | 59 mode_(mode), |
60 state_(UNUSED), | 60 state_(UNUSED), |
61 commit_pending_(false), | 61 commit_pending_(false), |
62 blob_write_success_(false), | |
62 callbacks_(callbacks), | 63 callbacks_(callbacks), |
63 database_(database), | 64 database_(database), |
64 transaction_(database->BackingStore().get()), | 65 transaction_(database->BackingStore().get()), |
65 should_process_queue_(false), | 66 should_process_queue_(false), |
66 pending_preemptive_events_(0) { | 67 pending_preemptive_events_(0) { |
68 fprintf(stderr, "ERICU: IndexedDBTransaction::IndexedDBTransaction(%p).\n", | |
69 this); | |
67 database_->transaction_coordinator().DidCreateTransaction(this); | 70 database_->transaction_coordinator().DidCreateTransaction(this); |
68 } | 71 } |
69 | 72 |
70 IndexedDBTransaction::~IndexedDBTransaction() { | 73 IndexedDBTransaction::~IndexedDBTransaction() { |
71 // It shouldn't be possible for this object to get deleted until it's either | 74 // It shouldn't be possible for this object to get deleted until it's either |
72 // complete or aborted. | 75 // complete or aborted. |
76 fprintf(stderr, "ERICU: IndexedDBTransaction::~IndexedDBTransaction.\n"); | |
73 DCHECK_EQ(state_, FINISHED); | 77 DCHECK_EQ(state_, FINISHED); |
74 DCHECK(preemptive_task_queue_.empty()); | 78 DCHECK(preemptive_task_queue_.empty()); |
75 DCHECK(task_queue_.empty()); | 79 DCHECK(task_queue_.empty()); |
76 DCHECK(abort_task_stack_.empty()); | 80 DCHECK(abort_task_stack_.empty()); |
77 } | 81 } |
78 | 82 |
79 void IndexedDBTransaction::ScheduleTask(Operation task, Operation abort_task) { | 83 void IndexedDBTransaction::ScheduleTask(Operation task, Operation abort_task) { |
80 if (state_ == FINISHED) | 84 if (state_ == FINISHED) |
81 return; | 85 return; |
82 task_queue_.push(task); | 86 task_queue_.push(task); |
83 abort_task_stack_.push(abort_task); | 87 abort_task_stack_.push(abort_task); |
84 EnsureTasksRunning(); | 88 EnsureTasksRunning(); |
85 } | 89 } |
86 | 90 |
87 void IndexedDBTransaction::ScheduleTask(IndexedDBDatabase::TaskType type, | 91 void IndexedDBTransaction::ScheduleTask(IndexedDBDatabase::TaskType type, |
88 Operation task) { | 92 Operation task) { |
89 if (state_ == FINISHED) | 93 if (IsStatePastRunning()) |
90 return; | 94 return; |
91 | 95 |
92 if (type == IndexedDBDatabase::NORMAL_TASK) | 96 if (type == IndexedDBDatabase::NORMAL_TASK) |
93 task_queue_.push(task); | 97 task_queue_.push(task); |
94 else | 98 else |
95 preemptive_task_queue_.push(task); | 99 preemptive_task_queue_.push(task); |
96 EnsureTasksRunning(); | 100 EnsureTasksRunning(); |
97 } | 101 } |
98 | 102 |
99 void IndexedDBTransaction::EnsureTasksRunning() { | 103 void IndexedDBTransaction::EnsureTasksRunning() { |
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
188 } | 192 } |
189 | 193 |
190 void IndexedDBTransaction::Start() { | 194 void IndexedDBTransaction::Start() { |
191 DCHECK_EQ(state_, UNUSED); | 195 DCHECK_EQ(state_, UNUSED); |
192 | 196 |
193 state_ = START_PENDING; | 197 state_ = START_PENDING; |
194 database_->transaction_coordinator().DidStartTransaction(this); | 198 database_->transaction_coordinator().DidStartTransaction(this); |
195 database_->TransactionStarted(this); | 199 database_->TransactionStarted(this); |
196 } | 200 } |
197 | 201 |
202 class BlobWriteCallbackImpl : public IndexedDBBackingStore::BlobWriteCallback { | |
203 public: | |
204 BlobWriteCallbackImpl(scoped_refptr<IndexedDBTransaction> transaction) | |
205 : transaction_(transaction) { } | |
206 virtual ~BlobWriteCallbackImpl() { } | |
207 virtual void didSucceed() { | |
208 transaction_->BlobWriteComplete(true); | |
209 } | |
210 virtual void didFail() { | |
211 transaction_->BlobWriteComplete(false); | |
212 } | |
213 private: | |
214 scoped_refptr<IndexedDBTransaction> transaction_; | |
215 }; | |
216 | |
198 void IndexedDBTransaction::Commit() { | 217 void IndexedDBTransaction::Commit() { |
199 IDB_TRACE("IndexedDBTransaction::Commit"); | 218 IDB_TRACE("IndexedDBTransaction::Commit"); |
219 fprintf(stderr, "ERICU: IndexedDBTransaction::Commit.\n"); | |
200 | 220 |
201 // In multiprocess ports, front-end may have requested a commit but | 221 // In multiprocess ports, front-end may have requested a commit but |
202 // an abort has already been initiated asynchronously by the | 222 // an abort has already been initiated asynchronously by the |
203 // back-end. | 223 // back-end. |
204 if (state_ == FINISHED) | 224 if (IsStatePastRunning()) |
205 return; | 225 return; |
206 | 226 |
207 DCHECK(state_ == UNUSED || state_ == RUNNING); | 227 DCHECK(state_ == UNUSED || state_ == RUNNING); |
208 commit_pending_ = true; | 228 commit_pending_ = true; |
209 | 229 |
210 // Front-end has requested a commit, but there may be tasks like | 230 // Front-end has requested a commit, but there may be tasks like |
211 // create_index which are considered synchronous by the front-end | 231 // create_index which are considered synchronous by the front-end |
212 // but are processed asynchronously. | 232 // but are processed asynchronously. |
213 if (HasPendingTasks()) | 233 if (HasPendingTasks()) |
214 return; | 234 return; |
215 | 235 |
216 // The last reference to this object may be released while performing the | 236 bool unused = state_ == UNUSED; |
217 // commit steps below. We therefore take a self reference to keep ourselves | 237 state_ = COMMITTING; |
218 // alive while executing this method. | |
219 scoped_refptr<IndexedDBTransaction> protect(this); | |
220 | 238 |
221 // TODO(jsbell): Run abort tasks if commit fails? http://crbug.com/241843 | 239 if (unused) |
222 abort_task_stack_.clear(); | 240 CommitPhaseTwo(WAS_NOT_USED, DO_COMMIT); |
223 | 241 else { |
224 bool unused = state_ == UNUSED; | 242 scoped_refptr<IndexedDBBackingStore::BlobWriteCallback> callback( |
225 state_ = FINISHED; | 243 new BlobWriteCallbackImpl(this)); |
226 | 244 // CommitPhaseOne will call the callback synchronously if there are no blobs |
227 bool committed = unused || transaction_.Commit(); | 245 // to write. |
228 | 246 if (!transaction_.CommitPhaseOne(callback)) |
229 // Backing store resources (held via cursors) must be released | 247 CommitPhaseTwo(WAS_USED, SKIP_COMMIT); |
230 // before script callbacks are fired, as the script callbacks may | |
231 // release references and allow the backing store itself to be | |
232 // released, and order is critical. | |
233 CloseOpenCursors(); | |
234 transaction_.Reset(); | |
235 | |
236 // Transactions must also be marked as completed before the | |
237 // front-end is notified, as the transaction completion unblocks | |
238 // operations like closing connections. | |
239 database_->transaction_coordinator().DidFinishTransaction(this); | |
240 database_->TransactionFinished(this); | |
241 | |
242 if (committed) { | |
243 callbacks_->OnComplete(id_); | |
244 database_->TransactionFinishedAndCompleteFired(this); | |
245 } else { | |
246 callbacks_->OnAbort( | |
247 id_, | |
248 IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError, | |
249 "Internal error committing transaction.")); | |
250 database_->TransactionFinishedAndAbortFired(this); | |
251 } | 248 } |
252 | |
253 database_ = NULL; | |
254 } | 249 } |
255 | 250 |
256 void IndexedDBTransaction::ProcessTaskQueue() { | 251 void IndexedDBTransaction::ProcessTaskQueue() { |
257 IDB_TRACE("IndexedDBTransaction::ProcessTaskQueue"); | 252 IDB_TRACE("IndexedDBTransaction::ProcessTaskQueue"); |
258 | 253 |
259 // May have been aborted. | 254 // May have been aborted. |
260 if (!should_process_queue_) | 255 if (!should_process_queue_) |
261 return; | 256 return; |
262 | 257 |
263 DCHECK(!IsTaskQueueEmpty()); | 258 DCHECK(!IsTaskQueueEmpty()); |
264 should_process_queue_ = false; | 259 should_process_queue_ = false; |
265 | 260 |
266 if (state_ == START_PENDING) { | 261 if (state_ == START_PENDING) { |
267 transaction_.Begin(); | 262 transaction_.Begin(); |
268 state_ = RUNNING; | 263 state_ = RUNNING; |
269 } | 264 } |
270 | 265 |
271 // The last reference to this object may be released while performing the | 266 // The last reference to this object may be released while performing the |
272 // tasks. Take take a self reference to keep this object alive so that | 267 // tasks. Take take a self reference to keep this object alive so that |
273 // the loop termination conditions can be checked. | 268 // the loop termination conditions can be checked. |
274 scoped_refptr<IndexedDBTransaction> protect(this); | 269 scoped_refptr<IndexedDBTransaction> protect(this); |
275 | 270 |
276 TaskQueue* task_queue = | 271 TaskQueue* task_queue = |
277 pending_preemptive_events_ ? &preemptive_task_queue_ : &task_queue_; | 272 pending_preemptive_events_ ? &preemptive_task_queue_ : &task_queue_; |
278 while (!task_queue->empty() && state_ != FINISHED) { | 273 while (!task_queue->empty() && !IsStatePastRunning()) { |
279 DCHECK_EQ(state_, RUNNING); | 274 DCHECK_EQ(state_, RUNNING); |
280 Operation task(task_queue->pop()); | 275 Operation task(task_queue->pop()); |
281 task.Run(this); | 276 task.Run(this); |
282 | 277 |
283 // Event itself may change which queue should be processed next. | 278 // Event itself may change which queue should be processed next. |
284 task_queue = | 279 task_queue = |
285 pending_preemptive_events_ ? &preemptive_task_queue_ : &task_queue_; | 280 pending_preemptive_events_ ? &preemptive_task_queue_ : &task_queue_; |
286 } | 281 } |
287 | 282 |
288 // If there are no pending tasks, we haven't already committed/aborted, | 283 // If there are no pending tasks, we haven't already committed/aborted, |
289 // and the front-end requested a commit, it is now safe to do so. | 284 // and the front-end requested a commit, it is now safe to do so. |
290 if (!HasPendingTasks() && state_ != FINISHED && commit_pending_) | 285 if (!HasPendingTasks() && !IsStatePastRunning() && commit_pending_) |
291 Commit(); | 286 Commit(); |
292 } | 287 } |
293 | 288 |
289 void IndexedDBTransaction::BlobWriteComplete(bool success) | |
290 { | |
291 IDB_TRACE("IndexedDBTransaction::BlobWriteComplete"); | |
292 if (state_ == FINISHED) // aborted | |
293 return; | |
294 DCHECK_EQ(state_, COMMITTING); | |
295 if (success) | |
296 CommitPhaseTwo(WAS_USED, DO_COMMIT); | |
297 else | |
298 Abort(IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionDataError, | |
299 "Failed to write blobs")); | |
300 } | |
301 | |
294 void IndexedDBTransaction::CloseOpenCursors() { | 302 void IndexedDBTransaction::CloseOpenCursors() { |
295 for (std::set<IndexedDBCursor*>::iterator i = open_cursors_.begin(); | 303 for (std::set<IndexedDBCursor*>::iterator i = open_cursors_.begin(); |
296 i != open_cursors_.end(); | 304 i != open_cursors_.end(); |
297 ++i) | 305 ++i) |
298 (*i)->Close(); | 306 (*i)->Close(); |
299 open_cursors_.clear(); | 307 open_cursors_.clear(); |
300 } | 308 } |
301 | 309 |
310 void IndexedDBTransaction::CommitPhaseTwo(TransactionUseState use_state, | |
311 ShouldSkipCommit skip_commit) { | |
312 DCHECK_EQ(state_, COMMITTING); | |
313 state_ = FINISHED; | |
314 | |
315 // The last reference to this object may be released while performing the | |
316 // commit steps below. We therefore take a self reference to keep ourselves | |
317 // alive while executing this method. | |
318 scoped_refptr<IndexedDBTransaction> protect(this); | |
319 | |
320 // TODO(jsbell): Run abort tasks if commit fails? http://crbug.com/241843 | |
321 abort_task_stack_.clear(); | |
322 | |
323 state_ = FINISHED; | |
324 | |
325 bool committed = (use_state == WAS_NOT_USED) || | |
jsbell
2013/09/13 00:12:21
Seems like use_state and skip_commit effectively h
ericu
2013/11/20 23:05:39
No--we skip_commit if we've failed or aborted, but
| |
326 ((skip_commit == DO_COMMIT) && transaction_.CommitPhaseTwo()); | |
327 | |
328 // Backing store resources (held via cursors) must be released | |
329 // before script callbacks are fired, as the script callbacks may | |
330 // release references and allow the backing store itself to be | |
331 // released, and order is critical. | |
332 CloseOpenCursors(); | |
333 transaction_.Reset(); | |
334 | |
335 // Transactions must also be marked as completed before the | |
336 // front-end is notified, as the transaction completion unblocks | |
337 // operations like closing connections. | |
338 database_->transaction_coordinator().DidFinishTransaction(this); | |
339 database_->TransactionFinished(this); | |
340 | |
341 if (committed) { | |
342 callbacks_->OnComplete(id_); | |
343 database_->TransactionFinishedAndCompleteFired(this); | |
344 } else { | |
345 callbacks_->OnAbort( | |
346 id_, | |
347 IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError, | |
348 "Internal error committing transaction.")); | |
349 database_->TransactionFinishedAndAbortFired(this); | |
350 } | |
351 | |
352 database_ = NULL; | |
353 } | |
354 | |
302 } // namespace content | 355 } // namespace content |
OLD | NEW |