Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/browser/indexed_db/indexed_db_transaction.h" | 5 #include "content/browser/indexed_db/indexed_db_transaction.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/logging.h" | 8 #include "base/logging.h" |
| 9 #include "base/message_loop/message_loop.h" | 9 #include "base/message_loop/message_loop.h" |
| 10 #include "base/strings/utf_string_conversions.h" | 10 #include "base/strings/utf_string_conversions.h" |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 52 int64 id, | 52 int64 id, |
| 53 scoped_refptr<IndexedDBDatabaseCallbacks> callbacks, | 53 scoped_refptr<IndexedDBDatabaseCallbacks> callbacks, |
| 54 const std::set<int64>& object_store_ids, | 54 const std::set<int64>& object_store_ids, |
| 55 indexed_db::TransactionMode mode, | 55 indexed_db::TransactionMode mode, |
| 56 IndexedDBDatabase* database) | 56 IndexedDBDatabase* database) |
| 57 : id_(id), | 57 : id_(id), |
| 58 object_store_ids_(object_store_ids), | 58 object_store_ids_(object_store_ids), |
| 59 mode_(mode), | 59 mode_(mode), |
| 60 state_(UNUSED), | 60 state_(UNUSED), |
| 61 commit_pending_(false), | 61 commit_pending_(false), |
| 62 blob_write_success_(false), | |
| 62 callbacks_(callbacks), | 63 callbacks_(callbacks), |
| 63 database_(database), | 64 database_(database), |
| 64 transaction_(database->BackingStore().get()), | 65 transaction_(database->BackingStore().get()), |
| 65 should_process_queue_(false), | 66 should_process_queue_(false), |
| 66 pending_preemptive_events_(0) { | 67 pending_preemptive_events_(0) { |
| 68 fprintf(stderr, "ERICU: IndexedDBTransaction::IndexedDBTransaction(%p).\n", | |
| 69 this); | |
| 67 database_->transaction_coordinator().DidCreateTransaction(this); | 70 database_->transaction_coordinator().DidCreateTransaction(this); |
| 68 } | 71 } |
| 69 | 72 |
| 70 IndexedDBTransaction::~IndexedDBTransaction() { | 73 IndexedDBTransaction::~IndexedDBTransaction() { |
| 71 // It shouldn't be possible for this object to get deleted until it's either | 74 // It shouldn't be possible for this object to get deleted until it's either |
| 72 // complete or aborted. | 75 // complete or aborted. |
| 76 fprintf(stderr, "ERICU: IndexedDBTransaction::~IndexedDBTransaction.\n"); | |
| 73 DCHECK_EQ(state_, FINISHED); | 77 DCHECK_EQ(state_, FINISHED); |
| 74 DCHECK(preemptive_task_queue_.empty()); | 78 DCHECK(preemptive_task_queue_.empty()); |
| 75 DCHECK(task_queue_.empty()); | 79 DCHECK(task_queue_.empty()); |
| 76 DCHECK(abort_task_stack_.empty()); | 80 DCHECK(abort_task_stack_.empty()); |
| 77 } | 81 } |
| 78 | 82 |
| 79 void IndexedDBTransaction::ScheduleTask(Operation task, Operation abort_task) { | 83 void IndexedDBTransaction::ScheduleTask(Operation task, Operation abort_task) { |
| 80 if (state_ == FINISHED) | 84 if (state_ == FINISHED) |
| 81 return; | 85 return; |
| 82 task_queue_.push(task); | 86 task_queue_.push(task); |
| 83 abort_task_stack_.push(abort_task); | 87 abort_task_stack_.push(abort_task); |
| 84 EnsureTasksRunning(); | 88 EnsureTasksRunning(); |
| 85 } | 89 } |
| 86 | 90 |
| 87 void IndexedDBTransaction::ScheduleTask(IndexedDBDatabase::TaskType type, | 91 void IndexedDBTransaction::ScheduleTask(IndexedDBDatabase::TaskType type, |
| 88 Operation task) { | 92 Operation task) { |
| 89 if (state_ == FINISHED) | 93 if (IsStatePastRunning()) |
| 90 return; | 94 return; |
| 91 | 95 |
| 92 if (type == IndexedDBDatabase::NORMAL_TASK) | 96 if (type == IndexedDBDatabase::NORMAL_TASK) |
| 93 task_queue_.push(task); | 97 task_queue_.push(task); |
| 94 else | 98 else |
| 95 preemptive_task_queue_.push(task); | 99 preemptive_task_queue_.push(task); |
| 96 EnsureTasksRunning(); | 100 EnsureTasksRunning(); |
| 97 } | 101 } |
| 98 | 102 |
| 99 void IndexedDBTransaction::EnsureTasksRunning() { | 103 void IndexedDBTransaction::EnsureTasksRunning() { |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 188 } | 192 } |
| 189 | 193 |
| 190 void IndexedDBTransaction::Start() { | 194 void IndexedDBTransaction::Start() { |
| 191 DCHECK_EQ(state_, UNUSED); | 195 DCHECK_EQ(state_, UNUSED); |
| 192 | 196 |
| 193 state_ = START_PENDING; | 197 state_ = START_PENDING; |
| 194 database_->transaction_coordinator().DidStartTransaction(this); | 198 database_->transaction_coordinator().DidStartTransaction(this); |
| 195 database_->TransactionStarted(this); | 199 database_->TransactionStarted(this); |
| 196 } | 200 } |
| 197 | 201 |
| 202 class BlobWriteCallbackImpl : public IndexedDBBackingStore::BlobWriteCallback { | |
| 203 public: | |
| 204 BlobWriteCallbackImpl(scoped_refptr<IndexedDBTransaction> transaction) | |
| 205 : transaction_(transaction) { } | |
| 206 virtual ~BlobWriteCallbackImpl() { } | |
| 207 virtual void didSucceed() { | |
| 208 transaction_->BlobWriteComplete(true); | |
| 209 } | |
| 210 virtual void didFail() { | |
| 211 transaction_->BlobWriteComplete(false); | |
| 212 } | |
| 213 private: | |
| 214 scoped_refptr<IndexedDBTransaction> transaction_; | |
| 215 }; | |
| 216 | |
| 198 void IndexedDBTransaction::Commit() { | 217 void IndexedDBTransaction::Commit() { |
| 199 IDB_TRACE("IndexedDBTransaction::Commit"); | 218 IDB_TRACE("IndexedDBTransaction::Commit"); |
| 219 fprintf(stderr, "ERICU: IndexedDBTransaction::Commit.\n"); | |
| 200 | 220 |
| 201 // In multiprocess ports, front-end may have requested a commit but | 221 // In multiprocess ports, front-end may have requested a commit but |
| 202 // an abort has already been initiated asynchronously by the | 222 // an abort has already been initiated asynchronously by the |
| 203 // back-end. | 223 // back-end. |
| 204 if (state_ == FINISHED) | 224 if (IsStatePastRunning()) |
| 205 return; | 225 return; |
| 206 | 226 |
| 207 DCHECK(state_ == UNUSED || state_ == RUNNING); | 227 DCHECK(state_ == UNUSED || state_ == RUNNING); |
| 208 commit_pending_ = true; | 228 commit_pending_ = true; |
| 209 | 229 |
| 210 // Front-end has requested a commit, but there may be tasks like | 230 // Front-end has requested a commit, but there may be tasks like |
| 211 // create_index which are considered synchronous by the front-end | 231 // create_index which are considered synchronous by the front-end |
| 212 // but are processed asynchronously. | 232 // but are processed asynchronously. |
| 213 if (HasPendingTasks()) | 233 if (HasPendingTasks()) |
| 214 return; | 234 return; |
| 215 | 235 |
| 216 // The last reference to this object may be released while performing the | 236 bool unused = state_ == UNUSED; |
| 217 // commit steps below. We therefore take a self reference to keep ourselves | 237 state_ = COMMITTING; |
| 218 // alive while executing this method. | |
| 219 scoped_refptr<IndexedDBTransaction> protect(this); | |
| 220 | 238 |
| 221 // TODO(jsbell): Run abort tasks if commit fails? http://crbug.com/241843 | 239 if (unused) |
| 222 abort_task_stack_.clear(); | 240 CommitPhaseTwo(WAS_NOT_USED, DO_COMMIT); |
| 223 | 241 else { |
| 224 bool unused = state_ == UNUSED; | 242 scoped_refptr<IndexedDBBackingStore::BlobWriteCallback> callback( |
| 225 state_ = FINISHED; | 243 new BlobWriteCallbackImpl(this)); |
| 226 | 244 // CommitPhaseOne will call the callback synchronously if there are no blobs |
| 227 bool committed = unused || transaction_.Commit(); | 245 // to write. |
| 228 | 246 if (!transaction_.CommitPhaseOne(callback)) |
| 229 // Backing store resources (held via cursors) must be released | 247 CommitPhaseTwo(WAS_USED, SKIP_COMMIT); |
| 230 // before script callbacks are fired, as the script callbacks may | |
| 231 // release references and allow the backing store itself to be | |
| 232 // released, and order is critical. | |
| 233 CloseOpenCursors(); | |
| 234 transaction_.Reset(); | |
| 235 | |
| 236 // Transactions must also be marked as completed before the | |
| 237 // front-end is notified, as the transaction completion unblocks | |
| 238 // operations like closing connections. | |
| 239 database_->transaction_coordinator().DidFinishTransaction(this); | |
| 240 database_->TransactionFinished(this); | |
| 241 | |
| 242 if (committed) { | |
| 243 callbacks_->OnComplete(id_); | |
| 244 database_->TransactionFinishedAndCompleteFired(this); | |
| 245 } else { | |
| 246 callbacks_->OnAbort( | |
| 247 id_, | |
| 248 IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError, | |
| 249 "Internal error committing transaction.")); | |
| 250 database_->TransactionFinishedAndAbortFired(this); | |
| 251 } | 248 } |
| 252 | |
| 253 database_ = NULL; | |
| 254 } | 249 } |
| 255 | 250 |
| 256 void IndexedDBTransaction::ProcessTaskQueue() { | 251 void IndexedDBTransaction::ProcessTaskQueue() { |
| 257 IDB_TRACE("IndexedDBTransaction::ProcessTaskQueue"); | 252 IDB_TRACE("IndexedDBTransaction::ProcessTaskQueue"); |
| 258 | 253 |
| 259 // May have been aborted. | 254 // May have been aborted. |
| 260 if (!should_process_queue_) | 255 if (!should_process_queue_) |
| 261 return; | 256 return; |
| 262 | 257 |
| 263 DCHECK(!IsTaskQueueEmpty()); | 258 DCHECK(!IsTaskQueueEmpty()); |
| 264 should_process_queue_ = false; | 259 should_process_queue_ = false; |
| 265 | 260 |
| 266 if (state_ == START_PENDING) { | 261 if (state_ == START_PENDING) { |
| 267 transaction_.Begin(); | 262 transaction_.Begin(); |
| 268 state_ = RUNNING; | 263 state_ = RUNNING; |
| 269 } | 264 } |
| 270 | 265 |
| 271 // The last reference to this object may be released while performing the | 266 // The last reference to this object may be released while performing the |
| 272 // tasks. Take take a self reference to keep this object alive so that | 267 // tasks. Take take a self reference to keep this object alive so that |
| 273 // the loop termination conditions can be checked. | 268 // the loop termination conditions can be checked. |
| 274 scoped_refptr<IndexedDBTransaction> protect(this); | 269 scoped_refptr<IndexedDBTransaction> protect(this); |
| 275 | 270 |
| 276 TaskQueue* task_queue = | 271 TaskQueue* task_queue = |
| 277 pending_preemptive_events_ ? &preemptive_task_queue_ : &task_queue_; | 272 pending_preemptive_events_ ? &preemptive_task_queue_ : &task_queue_; |
| 278 while (!task_queue->empty() && state_ != FINISHED) { | 273 while (!task_queue->empty() && !IsStatePastRunning()) { |
| 279 DCHECK_EQ(state_, RUNNING); | 274 DCHECK_EQ(state_, RUNNING); |
| 280 Operation task(task_queue->pop()); | 275 Operation task(task_queue->pop()); |
| 281 task.Run(this); | 276 task.Run(this); |
| 282 | 277 |
| 283 // Event itself may change which queue should be processed next. | 278 // Event itself may change which queue should be processed next. |
| 284 task_queue = | 279 task_queue = |
| 285 pending_preemptive_events_ ? &preemptive_task_queue_ : &task_queue_; | 280 pending_preemptive_events_ ? &preemptive_task_queue_ : &task_queue_; |
| 286 } | 281 } |
| 287 | 282 |
| 288 // If there are no pending tasks, we haven't already committed/aborted, | 283 // If there are no pending tasks, we haven't already committed/aborted, |
| 289 // and the front-end requested a commit, it is now safe to do so. | 284 // and the front-end requested a commit, it is now safe to do so. |
| 290 if (!HasPendingTasks() && state_ != FINISHED && commit_pending_) | 285 if (!HasPendingTasks() && !IsStatePastRunning() && commit_pending_) |
| 291 Commit(); | 286 Commit(); |
| 292 } | 287 } |
| 293 | 288 |
| 289 void IndexedDBTransaction::BlobWriteComplete(bool success) | |
| 290 { | |
| 291 IDB_TRACE("IndexedDBTransaction::BlobWriteComplete"); | |
| 292 if (state_ == FINISHED) // aborted | |
| 293 return; | |
| 294 DCHECK_EQ(state_, COMMITTING); | |
| 295 if (success) | |
| 296 CommitPhaseTwo(WAS_USED, DO_COMMIT); | |
| 297 else | |
| 298 Abort(IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionDataError, | |
| 299 "Failed to write blobs")); | |
| 300 } | |
| 301 | |
| 294 void IndexedDBTransaction::CloseOpenCursors() { | 302 void IndexedDBTransaction::CloseOpenCursors() { |
| 295 for (std::set<IndexedDBCursor*>::iterator i = open_cursors_.begin(); | 303 for (std::set<IndexedDBCursor*>::iterator i = open_cursors_.begin(); |
| 296 i != open_cursors_.end(); | 304 i != open_cursors_.end(); |
| 297 ++i) | 305 ++i) |
| 298 (*i)->Close(); | 306 (*i)->Close(); |
| 299 open_cursors_.clear(); | 307 open_cursors_.clear(); |
| 300 } | 308 } |
| 301 | 309 |
| 310 void IndexedDBTransaction::CommitPhaseTwo(TransactionUseState use_state, | |
| 311 ShouldSkipCommit skip_commit) { | |
| 312 DCHECK_EQ(state_, COMMITTING); | |
| 313 state_ = FINISHED; | |
| 314 | |
| 315 // The last reference to this object may be released while performing the | |
| 316 // commit steps below. We therefore take a self reference to keep ourselves | |
| 317 // alive while executing this method. | |
| 318 scoped_refptr<IndexedDBTransaction> protect(this); | |
| 319 | |
| 320 // TODO(jsbell): Run abort tasks if commit fails? http://crbug.com/241843 | |
| 321 abort_task_stack_.clear(); | |
| 322 | |
| 323 state_ = FINISHED; | |
| 324 | |
| 325 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
| |
| 326 ((skip_commit == DO_COMMIT) && transaction_.CommitPhaseTwo()); | |
| 327 | |
| 328 // Backing store resources (held via cursors) must be released | |
| 329 // before script callbacks are fired, as the script callbacks may | |
| 330 // release references and allow the backing store itself to be | |
| 331 // released, and order is critical. | |
| 332 CloseOpenCursors(); | |
| 333 transaction_.Reset(); | |
| 334 | |
| 335 // Transactions must also be marked as completed before the | |
| 336 // front-end is notified, as the transaction completion unblocks | |
| 337 // operations like closing connections. | |
| 338 database_->transaction_coordinator().DidFinishTransaction(this); | |
| 339 database_->TransactionFinished(this); | |
| 340 | |
| 341 if (committed) { | |
| 342 callbacks_->OnComplete(id_); | |
| 343 database_->TransactionFinishedAndCompleteFired(this); | |
| 344 } else { | |
| 345 callbacks_->OnAbort( | |
| 346 id_, | |
| 347 IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError, | |
| 348 "Internal error committing transaction.")); | |
| 349 database_->TransactionFinishedAndAbortFired(this); | |
| 350 } | |
| 351 | |
| 352 database_ = NULL; | |
| 353 } | |
| 354 | |
| 302 } // namespace content | 355 } // namespace content |
| OLD | NEW |