Chromium Code Reviews| 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 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 80 // It shouldn't be possible for this object to get deleted until it's either | 80 // It shouldn't be possible for this object to get deleted until it's either |
| 81 // complete or aborted. | 81 // complete or aborted. |
| 82 DCHECK_EQ(state_, FINISHED); | 82 DCHECK_EQ(state_, FINISHED); |
| 83 DCHECK(preemptive_task_queue_.empty()); | 83 DCHECK(preemptive_task_queue_.empty()); |
| 84 DCHECK_EQ(pending_preemptive_events_, 0); | 84 DCHECK_EQ(pending_preemptive_events_, 0); |
| 85 DCHECK(task_queue_.empty()); | 85 DCHECK(task_queue_.empty()); |
| 86 DCHECK(abort_task_stack_.empty()); | 86 DCHECK(abort_task_stack_.empty()); |
| 87 } | 87 } |
| 88 | 88 |
| 89 void IndexedDBTransaction::ScheduleTask(Operation task, Operation abort_task) { | 89 void IndexedDBTransaction::ScheduleTask(Operation task, Operation abort_task) { |
| 90 if (state_ == FINISHED) | 90 if (IsStatePastStarted()) |
| 91 return; | 91 return; |
| 92 | 92 |
| 93 timeout_timer_.Stop(); | 93 timeout_timer_.Stop(); |
| 94 used_ = true; | 94 used_ = true; |
| 95 task_queue_.push(task); | 95 task_queue_.push(task); |
| 96 ++diagnostics_.tasks_scheduled; | 96 ++diagnostics_.tasks_scheduled; |
| 97 abort_task_stack_.push(abort_task); | 97 abort_task_stack_.push(abort_task); |
| 98 RunTasksIfStarted(); | 98 RunTasksIfStarted(); |
| 99 } | 99 } |
| 100 | 100 |
| 101 void IndexedDBTransaction::ScheduleTask(IndexedDBDatabase::TaskType type, | 101 void IndexedDBTransaction::ScheduleTask(IndexedDBDatabase::TaskType type, |
| 102 Operation task) { | 102 Operation task) { |
| 103 if (state_ == FINISHED) | 103 if (IsStatePastStarted()) |
| 104 return; | 104 return; |
| 105 | 105 |
| 106 timeout_timer_.Stop(); | 106 timeout_timer_.Stop(); |
| 107 used_ = true; | 107 used_ = true; |
| 108 if (type == IndexedDBDatabase::NORMAL_TASK) { | 108 if (type == IndexedDBDatabase::NORMAL_TASK) { |
| 109 task_queue_.push(task); | 109 task_queue_.push(task); |
| 110 ++diagnostics_.tasks_scheduled; | 110 ++diagnostics_.tasks_scheduled; |
| 111 } else { | 111 } else { |
| 112 preemptive_task_queue_.push(task); | 112 preemptive_task_queue_.push(task); |
| 113 } | 113 } |
| (...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 205 DCHECK_EQ(CREATED, state_); | 205 DCHECK_EQ(CREATED, state_); |
| 206 state_ = STARTED; | 206 state_ = STARTED; |
| 207 diagnostics_.start_time = base::Time::Now(); | 207 diagnostics_.start_time = base::Time::Now(); |
| 208 | 208 |
| 209 if (!used_) | 209 if (!used_) |
| 210 return; | 210 return; |
| 211 | 211 |
| 212 RunTasksIfStarted(); | 212 RunTasksIfStarted(); |
| 213 } | 213 } |
| 214 | 214 |
| 215 class BlobWriteCallbackImpl : public IndexedDBBackingStore::BlobWriteCallback { | |
| 216 public: | |
| 217 BlobWriteCallbackImpl(scoped_refptr<IndexedDBTransaction> transaction) | |
| 218 : transaction_(transaction) {} | |
| 219 virtual ~BlobWriteCallbackImpl() {} | |
| 220 virtual void Run(bool succeeded) { | |
| 221 transaction_->BlobWriteComplete(succeeded); | |
| 222 } | |
| 223 | |
| 224 private: | |
| 225 scoped_refptr<IndexedDBTransaction> transaction_; | |
| 226 }; | |
| 227 | |
| 228 void IndexedDBTransaction::BlobWriteComplete(bool success) { | |
| 229 IDB_TRACE("IndexedDBTransaction::BlobWriteComplete"); | |
| 230 if (state_ == FINISHED) // aborted | |
| 231 return; | |
| 232 DCHECK_EQ(state_, COMMITTING); | |
| 233 if (success) | |
| 234 CommitPhaseTwo(); | |
| 235 else | |
| 236 Abort(IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionDataError, | |
| 237 "Failed to write blobs.")); | |
| 238 } | |
| 239 | |
| 215 void IndexedDBTransaction::Commit() { | 240 void IndexedDBTransaction::Commit() { |
| 216 IDB_TRACE("IndexedDBTransaction::Commit"); | 241 IDB_TRACE("IndexedDBTransaction::Commit"); |
| 217 | 242 |
| 218 // In multiprocess ports, front-end may have requested a commit but | 243 // In multiprocess ports, front-end may have requested a commit but |
| 219 // an abort has already been initiated asynchronously by the | 244 // an abort has already been initiated asynchronously by the |
| 220 // back-end. | 245 // back-end. |
| 221 if (state_ == FINISHED) | 246 if (IsStatePastStarted()) { |
| 247 DCHECK(state_ == FINISHED); | |
| 222 return; | 248 return; |
| 249 } | |
| 223 | 250 |
| 224 DCHECK(!used_ || state_ == STARTED); | 251 DCHECK(!used_ || state_ == STARTED); |
| 225 commit_pending_ = true; | 252 commit_pending_ = true; |
| 226 | 253 |
| 227 // Front-end has requested a commit, but there may be tasks like | 254 // Front-end has requested a commit, but there may be tasks like |
| 228 // create_index which are considered synchronous by the front-end | 255 // create_index which are considered synchronous by the front-end |
| 229 // but are processed asynchronously. | 256 // but are processed asynchronously. |
| 230 if (HasPendingTasks()) | 257 if (HasPendingTasks()) |
| 231 return; | 258 return; |
| 232 | 259 |
| 260 state_ = COMMITTING; | |
| 261 | |
| 262 if (!used_) | |
| 263 CommitPhaseTwo(); | |
| 264 else { | |
| 265 scoped_refptr<IndexedDBBackingStore::BlobWriteCallback> callback( | |
| 266 new BlobWriteCallbackImpl(this)); | |
| 267 // CommitPhaseOne will call the callback synchronously if there are no blobs | |
| 268 // to write. | |
| 269 if (!transaction_->CommitPhaseOne(callback).ok()) | |
| 270 Abort(IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionDataError, | |
| 271 "Error processing blob journal.")); | |
| 272 } | |
| 273 } | |
| 274 | |
| 275 void IndexedDBTransaction::CommitPhaseTwo() { | |
|
jsbell
2014/05/20 22:02:19
Is it possible for an Abort() to slip in between C
ericu
2014/05/21 18:14:47
Ah, yes it is. For CommitPhaseTwo to be called af
| |
| 276 DCHECK_EQ(state_, COMMITTING); | |
| 277 | |
| 233 // The last reference to this object may be released while performing the | 278 // The last reference to this object may be released while performing the |
| 234 // commit steps below. We therefore take a self reference to keep ourselves | 279 // commit steps below. We therefore take a self reference to keep ourselves |
| 235 // alive while executing this method. | 280 // alive while executing this method. |
| 236 scoped_refptr<IndexedDBTransaction> protect(this); | 281 scoped_refptr<IndexedDBTransaction> protect(this); |
| 237 | 282 |
| 238 timeout_timer_.Stop(); | 283 timeout_timer_.Stop(); |
| 239 | 284 |
| 240 state_ = FINISHED; | 285 state_ = FINISHED; |
| 241 | 286 |
| 242 bool committed = !used_ || transaction_->Commit().ok(); | 287 bool committed = !used_ || transaction_->CommitPhaseTwo().ok(); |
| 243 | 288 |
| 244 // Backing store resources (held via cursors) must be released | 289 // Backing store resources (held via cursors) must be released |
| 245 // before script callbacks are fired, as the script callbacks may | 290 // before script callbacks are fired, as the script callbacks may |
| 246 // release references and allow the backing store itself to be | 291 // release references and allow the backing store itself to be |
| 247 // released, and order is critical. | 292 // released, and order is critical. |
| 248 CloseOpenCursors(); | 293 CloseOpenCursors(); |
| 249 transaction_->Reset(); | 294 transaction_->Reset(); |
| 250 | 295 |
| 251 // Transactions must also be marked as completed before the | 296 // Transactions must also be marked as completed before the |
| 252 // front-end is notified, as the transaction completion unblocks | 297 // front-end is notified, as the transaction completion unblocks |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 287 backing_store_transaction_begun_ = true; | 332 backing_store_transaction_begun_ = true; |
| 288 } | 333 } |
| 289 | 334 |
| 290 // The last reference to this object may be released while performing the | 335 // The last reference to this object may be released while performing the |
| 291 // tasks. Take take a self reference to keep this object alive so that | 336 // tasks. Take take a self reference to keep this object alive so that |
| 292 // the loop termination conditions can be checked. | 337 // the loop termination conditions can be checked. |
| 293 scoped_refptr<IndexedDBTransaction> protect(this); | 338 scoped_refptr<IndexedDBTransaction> protect(this); |
| 294 | 339 |
| 295 TaskQueue* task_queue = | 340 TaskQueue* task_queue = |
| 296 pending_preemptive_events_ ? &preemptive_task_queue_ : &task_queue_; | 341 pending_preemptive_events_ ? &preemptive_task_queue_ : &task_queue_; |
| 297 while (!task_queue->empty() && state_ != FINISHED) { | 342 while (!task_queue->empty() && !IsStatePastStarted()) { |
| 298 DCHECK_EQ(STARTED, state_); | 343 DCHECK_EQ(state_, STARTED); |
| 299 Operation task(task_queue->pop()); | 344 Operation task(task_queue->pop()); |
| 300 task.Run(this); | 345 task.Run(this); |
| 301 if (!pending_preemptive_events_) { | 346 if (!pending_preemptive_events_) { |
| 302 DCHECK(diagnostics_.tasks_completed < diagnostics_.tasks_scheduled); | 347 DCHECK(diagnostics_.tasks_completed < diagnostics_.tasks_scheduled); |
| 303 ++diagnostics_.tasks_completed; | 348 ++diagnostics_.tasks_completed; |
| 304 } | 349 } |
| 305 | 350 |
| 306 // Event itself may change which queue should be processed next. | 351 // Event itself may change which queue should be processed next. |
| 307 task_queue = | 352 task_queue = |
| 308 pending_preemptive_events_ ? &preemptive_task_queue_ : &task_queue_; | 353 pending_preemptive_events_ ? &preemptive_task_queue_ : &task_queue_; |
| 309 } | 354 } |
| 310 | 355 |
| 311 // If there are no pending tasks, we haven't already committed/aborted, | 356 // If there are no pending tasks, we haven't already committed/aborted, |
| 312 // and the front-end requested a commit, it is now safe to do so. | 357 // and the front-end requested a commit, it is now safe to do so. |
| 313 if (!HasPendingTasks() && state_ != FINISHED && commit_pending_) { | 358 if (!HasPendingTasks() && !IsStatePastStarted() && commit_pending_) { |
| 314 Commit(); | 359 Commit(); |
| 315 return; | 360 return; |
| 316 } | 361 } |
| 317 | 362 |
| 318 // The transaction may have been aborted while processing tasks. | 363 // The transaction may have been aborted while processing tasks. |
| 319 if (state_ == FINISHED) | 364 if (state_ == FINISHED) |
| 320 return; | 365 return; |
| 321 | 366 |
| 322 // Otherwise, start a timer in case the front-end gets wedged and | 367 // Otherwise, start a timer in case the front-end gets wedged and |
| 323 // never requests further activity. Read-only transactions don't | 368 // never requests further activity. Read-only transactions don't |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 338 | 383 |
| 339 void IndexedDBTransaction::CloseOpenCursors() { | 384 void IndexedDBTransaction::CloseOpenCursors() { |
| 340 for (std::set<IndexedDBCursor*>::iterator i = open_cursors_.begin(); | 385 for (std::set<IndexedDBCursor*>::iterator i = open_cursors_.begin(); |
| 341 i != open_cursors_.end(); | 386 i != open_cursors_.end(); |
| 342 ++i) | 387 ++i) |
| 343 (*i)->Close(); | 388 (*i)->Close(); |
| 344 open_cursors_.clear(); | 389 open_cursors_.clear(); |
| 345 } | 390 } |
| 346 | 391 |
| 347 } // namespace content | 392 } // namespace content |
| OLD | NEW |