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
IndexedDBTransactionCoordinator()14 IndexedDBTransactionCoordinator::IndexedDBTransactionCoordinator() {}
15
~IndexedDBTransactionCoordinator()16 IndexedDBTransactionCoordinator::~IndexedDBTransactionCoordinator() {
17 DCHECK(!queued_transactions_.size());
18 DCHECK(!started_transactions_.size());
19 }
20
DidCreateTransaction(scoped_refptr<IndexedDBTransaction> transaction)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
DidFinishTransaction(IndexedDBTransaction * transaction)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
IsRunningVersionChangeTransaction() const44 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.
IsActive(IndexedDBTransaction * transaction)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*>
GetTransactions() const67 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
ProcessQueuedTransactions()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>
DoSetsIntersect(const std::set<T> & set1,const std::set<T> & set2)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
CanStartTransaction(IndexedDBTransaction * const transaction,const std::set<int64> & locked_scope) const146 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