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