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_coordinator.h" 6 7 #include "base/basictypes.h" 8 #include "base/logging.h" 9 #include "content/browser/indexed_db/indexed_db_transaction.h" 10 #include "third_party/WebKit/public/platform/WebIDBTypes.h" 11 12 namespace content { 13 14 IndexedDBTransactionCoordinator::IndexedDBTransactionCoordinator() {} 15 16 IndexedDBTransactionCoordinator::~IndexedDBTransactionCoordinator() { 17 DCHECK(!queued_transactions_.size()); 18 DCHECK(!started_transactions_.size()); 19 } 20 21 void IndexedDBTransactionCoordinator::DidCreateTransaction( 22 scoped_refptr<IndexedDBTransaction> transaction) { 23 DCHECK(!queued_transactions_.count(transaction)); 24 DCHECK(!started_transactions_.count(transaction)); 25 DCHECK_EQ(IndexedDBTransaction::CREATED, transaction->state()); 26 27 queued_transactions_.insert(transaction); 28 ProcessQueuedTransactions(); 29 } 30 31 void IndexedDBTransactionCoordinator::DidFinishTransaction( 32 IndexedDBTransaction* transaction) { 33 if (queued_transactions_.count(transaction)) { 34 DCHECK(!started_transactions_.count(transaction)); 35 queued_transactions_.erase(transaction); 36 } else { 37 DCHECK(started_transactions_.count(transaction)); 38 started_transactions_.erase(transaction); 39 } 40 41 ProcessQueuedTransactions(); 42 } 43 44 bool IndexedDBTransactionCoordinator::IsRunningVersionChangeTransaction() 45 const { 46 return !started_transactions_.empty() && 47 (*started_transactions_.begin())->mode() == 48 blink::WebIDBTransactionModeVersionChange; 49 } 50 51 #ifndef NDEBUG 52 // Verifies internal consistency while returning whether anything is found. 53 bool IndexedDBTransactionCoordinator::IsActive( 54 IndexedDBTransaction* transaction) { 55 bool found = false; 56 if (queued_transactions_.count(transaction)) 57 found = true; 58 if (started_transactions_.count(transaction)) { 59 DCHECK(!found); 60 found = true; 61 } 62 return found; 63 } 64 #endif 65 66 std::vector<const IndexedDBTransaction*> 67 IndexedDBTransactionCoordinator::GetTransactions() const { 68 std::vector<const IndexedDBTransaction*> result; 69 70 for (TransactionSet::const_iterator it = started_transactions_.begin(); 71 it != started_transactions_.end(); 72 ++it) { 73 result.push_back(it->get()); 74 } 75 for (TransactionSet::const_iterator it = queued_transactions_.begin(); 76 it != queued_transactions_.end(); 77 ++it) { 78 result.push_back(it->get()); 79 } 80 81 return result; 82 } 83 84 void IndexedDBTransactionCoordinator::ProcessQueuedTransactions() { 85 if (queued_transactions_.empty()) 86 return; 87 88 DCHECK(!IsRunningVersionChangeTransaction()); 89 90 // The locked_scope set accumulates the ids of object stores in the scope of 91 // running read/write transactions. Other read-write transactions with 92 // stores in this set may not be started. Read-only transactions may start, 93 // taking a snapshot of the database, which does not include uncommitted 94 // data. ("Version change" transactions are exclusive, but handled by the 95 // connection sequencing in IndexedDBDatabase.) 96 std::set<int64> locked_scope; 97 for (TransactionSet::const_iterator it = started_transactions_.begin(); 98 it != started_transactions_.end(); 99 ++it) { 100 IndexedDBTransaction* transaction = it->get(); 101 if (transaction->mode() == blink::WebIDBTransactionModeReadWrite) { 102 // Started read/write transactions have exclusive access to the object 103 // stores within their scopes. 104 locked_scope.insert(transaction->scope().begin(), 105 transaction->scope().end()); 106 } 107 } 108 109 TransactionSet::const_iterator it = queued_transactions_.begin(); 110 while (it != queued_transactions_.end()) { 111 scoped_refptr<IndexedDBTransaction> transaction = *it; 112 ++it; 113 if (CanStartTransaction(transaction.get(), locked_scope)) { 114 DCHECK_EQ(IndexedDBTransaction::CREATED, transaction->state()); 115 queued_transactions_.erase(transaction); 116 started_transactions_.insert(transaction); 117 transaction->Start(); 118 DCHECK_EQ(IndexedDBTransaction::STARTED, transaction->state()); 119 } 120 if (transaction->mode() == blink::WebIDBTransactionModeReadWrite) { 121 // Either the transaction started, so it has exclusive access to the 122 // stores in its scope, or per the spec the transaction which was 123 // created first must get access first, so the stores are also locked. 124 locked_scope.insert(transaction->scope().begin(), 125 transaction->scope().end()); 126 } 127 } 128 } 129 130 template<typename T> 131 static bool DoSetsIntersect(const std::set<T>& set1, 132 const std::set<T>& set2) { 133 typename std::set<T>::const_iterator it1 = set1.begin(); 134 typename std::set<T>::const_iterator it2 = set2.begin(); 135 while (it1 != set1.end() && it2 != set2.end()) { 136 if (*it1 < *it2) 137 ++it1; 138 else if (*it2 < *it1) 139 ++it2; 140 else 141 return true; 142 } 143 return false; 144 } 145 146 bool IndexedDBTransactionCoordinator::CanStartTransaction( 147 IndexedDBTransaction* const transaction, 148 const std::set<int64>& locked_scope) const { 149 DCHECK(queued_transactions_.count(transaction)); 150 switch (transaction->mode()) { 151 case blink::WebIDBTransactionModeVersionChange: 152 DCHECK_EQ(1u, queued_transactions_.size()); 153 DCHECK(started_transactions_.empty()); 154 DCHECK(locked_scope.empty()); 155 return true; 156 157 case blink::WebIDBTransactionModeReadOnly: 158 return true; 159 160 case blink::WebIDBTransactionModeReadWrite: 161 return !DoSetsIntersect(transaction->scope(), locked_scope); 162 } 163 NOTREACHED(); 164 return false; 165 } 166 167 } // namespace content 168