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