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

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

Issue 2506773002: [IndexedDB] Integrating failures and corruption with transaction (Closed)
Patch Set: Created 4 years, 1 month 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
OLDNEW
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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698