blob: 56a7c9983b06294828e0d1c707987858fc6a6c08 [file] [log] [blame]
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/indexed_db/indexed_db_transaction_coordinator.h"
#include "base/basictypes.h"
#include "base/logging.h"
#include "content/browser/indexed_db/indexed_db_transaction.h"
namespace content {
IndexedDBTransactionCoordinator::IndexedDBTransactionCoordinator() {}
IndexedDBTransactionCoordinator::~IndexedDBTransactionCoordinator() {
DCHECK(!transactions_.size());
DCHECK(!queued_transactions_.size());
DCHECK(!started_transactions_.size());
}
void IndexedDBTransactionCoordinator::DidCreateTransaction(
IndexedDBTransaction* transaction) {
DCHECK(transactions_.find(transaction) == transactions_.end());
DCHECK(transaction->queue_status() == IndexedDBTransaction::CREATED);
transactions_[transaction] = transaction;
}
void IndexedDBTransactionCoordinator::DidStartTransaction(
IndexedDBTransaction* transaction) {
DCHECK(transactions_.find(transaction) != transactions_.end());
queued_transactions_.insert(transaction);
ProcessStartedTransactions();
}
void IndexedDBTransactionCoordinator::DidFinishTransaction(
IndexedDBTransaction* transaction) {
DCHECK(transactions_.find(transaction) != transactions_.end());
if (queued_transactions_.count(transaction)) {
DCHECK(!started_transactions_.count(transaction));
queued_transactions_.erase(transaction);
} else {
if (started_transactions_.count(transaction))
started_transactions_.erase(transaction);
}
transactions_.erase(transaction);
ProcessStartedTransactions();
}
#ifndef NDEBUG
// Verifies internal consistency while returning whether anything is found.
bool IndexedDBTransactionCoordinator::IsActive(
IndexedDBTransaction* transaction) {
bool found = false;
if (queued_transactions_.count(transaction))
found = true;
if (started_transactions_.count(transaction)) {
DCHECK(!found);
found = true;
}
DCHECK_EQ(found, (transactions_.find(transaction) != transactions_.end()));
return found;
}
#endif
std::vector<const IndexedDBTransaction*>
IndexedDBTransactionCoordinator::GetTransactions() const {
std::vector<const IndexedDBTransaction*> result;
for (list_set<IndexedDBTransaction*>::const_iterator it =
started_transactions_.begin();
it != started_transactions_.end();
++it) {
result.push_back(*it);
}
for (list_set<IndexedDBTransaction*>::const_iterator it =
queued_transactions_.begin();
it != queued_transactions_.end();
++it) {
result.push_back(*it);
}
return result;
}
void IndexedDBTransactionCoordinator::ProcessStartedTransactions() {
if (queued_transactions_.empty())
return;
DCHECK(started_transactions_.empty() ||
(*started_transactions_.begin())->mode() !=
indexed_db::TRANSACTION_VERSION_CHANGE);
// The locked_scope set accumulates the ids of object stores in the scope of
// running read/write transactions. Other read-write transactions with
// stores in this set may not be started. Read-only transactions may start,
// taking a snapshot of the database, which does not include uncommitted
// data. ("Version change" transactions are exclusive, but handled by the
// connection sequencing in IndexedDBDatabase.)
std::set<int64> locked_scope;
for (list_set<IndexedDBTransaction*>::const_iterator it =
started_transactions_.begin();
it != started_transactions_.end();
++it) {
IndexedDBTransaction* transaction = *it;
if (transaction->mode() == indexed_db::TRANSACTION_READ_WRITE) {
// Running read/write transactions have exclusive access to the object
// stores within their scopes.
locked_scope.insert(transaction->scope().begin(),
transaction->scope().end());
}
}
list_set<IndexedDBTransaction*>::const_iterator it =
queued_transactions_.begin();
while (it != queued_transactions_.end()) {
IndexedDBTransaction* transaction = *it;
++it;
if (CanRunTransaction(transaction, locked_scope)) {
transaction->set_queue_status(IndexedDBTransaction::UNBLOCKED);
queued_transactions_.erase(transaction);
started_transactions_.insert(transaction);
transaction->Run();
} else {
transaction->set_queue_status(IndexedDBTransaction::BLOCKED);
}
if (transaction->mode() == indexed_db::TRANSACTION_READ_WRITE) {
// Either the transaction started, so it has exclusive access to the
// stores in its scope, or per the spec the transaction which was
// created first must get access first, so the stores are also locked.
locked_scope.insert(transaction->scope().begin(),
transaction->scope().end());
}
}
}
template<typename T>
static bool DoSetsIntersect(const std::set<T>& set1,
const std::set<T>& set2) {
typename std::set<T>::const_iterator it1 = set1.begin();
typename std::set<T>::const_iterator it2 = set2.begin();
while (it1 != set1.end() && it2 != set2.end()) {
if (*it1 < *it2)
++it1;
else if (*it2 < *it1)
++it2;
else
return true;
}
return false;
}
bool IndexedDBTransactionCoordinator::CanRunTransaction(
IndexedDBTransaction* const transaction,
const std::set<int64>& locked_scope) const {
DCHECK(queued_transactions_.count(transaction));
switch (transaction->mode()) {
case indexed_db::TRANSACTION_VERSION_CHANGE:
DCHECK_EQ(static_cast<size_t>(1), queued_transactions_.size());
DCHECK(started_transactions_.empty());
DCHECK(locked_scope.empty());
return true;
case indexed_db::TRANSACTION_READ_ONLY:
return true;
case indexed_db::TRANSACTION_READ_WRITE:
return !DoSetsIntersect(transaction->scope(), locked_scope);
}
NOTREACHED();
return false;
}
} // namespace content