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 #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