Home | History | Annotate | Download | only in indexed_db
      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