Chromium Code Reviews| Index: content/browser/indexed_db/indexed_db_transaction.cc |
| diff --git a/content/browser/indexed_db/indexed_db_transaction.cc b/content/browser/indexed_db/indexed_db_transaction.cc |
| index ee38d0b914d84be5a3e9dceecde3a027bf60371e..4f3f0adeee3905ee3650ebe7fa79a54d24f5ac19 100644 |
| --- a/content/browser/indexed_db/indexed_db_transaction.cc |
| +++ b/content/browser/indexed_db/indexed_db_transaction.cc |
| @@ -59,17 +59,21 @@ IndexedDBTransaction::IndexedDBTransaction( |
| mode_(mode), |
| state_(UNUSED), |
| commit_pending_(false), |
| + blob_write_success_(false), |
| callbacks_(callbacks), |
| database_(database), |
| transaction_(database->BackingStore().get()), |
| should_process_queue_(false), |
| pending_preemptive_events_(0) { |
| + fprintf(stderr, "ERICU: IndexedDBTransaction::IndexedDBTransaction(%p).\n", |
| + this); |
| database_->transaction_coordinator().DidCreateTransaction(this); |
| } |
| IndexedDBTransaction::~IndexedDBTransaction() { |
| // It shouldn't be possible for this object to get deleted until it's either |
| // complete or aborted. |
| + fprintf(stderr, "ERICU: IndexedDBTransaction::~IndexedDBTransaction.\n"); |
| DCHECK_EQ(state_, FINISHED); |
| DCHECK(preemptive_task_queue_.empty()); |
| DCHECK(task_queue_.empty()); |
| @@ -86,7 +90,7 @@ void IndexedDBTransaction::ScheduleTask(Operation task, Operation abort_task) { |
| void IndexedDBTransaction::ScheduleTask(IndexedDBDatabase::TaskType type, |
| Operation task) { |
| - if (state_ == FINISHED) |
| + if (IsStatePastRunning()) |
| return; |
| if (type == IndexedDBDatabase::NORMAL_TASK) |
| @@ -195,13 +199,29 @@ void IndexedDBTransaction::Start() { |
| database_->TransactionStarted(this); |
| } |
| +class BlobWriteCallbackImpl : public IndexedDBBackingStore::BlobWriteCallback { |
| + public: |
| + BlobWriteCallbackImpl(scoped_refptr<IndexedDBTransaction> transaction) |
| + : transaction_(transaction) { } |
| + virtual ~BlobWriteCallbackImpl() { } |
| + virtual void didSucceed() { |
| + transaction_->BlobWriteComplete(true); |
| + } |
| + virtual void didFail() { |
| + transaction_->BlobWriteComplete(false); |
| + } |
| + private: |
| + scoped_refptr<IndexedDBTransaction> transaction_; |
| +}; |
| + |
| void IndexedDBTransaction::Commit() { |
| IDB_TRACE("IndexedDBTransaction::Commit"); |
| + fprintf(stderr, "ERICU: IndexedDBTransaction::Commit.\n"); |
| // In multiprocess ports, front-end may have requested a commit but |
| // an abort has already been initiated asynchronously by the |
| // back-end. |
| - if (state_ == FINISHED) |
| + if (IsStatePastRunning()) |
| return; |
| DCHECK(state_ == UNUSED || state_ == RUNNING); |
| @@ -213,44 +233,19 @@ void IndexedDBTransaction::Commit() { |
| if (HasPendingTasks()) |
| return; |
| - // The last reference to this object may be released while performing the |
| - // commit steps below. We therefore take a self reference to keep ourselves |
| - // alive while executing this method. |
| - scoped_refptr<IndexedDBTransaction> protect(this); |
| - |
| - // TODO(jsbell): Run abort tasks if commit fails? http://crbug.com/241843 |
| - abort_task_stack_.clear(); |
| - |
| bool unused = state_ == UNUSED; |
| - state_ = FINISHED; |
| - |
| - bool committed = unused || transaction_.Commit(); |
| - |
| - // Backing store resources (held via cursors) must be released |
| - // before script callbacks are fired, as the script callbacks may |
| - // release references and allow the backing store itself to be |
| - // released, and order is critical. |
| - CloseOpenCursors(); |
| - transaction_.Reset(); |
| - |
| - // Transactions must also be marked as completed before the |
| - // front-end is notified, as the transaction completion unblocks |
| - // operations like closing connections. |
| - database_->transaction_coordinator().DidFinishTransaction(this); |
| - database_->TransactionFinished(this); |
| - |
| - if (committed) { |
| - callbacks_->OnComplete(id_); |
| - database_->TransactionFinishedAndCompleteFired(this); |
| - } else { |
| - callbacks_->OnAbort( |
| - id_, |
| - IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError, |
| - "Internal error committing transaction.")); |
| - database_->TransactionFinishedAndAbortFired(this); |
| + state_ = COMMITTING; |
| + |
| + if (unused) |
| + CommitPhaseTwo(WAS_NOT_USED, DO_COMMIT); |
| + else { |
| + scoped_refptr<IndexedDBBackingStore::BlobWriteCallback> callback( |
| + new BlobWriteCallbackImpl(this)); |
| + // CommitPhaseOne will call the callback synchronously if there are no blobs |
| + // to write. |
| + if (!transaction_.CommitPhaseOne(callback)) |
| + CommitPhaseTwo(WAS_USED, SKIP_COMMIT); |
| } |
| - |
| - database_ = NULL; |
| } |
| void IndexedDBTransaction::ProcessTaskQueue() { |
| @@ -275,7 +270,7 @@ void IndexedDBTransaction::ProcessTaskQueue() { |
| TaskQueue* task_queue = |
| pending_preemptive_events_ ? &preemptive_task_queue_ : &task_queue_; |
| - while (!task_queue->empty() && state_ != FINISHED) { |
| + while (!task_queue->empty() && !IsStatePastRunning()) { |
| DCHECK_EQ(state_, RUNNING); |
| Operation task(task_queue->pop()); |
| task.Run(this); |
| @@ -287,10 +282,23 @@ void IndexedDBTransaction::ProcessTaskQueue() { |
| // If there are no pending tasks, we haven't already committed/aborted, |
| // and the front-end requested a commit, it is now safe to do so. |
| - if (!HasPendingTasks() && state_ != FINISHED && commit_pending_) |
| + if (!HasPendingTasks() && !IsStatePastRunning() && commit_pending_) |
| Commit(); |
| } |
| +void IndexedDBTransaction::BlobWriteComplete(bool success) |
| +{ |
| + IDB_TRACE("IndexedDBTransaction::BlobWriteComplete"); |
| + if (state_ == FINISHED) // aborted |
| + return; |
| + DCHECK_EQ(state_, COMMITTING); |
| + if (success) |
| + CommitPhaseTwo(WAS_USED, DO_COMMIT); |
| + else |
| + Abort(IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionDataError, |
| + "Failed to write blobs")); |
| +} |
| + |
| void IndexedDBTransaction::CloseOpenCursors() { |
| for (std::set<IndexedDBCursor*>::iterator i = open_cursors_.begin(); |
| i != open_cursors_.end(); |
| @@ -299,4 +307,49 @@ void IndexedDBTransaction::CloseOpenCursors() { |
| open_cursors_.clear(); |
| } |
| +void IndexedDBTransaction::CommitPhaseTwo(TransactionUseState use_state, |
| + ShouldSkipCommit skip_commit) { |
| + DCHECK_EQ(state_, COMMITTING); |
| + state_ = FINISHED; |
| + |
| + // The last reference to this object may be released while performing the |
| + // commit steps below. We therefore take a self reference to keep ourselves |
| + // alive while executing this method. |
| + scoped_refptr<IndexedDBTransaction> protect(this); |
| + |
| + // TODO(jsbell): Run abort tasks if commit fails? http://crbug.com/241843 |
| + abort_task_stack_.clear(); |
| + |
| + state_ = FINISHED; |
| + |
| + bool committed = (use_state == WAS_NOT_USED) || |
|
jsbell
2013/09/13 00:12:21
Seems like use_state and skip_commit effectively h
ericu
2013/11/20 23:05:39
No--we skip_commit if we've failed or aborted, but
|
| + ((skip_commit == DO_COMMIT) && transaction_.CommitPhaseTwo()); |
| + |
| + // Backing store resources (held via cursors) must be released |
| + // before script callbacks are fired, as the script callbacks may |
| + // release references and allow the backing store itself to be |
| + // released, and order is critical. |
| + CloseOpenCursors(); |
| + transaction_.Reset(); |
| + |
| + // Transactions must also be marked as completed before the |
| + // front-end is notified, as the transaction completion unblocks |
| + // operations like closing connections. |
| + database_->transaction_coordinator().DidFinishTransaction(this); |
| + database_->TransactionFinished(this); |
| + |
| + if (committed) { |
| + callbacks_->OnComplete(id_); |
| + database_->TransactionFinishedAndCompleteFired(this); |
| + } else { |
| + callbacks_->OnAbort( |
| + id_, |
| + IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError, |
| + "Internal error committing transaction.")); |
| + database_->TransactionFinishedAndAbortFired(this); |
| + } |
| + |
| + database_ = NULL; |
| +} |
| + |
| } // namespace content |