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/location.h" | 8 #include "base/location.h" |
| 9 #include "base/logging.h" | 9 #include "base/logging.h" |
| 10 #include "base/memory/ptr_util.h" | 10 #include "base/memory/ptr_util.h" |
| 11 #include "base/single_thread_task_runner.h" | 11 #include "base/single_thread_task_runner.h" |
| 12 #include "base/stl_util.h" | 12 #include "base/stl_util.h" |
| 13 #include "base/strings/utf_string_conversions.h" | 13 #include "base/strings/utf_string_conversions.h" |
| 14 #include "base/threading/thread_task_runner_handle.h" | 14 #include "base/threading/thread_task_runner_handle.h" |
| 15 #include "content/browser/indexed_db/indexed_db_backing_store.h" | 15 #include "content/browser/indexed_db/indexed_db_backing_store.h" |
| 16 #include "content/browser/indexed_db/indexed_db_cursor.h" | 16 #include "content/browser/indexed_db/indexed_db_cursor.h" |
| 17 #include "content/browser/indexed_db/indexed_db_database.h" | 17 #include "content/browser/indexed_db/indexed_db_database.h" |
| 18 #include "content/browser/indexed_db/indexed_db_database_callbacks.h" | 18 #include "content/browser/indexed_db/indexed_db_database_callbacks.h" |
| 19 #include "content/browser/indexed_db/indexed_db_factory.h" | |
| 19 #include "content/browser/indexed_db/indexed_db_observation.h" | 20 #include "content/browser/indexed_db/indexed_db_observation.h" |
| 20 #include "content/browser/indexed_db/indexed_db_observer_changes.h" | 21 #include "content/browser/indexed_db/indexed_db_observer_changes.h" |
| 21 #include "content/browser/indexed_db/indexed_db_tracing.h" | 22 #include "content/browser/indexed_db/indexed_db_tracing.h" |
| 22 #include "content/browser/indexed_db/indexed_db_transaction_coordinator.h" | 23 #include "content/browser/indexed_db/indexed_db_transaction_coordinator.h" |
| 23 #include "third_party/WebKit/public/platform/modules/indexeddb/WebIDBDatabaseExc eption.h" | 24 #include "third_party/WebKit/public/platform/modules/indexeddb/WebIDBDatabaseExc eption.h" |
| 24 #include "third_party/leveldatabase/env_chromium.h" | 25 #include "third_party/leveldatabase/env_chromium.h" |
| 25 | 26 |
| 26 namespace content { | 27 namespace content { |
| 27 | 28 |
| 28 namespace { | 29 namespace { |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 41 IndexedDBTransaction::TaskQueue::TaskQueue() {} | 42 IndexedDBTransaction::TaskQueue::TaskQueue() {} |
| 42 IndexedDBTransaction::TaskQueue::~TaskQueue() { clear(); } | 43 IndexedDBTransaction::TaskQueue::~TaskQueue() { clear(); } |
| 43 | 44 |
| 44 void IndexedDBTransaction::TaskQueue::clear() { | 45 void IndexedDBTransaction::TaskQueue::clear() { |
| 45 while (!queue_.empty()) | 46 while (!queue_.empty()) |
| 46 queue_.pop(); | 47 queue_.pop(); |
| 47 } | 48 } |
| 48 | 49 |
| 49 IndexedDBTransaction::Operation IndexedDBTransaction::TaskQueue::pop() { | 50 IndexedDBTransaction::Operation IndexedDBTransaction::TaskQueue::pop() { |
| 50 DCHECK(!queue_.empty()); | 51 DCHECK(!queue_.empty()); |
| 51 Operation task(queue_.front()); | 52 Operation task = std::move(queue_.front()); |
| 52 queue_.pop(); | 53 queue_.pop(); |
| 53 return task; | 54 return task; |
| 54 } | 55 } |
| 55 | 56 |
| 56 IndexedDBTransaction::TaskStack::TaskStack() {} | 57 IndexedDBTransaction::TaskStack::TaskStack() {} |
| 57 IndexedDBTransaction::TaskStack::~TaskStack() { clear(); } | 58 IndexedDBTransaction::TaskStack::~TaskStack() { clear(); } |
| 58 | 59 |
| 59 void IndexedDBTransaction::TaskStack::clear() { | 60 void IndexedDBTransaction::TaskStack::clear() { |
| 60 while (!stack_.empty()) | 61 while (!stack_.empty()) |
| 61 stack_.pop(); | 62 stack_.pop(); |
| 62 } | 63 } |
| 63 | 64 |
| 64 IndexedDBTransaction::Operation IndexedDBTransaction::TaskStack::pop() { | 65 IndexedDBTransaction::AbortOperation IndexedDBTransaction::TaskStack::pop() { |
| 65 DCHECK(!stack_.empty()); | 66 DCHECK(!stack_.empty()); |
| 66 Operation task(stack_.top()); | 67 AbortOperation task = std::move(stack_.top()); |
| 67 stack_.pop(); | 68 stack_.pop(); |
| 68 return task; | 69 return task; |
| 69 } | 70 } |
| 70 | 71 |
| 71 IndexedDBTransaction::IndexedDBTransaction( | 72 IndexedDBTransaction::IndexedDBTransaction( |
| 72 int64_t id, | 73 int64_t id, |
| 73 base::WeakPtr<IndexedDBConnection> connection, | 74 base::WeakPtr<IndexedDBConnection> connection, |
| 74 const std::set<int64_t>& object_store_ids, | 75 const std::set<int64_t>& object_store_ids, |
| 75 blink::WebIDBTransactionMode mode, | 76 blink::WebIDBTransactionMode mode, |
| 76 IndexedDBBackingStore::Transaction* backing_store_transaction) | 77 IndexedDBBackingStore::Transaction* backing_store_transaction) |
| 77 : id_(id), | 78 : id_(id), |
| 78 object_store_ids_(object_store_ids), | 79 object_store_ids_(object_store_ids), |
| 79 mode_(mode), | 80 mode_(mode), |
| 80 used_(false), | |
| 81 state_(CREATED), | |
| 82 commit_pending_(false), | |
| 83 connection_(std::move(connection)), | 81 connection_(std::move(connection)), |
| 84 transaction_(backing_store_transaction), | 82 transaction_(backing_store_transaction) { |
| 85 backing_store_transaction_begun_(false), | |
| 86 should_process_queue_(false), | |
| 87 pending_preemptive_events_(0) { | |
| 88 callbacks_ = connection_->callbacks(); | 83 callbacks_ = connection_->callbacks(); |
| 89 database_ = connection_->database(); | 84 database_ = connection_->database(); |
| 90 | 85 |
| 91 database_->transaction_coordinator().DidCreateTransaction(this); | 86 database_->transaction_coordinator().DidCreateTransaction(this); |
| 92 | 87 |
| 93 diagnostics_.tasks_scheduled = 0; | 88 diagnostics_.tasks_scheduled = 0; |
| 94 diagnostics_.tasks_completed = 0; | 89 diagnostics_.tasks_completed = 0; |
| 95 diagnostics_.creation_time = base::Time::Now(); | 90 diagnostics_.creation_time = base::Time::Now(); |
| 96 } | 91 } |
| 97 | 92 |
| 98 IndexedDBTransaction::~IndexedDBTransaction() { | 93 IndexedDBTransaction::~IndexedDBTransaction() { |
| 99 // It shouldn't be possible for this object to get deleted until it's either | 94 // It shouldn't be possible for this object to get deleted until it's either |
| 100 // complete or aborted. | 95 // complete or aborted. |
| 101 DCHECK_EQ(state_, FINISHED); | 96 DCHECK_EQ(state_, FINISHED); |
| 102 DCHECK(preemptive_task_queue_.empty()); | 97 DCHECK(preemptive_task_queue_.empty()); |
| 103 DCHECK_EQ(pending_preemptive_events_, 0); | 98 DCHECK_EQ(pending_preemptive_events_, 0); |
| 104 DCHECK(task_queue_.empty()); | 99 DCHECK(task_queue_.empty()); |
| 105 DCHECK(abort_task_stack_.empty()); | 100 DCHECK(abort_task_stack_.empty()); |
| 106 DCHECK(pending_observers_.empty()); | 101 DCHECK(pending_observers_.empty()); |
| 102 DCHECK(!processing_event_queue_); | |
| 107 } | 103 } |
| 108 | 104 |
| 109 void IndexedDBTransaction::ScheduleTask(blink::WebIDBTaskType type, | 105 void IndexedDBTransaction::ScheduleTask(blink::WebIDBTaskType type, |
| 110 Operation task) { | 106 Operation task) { |
| 111 DCHECK_NE(state_, COMMITTING); | 107 DCHECK_NE(state_, COMMITTING); |
| 112 if (state_ == FINISHED) | 108 if (state_ == FINISHED) |
| 113 return; | 109 return; |
| 114 | 110 |
| 115 timeout_timer_.Stop(); | 111 timeout_timer_.Stop(); |
| 116 used_ = true; | 112 used_ = true; |
| 117 if (type == blink::WebIDBTaskTypeNormal) { | 113 if (type == blink::WebIDBTaskTypeNormal) { |
| 118 task_queue_.push(task); | 114 task_queue_.push(task); |
| 119 ++diagnostics_.tasks_scheduled; | 115 ++diagnostics_.tasks_scheduled; |
| 120 } else { | 116 } else { |
| 121 preemptive_task_queue_.push(task); | 117 preemptive_task_queue_.push(task); |
| 122 } | 118 } |
| 123 RunTasksIfStarted(); | 119 RunTasksIfStarted(); |
| 124 } | 120 } |
| 125 | 121 |
| 126 void IndexedDBTransaction::ScheduleAbortTask(Operation abort_task) { | 122 void IndexedDBTransaction::ScheduleAbortTask(AbortOperation abort_task) { |
| 127 DCHECK_NE(FINISHED, state_); | 123 DCHECK_NE(FINISHED, state_); |
| 128 DCHECK(used_); | 124 DCHECK(used_); |
| 129 abort_task_stack_.push(abort_task); | 125 abort_task_stack_.push(std::move(abort_task)); |
| 130 } | 126 } |
| 131 | 127 |
| 132 void IndexedDBTransaction::RunTasksIfStarted() { | 128 void IndexedDBTransaction::RunTasksIfStarted() { |
| 133 DCHECK(used_); | 129 DCHECK(used_); |
| 134 | 130 |
| 135 // Not started by the coordinator yet. | 131 // Not started by the coordinator yet. |
| 136 if (state_ != STARTED) | 132 if (state_ != STARTED) |
| 137 return; | 133 return; |
| 138 | 134 |
| 139 // A task is already posted. | 135 // A task is already posted. |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 163 timeout_timer_.Stop(); | 159 timeout_timer_.Stop(); |
| 164 | 160 |
| 165 state_ = FINISHED; | 161 state_ = FINISHED; |
| 166 should_process_queue_ = false; | 162 should_process_queue_ = false; |
| 167 | 163 |
| 168 if (backing_store_transaction_begun_) | 164 if (backing_store_transaction_begun_) |
| 169 transaction_->Rollback(); | 165 transaction_->Rollback(); |
| 170 | 166 |
| 171 // Run the abort tasks, if any. | 167 // Run the abort tasks, if any. |
| 172 while (!abort_task_stack_.empty()) | 168 while (!abort_task_stack_.empty()) |
| 173 abort_task_stack_.pop().Run(NULL); | 169 abort_task_stack_.pop().Run(); |
| 174 | 170 |
| 175 preemptive_task_queue_.clear(); | 171 preemptive_task_queue_.clear(); |
| 176 pending_preemptive_events_ = 0; | 172 pending_preemptive_events_ = 0; |
| 177 task_queue_.clear(); | 173 task_queue_.clear(); |
| 178 | 174 |
| 179 // Backing store resources (held via cursors) must be released | 175 // Backing store resources (held via cursors) must be released |
| 180 // before script callbacks are fired, as the script callbacks may | 176 // before script callbacks are fired, as the script callbacks may |
| 181 // release references and allow the backing store itself to be | 177 // release references and allow the backing store itself to be |
| 182 // released, and order is critical. | 178 // released, and order is critical. |
| 183 CloseOpenCursors(); | 179 CloseOpenCursors(); |
| (...skipping 178 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 362 callbacks_->OnComplete(id_); | 358 callbacks_->OnComplete(id_); |
| 363 } | 359 } |
| 364 if (!pending_observers_.empty() && connection_) { | 360 if (!pending_observers_.empty() && connection_) { |
| 365 connection_->ActivatePendingObservers(std::move(pending_observers_)); | 361 connection_->ActivatePendingObservers(std::move(pending_observers_)); |
| 366 pending_observers_.clear(); | 362 pending_observers_.clear(); |
| 367 } | 363 } |
| 368 | 364 |
| 369 database_->TransactionFinished(this, true); | 365 database_->TransactionFinished(this, true); |
| 370 } else { | 366 } else { |
| 371 while (!abort_task_stack_.empty()) | 367 while (!abort_task_stack_.empty()) |
| 372 abort_task_stack_.pop().Run(NULL); | 368 abort_task_stack_.pop().Run(); |
| 373 | 369 |
| 374 IndexedDBDatabaseError error; | 370 IndexedDBDatabaseError error; |
| 375 if (leveldb_env::IndicatesDiskFull(s)) { | 371 if (leveldb_env::IndicatesDiskFull(s)) { |
| 376 error = IndexedDBDatabaseError( | 372 error = IndexedDBDatabaseError( |
| 377 blink::WebIDBDatabaseExceptionQuotaError, | 373 blink::WebIDBDatabaseExceptionQuotaError, |
| 378 "Encountered disk full while committing transaction."); | 374 "Encountered disk full while committing transaction."); |
| 379 } else { | 375 } else { |
| 380 error = IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError, | 376 error = IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError, |
| 381 "Internal error committing transaction."); | 377 "Internal error committing transaction."); |
| 382 } | 378 } |
| 383 callbacks_->OnAbort(id_, error); | 379 callbacks_->OnAbort(id_, error); |
| 384 | 380 |
| 385 database_->TransactionFinished(this, false); | 381 database_->TransactionFinished(this, false); |
| 386 database_->TransactionCommitFailed(s); | |
| 387 } | 382 } |
| 388 | 383 |
| 389 database_ = NULL; | 384 database_ = NULL; |
| 390 return s; | 385 return s; |
| 391 } | 386 } |
| 392 | 387 |
| 393 void IndexedDBTransaction::ProcessTaskQueue() { | 388 void IndexedDBTransaction::ProcessTaskQueue() { |
| 394 IDB_TRACE1("IndexedDBTransaction::ProcessTaskQueue", "txn.id", id()); | 389 IDB_TRACE1("IndexedDBTransaction::ProcessTaskQueue", "txn.id", id()); |
| 395 | 390 |
| 391 DCHECK(!processing_event_queue_); | |
| 392 | |
| 396 // May have been aborted. | 393 // May have been aborted. |
| 397 if (!should_process_queue_) | 394 if (!should_process_queue_) |
| 398 return; | 395 return; |
| 399 | 396 |
| 397 processing_event_queue_ = true; | |
| 398 | |
| 400 DCHECK(!IsTaskQueueEmpty()); | 399 DCHECK(!IsTaskQueueEmpty()); |
| 401 should_process_queue_ = false; | 400 should_process_queue_ = false; |
| 402 | 401 |
| 403 if (!backing_store_transaction_begun_) { | 402 if (!backing_store_transaction_begun_) { |
| 404 transaction_->Begin(); | 403 transaction_->Begin(); |
| 405 backing_store_transaction_begun_ = true; | 404 backing_store_transaction_begun_ = true; |
| 406 } | 405 } |
| 407 | 406 |
| 408 // The last reference to this object may be released while performing the | 407 // The last reference to this object may be released while performing the |
| 409 // tasks. Take take a self reference to keep this object alive so that | 408 // tasks. Take take a self reference to keep this object alive so that |
| 410 // the loop termination conditions can be checked. | 409 // the loop termination conditions can be checked. |
| 411 scoped_refptr<IndexedDBTransaction> protect(this); | 410 scoped_refptr<IndexedDBTransaction> protect(this); |
| 412 | 411 |
| 413 TaskQueue* task_queue = | 412 TaskQueue* task_queue = |
| 414 pending_preemptive_events_ ? &preemptive_task_queue_ : &task_queue_; | 413 pending_preemptive_events_ ? &preemptive_task_queue_ : &task_queue_; |
| 415 while (!task_queue->empty() && state_ != FINISHED) { | 414 while (!task_queue->empty() && state_ != FINISHED) { |
| 416 DCHECK_EQ(state_, STARTED); | 415 DCHECK_EQ(state_, STARTED); |
| 417 Operation task(task_queue->pop()); | 416 Operation task(task_queue->pop()); |
| 418 task.Run(this); | 417 OperationResult result = task.Run(this); |
| 419 if (!pending_preemptive_events_) { | 418 if (!pending_preemptive_events_) { |
| 420 DCHECK(diagnostics_.tasks_completed < diagnostics_.tasks_scheduled); | 419 DCHECK(diagnostics_.tasks_completed < diagnostics_.tasks_scheduled); |
| 421 ++diagnostics_.tasks_completed; | 420 ++diagnostics_.tasks_completed; |
| 422 } | 421 } |
| 422 if (!result.ok()) { | |
| 423 processing_event_queue_ = false; | |
| 424 scoped_refptr<IndexedDBDatabase> database = database_; | |
| 425 if (result.IsCorruption()) { | |
| 426 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError, | |
| 427 base::ASCIIToUTF16(result.ToString())); | |
|
cmumford
2016/11/21 18:29:22
I think that leveldb::Status can contain paths to
dmurph
2016/11/22 23:33:11
We don't actually send this back to the page, it's
cmumford
2016/11/28 20:48:25
Yes, but that file is read on the next db open in
| |
| 428 database->factory()->HandleBackingStoreCorruption(database_->origin(), | |
| 429 error); | |
| 430 } else { | |
| 431 database->factory()->HandleBackingStoreFailure(database_->origin()); | |
| 432 } | |
| 433 return; | |
| 434 } | |
| 423 | 435 |
| 424 // Event itself may change which queue should be processed next. | 436 // Event itself may change which queue should be processed next. |
| 425 task_queue = | 437 task_queue = |
| 426 pending_preemptive_events_ ? &preemptive_task_queue_ : &task_queue_; | 438 pending_preemptive_events_ ? &preemptive_task_queue_ : &task_queue_; |
| 427 } | 439 } |
| 428 | 440 |
| 429 // If there are no pending tasks, we haven't already committed/aborted, | 441 // If there are no pending tasks, we haven't already committed/aborted, |
| 430 // and the front-end requested a commit, it is now safe to do so. | 442 // and the front-end requested a commit, it is now safe to do so. |
| 431 if (!HasPendingTasks() && state_ != FINISHED && commit_pending_) { | 443 if (!HasPendingTasks() && state_ != FINISHED && commit_pending_) { |
| 432 Commit(); | 444 Commit(); |
| 445 processing_event_queue_ = false; | |
| 433 return; | 446 return; |
| 434 } | 447 } |
| 435 | 448 |
| 436 // The transaction may have been aborted while processing tasks. | 449 // The transaction may have been aborted while processing tasks. |
| 437 if (state_ == FINISHED) | 450 if (state_ == FINISHED) { |
| 451 processing_event_queue_ = false; | |
| 438 return; | 452 return; |
| 453 } | |
| 439 | 454 |
| 440 DCHECK(state_ == STARTED); | 455 DCHECK(state_ == STARTED); |
| 441 | 456 |
| 442 // Otherwise, start a timer in case the front-end gets wedged and | 457 // Otherwise, start a timer in case the front-end gets wedged and |
| 443 // never requests further activity. Read-only transactions don't | 458 // never requests further activity. Read-only transactions don't |
| 444 // block other transactions, so don't time those out. | 459 // block other transactions, so don't time those out. |
| 445 if (mode_ != blink::WebIDBTransactionModeReadOnly) { | 460 if (mode_ != blink::WebIDBTransactionModeReadOnly) { |
| 446 timeout_timer_.Start(FROM_HERE, GetInactivityTimeout(), | 461 timeout_timer_.Start(FROM_HERE, GetInactivityTimeout(), |
| 447 base::Bind(&IndexedDBTransaction::Timeout, this)); | 462 base::Bind(&IndexedDBTransaction::Timeout, this)); |
| 448 } | 463 } |
| 464 processing_event_queue_ = false; | |
| 449 } | 465 } |
| 450 | 466 |
| 451 base::TimeDelta IndexedDBTransaction::GetInactivityTimeout() const { | 467 base::TimeDelta IndexedDBTransaction::GetInactivityTimeout() const { |
| 452 return base::TimeDelta::FromSeconds(kInactivityTimeoutPeriodSeconds); | 468 return base::TimeDelta::FromSeconds(kInactivityTimeoutPeriodSeconds); |
| 453 } | 469 } |
| 454 | 470 |
| 455 void IndexedDBTransaction::Timeout() { | 471 void IndexedDBTransaction::Timeout() { |
| 456 Abort(IndexedDBDatabaseError( | 472 Abort(IndexedDBDatabaseError( |
| 457 blink::WebIDBDatabaseExceptionTimeoutError, | 473 blink::WebIDBDatabaseExceptionTimeoutError, |
| 458 base::ASCIIToUTF16("Transaction timed out due to inactivity."))); | 474 base::ASCIIToUTF16("Transaction timed out due to inactivity."))); |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 498 | 514 |
| 499 void IndexedDBTransaction::RecordObserverForLastObservation( | 515 void IndexedDBTransaction::RecordObserverForLastObservation( |
| 500 int32_t connection_id, | 516 int32_t connection_id, |
| 501 int32_t observer_id) { | 517 int32_t observer_id) { |
| 502 auto it = connection_changes_map_.find(connection_id); | 518 auto it = connection_changes_map_.find(connection_id); |
| 503 DCHECK(it != connection_changes_map_.end()); | 519 DCHECK(it != connection_changes_map_.end()); |
| 504 it->second->RecordObserverForLastObservation(observer_id); | 520 it->second->RecordObserverForLastObservation(observer_id); |
| 505 } | 521 } |
| 506 | 522 |
| 507 } // namespace content | 523 } // namespace content |
| OLD | NEW |