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
11namespace content {
12
13IndexedDBTransactionCoordinator::IndexedDBTransactionCoordinator() {}
14
15IndexedDBTransactionCoordinator::~IndexedDBTransactionCoordinator() {
16  DCHECK(!transactions_.size());
17  DCHECK(!queued_transactions_.size());
18  DCHECK(!started_transactions_.size());
19}
20
21void IndexedDBTransactionCoordinator::DidCreateTransaction(
22    IndexedDBTransaction* transaction) {
23  DCHECK(transactions_.find(transaction) == transactions_.end());
24  transactions_[transaction] = transaction;
25}
26
27void IndexedDBTransactionCoordinator::DidStartTransaction(
28    IndexedDBTransaction* transaction) {
29  DCHECK(transactions_.find(transaction) != transactions_.end());
30
31  queued_transactions_.insert(transaction);
32  ProcessStartedTransactions();
33}
34
35void IndexedDBTransactionCoordinator::DidFinishTransaction(
36    IndexedDBTransaction* transaction) {
37  DCHECK(transactions_.find(transaction) != transactions_.end());
38
39  if (queued_transactions_.has(transaction)) {
40    DCHECK(!started_transactions_.has(transaction));
41    queued_transactions_.erase(transaction);
42  } else {
43    if (started_transactions_.has(transaction))
44      started_transactions_.erase(transaction);
45  }
46  transactions_.erase(transaction);
47
48  ProcessStartedTransactions();
49}
50
51#ifndef NDEBUG
52// Verifies internal consistency while returning whether anything is found.
53bool IndexedDBTransactionCoordinator::IsActive(
54    IndexedDBTransaction* transaction) {
55  bool found = false;
56  if (queued_transactions_.has(transaction))
57    found = true;
58  if (started_transactions_.has(transaction)) {
59    DCHECK(!found);
60    found = true;
61  }
62  DCHECK_EQ(found, (transactions_.find(transaction) != transactions_.end()));
63  return found;
64}
65#endif
66
67std::vector<const IndexedDBTransaction*>
68IndexedDBTransactionCoordinator::GetTransactions() const {
69  std::vector<const IndexedDBTransaction*> result;
70
71  for (list_set<IndexedDBTransaction*>::const_iterator it =
72           started_transactions_.begin();
73       it != started_transactions_.end();
74       ++it) {
75    result.push_back(*it);
76  }
77  for (list_set<IndexedDBTransaction*>::const_iterator it =
78           queued_transactions_.begin();
79       it != queued_transactions_.end();
80       ++it) {
81    result.push_back(*it);
82  }
83
84  return result;
85}
86
87void IndexedDBTransactionCoordinator::ProcessStartedTransactions() {
88  if (queued_transactions_.empty())
89    return;
90
91  DCHECK(started_transactions_.empty() ||
92         (*started_transactions_.begin())->mode() !=
93             indexed_db::TRANSACTION_VERSION_CHANGE);
94
95  list_set<IndexedDBTransaction*>::const_iterator it =
96      queued_transactions_.begin();
97  while (it != queued_transactions_.end()) {
98    IndexedDBTransaction* transaction = *it;
99    ++it;
100    if (CanRunTransaction(transaction)) {
101      queued_transactions_.erase(transaction);
102      started_transactions_.insert(transaction);
103      transaction->Run();
104    }
105  }
106}
107
108static bool DoScopesOverlap(const std::set<int64>& scope1,
109                            const std::set<int64>& scope2) {
110  for (std::set<int64>::const_iterator it = scope1.begin(); it != scope1.end();
111       ++it) {
112    if (scope2.find(*it) != scope2.end())
113      return true;
114  }
115  return false;
116}
117
118bool IndexedDBTransactionCoordinator::CanRunTransaction(
119    IndexedDBTransaction* transaction) {
120  DCHECK(queued_transactions_.has(transaction));
121  switch (transaction->mode()) {
122    case indexed_db::TRANSACTION_VERSION_CHANGE:
123      DCHECK_EQ(static_cast<size_t>(1), queued_transactions_.size());
124      DCHECK(started_transactions_.empty());
125      return true;
126
127    case indexed_db::TRANSACTION_READ_ONLY:
128      return true;
129
130    case indexed_db::TRANSACTION_READ_WRITE:
131      for (list_set<IndexedDBTransaction*>::const_iterator it =
132               started_transactions_.begin();
133           it != started_transactions_.end();
134           ++it) {
135        IndexedDBTransaction* other = *it;
136        if (other->mode() == indexed_db::TRANSACTION_READ_WRITE &&
137            DoScopesOverlap(transaction->scope(), other->scope()))
138          return false;
139      }
140      for (list_set<IndexedDBTransaction*>::const_iterator it =
141               queued_transactions_.begin();
142           *it != transaction;
143           ++it) {
144        DCHECK(it != queued_transactions_.end());
145        IndexedDBTransaction* other = *it;
146        if (other->mode() == indexed_db::TRANSACTION_READ_WRITE &&
147            DoScopesOverlap(transaction->scope(), other->scope()))
148          return false;
149      }
150      return true;
151  }
152  NOTREACHED();
153  return false;
154}
155
156}  // namespace content
157