This source file includes following definitions.
- clear
- pop
- clear
- pop
- pending_preemptive_events_
- ScheduleTask
- ScheduleTask
- RunTasksIfStarted
- Abort
- Abort
- IsTaskQueueEmpty
- HasPendingTasks
- RegisterOpenCursor
- UnregisterOpenCursor
- Start
- Commit
- ProcessTaskQueue
- Timeout
- CloseOpenCursors
#include "content/browser/indexed_db/indexed_db_transaction.h"
#include "base/bind.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "content/browser/indexed_db/indexed_db_backing_store.h"
#include "content/browser/indexed_db/indexed_db_cursor.h"
#include "content/browser/indexed_db/indexed_db_database.h"
#include "content/browser/indexed_db/indexed_db_database_callbacks.h"
#include "content/browser/indexed_db/indexed_db_tracing.h"
#include "content/browser/indexed_db/indexed_db_transaction_coordinator.h"
#include "third_party/WebKit/public/platform/WebIDBDatabaseException.h"
namespace content {
const int64 kInactivityTimeoutPeriodSeconds = 60;
IndexedDBTransaction::TaskQueue::TaskQueue() {}
IndexedDBTransaction::TaskQueue::~TaskQueue() { clear(); }
void IndexedDBTransaction::TaskQueue::clear() {
while (!queue_.empty())
queue_.pop();
}
IndexedDBTransaction::Operation IndexedDBTransaction::TaskQueue::pop() {
DCHECK(!queue_.empty());
Operation task(queue_.front());
queue_.pop();
return task;
}
IndexedDBTransaction::TaskStack::TaskStack() {}
IndexedDBTransaction::TaskStack::~TaskStack() { clear(); }
void IndexedDBTransaction::TaskStack::clear() {
while (!stack_.empty())
stack_.pop();
}
IndexedDBTransaction::Operation IndexedDBTransaction::TaskStack::pop() {
DCHECK(!stack_.empty());
Operation task(stack_.top());
stack_.pop();
return task;
}
IndexedDBTransaction::IndexedDBTransaction(
int64 id,
scoped_refptr<IndexedDBDatabaseCallbacks> callbacks,
const std::set<int64>& object_store_ids,
indexed_db::TransactionMode mode,
IndexedDBDatabase* database,
IndexedDBBackingStore::Transaction* backing_store_transaction)
: id_(id),
object_store_ids_(object_store_ids),
mode_(mode),
used_(false),
state_(CREATED),
commit_pending_(false),
callbacks_(callbacks),
database_(database),
transaction_(backing_store_transaction),
backing_store_transaction_begun_(false),
should_process_queue_(false),
pending_preemptive_events_(0) {
database_->transaction_coordinator().DidCreateTransaction(this);
diagnostics_.tasks_scheduled = 0;
diagnostics_.tasks_completed = 0;
diagnostics_.creation_time = base::Time::Now();
}
IndexedDBTransaction::~IndexedDBTransaction() {
DCHECK_EQ(state_, FINISHED);
DCHECK(preemptive_task_queue_.empty());
DCHECK(task_queue_.empty());
DCHECK(abort_task_stack_.empty());
}
void IndexedDBTransaction::ScheduleTask(Operation task, Operation abort_task) {
if (state_ == FINISHED)
return;
timeout_timer_.Stop();
used_ = true;
task_queue_.push(task);
++diagnostics_.tasks_scheduled;
abort_task_stack_.push(abort_task);
RunTasksIfStarted();
}
void IndexedDBTransaction::ScheduleTask(IndexedDBDatabase::TaskType type,
Operation task) {
if (state_ == FINISHED)
return;
timeout_timer_.Stop();
used_ = true;
if (type == IndexedDBDatabase::NORMAL_TASK) {
task_queue_.push(task);
++diagnostics_.tasks_scheduled;
} else {
preemptive_task_queue_.push(task);
}
RunTasksIfStarted();
}
void IndexedDBTransaction::RunTasksIfStarted() {
DCHECK(used_);
if (state_ != STARTED)
return;
if (should_process_queue_)
return;
should_process_queue_ = true;
base::MessageLoop::current()->PostTask(
FROM_HERE, base::Bind(&IndexedDBTransaction::ProcessTaskQueue, this));
}
void IndexedDBTransaction::Abort() {
Abort(IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
"Internal error (unknown cause)"));
}
void IndexedDBTransaction::Abort(const IndexedDBDatabaseError& error) {
IDB_TRACE("IndexedDBTransaction::Abort");
if (state_ == FINISHED)
return;
scoped_refptr<IndexedDBTransaction> protect(this);
timeout_timer_.Stop();
state_ = FINISHED;
should_process_queue_ = false;
if (backing_store_transaction_begun_)
transaction_->Rollback();
while (!abort_task_stack_.empty())
abort_task_stack_.pop().Run(NULL);
preemptive_task_queue_.clear();
task_queue_.clear();
CloseOpenCursors();
transaction_->Reset();
database_->transaction_coordinator().DidFinishTransaction(this);
#ifndef NDEBUG
DCHECK(!database_->transaction_coordinator().IsActive(this));
#endif
if (callbacks_.get())
callbacks_->OnAbort(id_, error);
database_->TransactionFinished(this, false);
database_ = NULL;
}
bool IndexedDBTransaction::IsTaskQueueEmpty() const {
return preemptive_task_queue_.empty() && task_queue_.empty();
}
bool IndexedDBTransaction::HasPendingTasks() const {
return pending_preemptive_events_ || !IsTaskQueueEmpty();
}
void IndexedDBTransaction::RegisterOpenCursor(IndexedDBCursor* cursor) {
open_cursors_.insert(cursor);
}
void IndexedDBTransaction::UnregisterOpenCursor(IndexedDBCursor* cursor) {
open_cursors_.erase(cursor);
}
void IndexedDBTransaction::Start() {
DCHECK_EQ(CREATED, state_);
state_ = STARTED;
diagnostics_.start_time = base::Time::Now();
if (!used_)
return;
RunTasksIfStarted();
}
void IndexedDBTransaction::Commit() {
IDB_TRACE("IndexedDBTransaction::Commit");
if (state_ == FINISHED)
return;
DCHECK(!used_ || state_ == STARTED);
commit_pending_ = true;
if (HasPendingTasks())
return;
scoped_refptr<IndexedDBTransaction> protect(this);
timeout_timer_.Stop();
state_ = FINISHED;
bool committed = !used_ || transaction_->Commit().ok();
CloseOpenCursors();
transaction_->Reset();
database_->transaction_coordinator().DidFinishTransaction(this);
if (committed) {
abort_task_stack_.clear();
callbacks_->OnComplete(id_);
database_->TransactionFinished(this, true);
} else {
while (!abort_task_stack_.empty())
abort_task_stack_.pop().Run(NULL);
callbacks_->OnAbort(
id_,
IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
"Internal error committing transaction."));
database_->TransactionFinished(this, false);
database_->TransactionCommitFailed();
}
database_ = NULL;
}
void IndexedDBTransaction::ProcessTaskQueue() {
IDB_TRACE("IndexedDBTransaction::ProcessTaskQueue");
if (!should_process_queue_)
return;
DCHECK(!IsTaskQueueEmpty());
should_process_queue_ = false;
if (!backing_store_transaction_begun_) {
transaction_->Begin();
backing_store_transaction_begun_ = true;
}
scoped_refptr<IndexedDBTransaction> protect(this);
TaskQueue* task_queue =
pending_preemptive_events_ ? &preemptive_task_queue_ : &task_queue_;
while (!task_queue->empty() && state_ != FINISHED) {
DCHECK_EQ(STARTED, state_);
Operation task(task_queue->pop());
task.Run(this);
if (!pending_preemptive_events_) {
DCHECK(diagnostics_.tasks_completed < diagnostics_.tasks_scheduled);
++diagnostics_.tasks_completed;
}
task_queue =
pending_preemptive_events_ ? &preemptive_task_queue_ : &task_queue_;
}
if (!HasPendingTasks() && state_ != FINISHED && commit_pending_) {
Commit();
return;
}
if (state_ == FINISHED)
return;
if (mode_ != indexed_db::TRANSACTION_READ_ONLY) {
timeout_timer_.Start(
FROM_HERE,
base::TimeDelta::FromSeconds(kInactivityTimeoutPeriodSeconds),
base::Bind(&IndexedDBTransaction::Timeout, this));
}
}
void IndexedDBTransaction::Timeout() {
Abort(IndexedDBDatabaseError(
blink::WebIDBDatabaseExceptionTimeoutError,
base::ASCIIToUTF16("Transaction timed out due to inactivity.")));
}
void IndexedDBTransaction::CloseOpenCursors() {
for (std::set<IndexedDBCursor*>::iterator i = open_cursors_.begin();
i != open_cursors_.end();
++i)
(*i)->Close();
open_cursors_.clear();
}
}