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 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
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 used_(false), | 60 used_(false), |
61 state_(CREATED), | 61 state_(CREATED), |
62 commit_pending_(false), | 62 commit_pending_(false), |
| 63 blob_write_success_(false), |
63 callbacks_(callbacks), | 64 callbacks_(callbacks), |
64 database_(database), | 65 database_(database), |
65 transaction_(database->backing_store()), | 66 transaction_(database->backing_store()), |
66 backing_store_transaction_begun_(false), | 67 backing_store_transaction_begun_(false), |
67 should_process_queue_(false), | 68 should_process_queue_(false), |
68 pending_preemptive_events_(0) { | 69 pending_preemptive_events_(0) { |
69 database_->transaction_coordinator().DidCreateTransaction(this); | 70 database_->transaction_coordinator().DidCreateTransaction(this); |
70 | 71 |
71 diagnostics_.tasks_scheduled = 0; | 72 diagnostics_.tasks_scheduled = 0; |
72 diagnostics_.tasks_completed = 0; | 73 diagnostics_.tasks_completed = 0; |
(...skipping 15 matching lines...) Expand all Loading... |
88 | 89 |
89 used_ = true; | 90 used_ = true; |
90 task_queue_.push(task); | 91 task_queue_.push(task); |
91 ++diagnostics_.tasks_scheduled; | 92 ++diagnostics_.tasks_scheduled; |
92 abort_task_stack_.push(abort_task); | 93 abort_task_stack_.push(abort_task); |
93 RunTasksIfStarted(); | 94 RunTasksIfStarted(); |
94 } | 95 } |
95 | 96 |
96 void IndexedDBTransaction::ScheduleTask(IndexedDBDatabase::TaskType type, | 97 void IndexedDBTransaction::ScheduleTask(IndexedDBDatabase::TaskType type, |
97 Operation task) { | 98 Operation task) { |
98 if (state_ == FINISHED) | 99 if (IsStatePastRunning()) |
99 return; | 100 return; |
100 | 101 |
101 used_ = true; | 102 used_ = true; |
102 if (type == IndexedDBDatabase::NORMAL_TASK) { | 103 if (type == IndexedDBDatabase::NORMAL_TASK) { |
103 task_queue_.push(task); | 104 task_queue_.push(task); |
104 ++diagnostics_.tasks_scheduled; | 105 ++diagnostics_.tasks_scheduled; |
105 } else { | 106 } else { |
106 preemptive_task_queue_.push(task); | 107 preemptive_task_queue_.push(task); |
107 } | 108 } |
108 RunTasksIfStarted(); | 109 RunTasksIfStarted(); |
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
199 state_ = STARTED; | 200 state_ = STARTED; |
200 database_->TransactionStarted(this); | 201 database_->TransactionStarted(this); |
201 diagnostics_.start_time = base::Time::Now(); | 202 diagnostics_.start_time = base::Time::Now(); |
202 | 203 |
203 if (!used_) | 204 if (!used_) |
204 return; | 205 return; |
205 | 206 |
206 RunTasksIfStarted(); | 207 RunTasksIfStarted(); |
207 } | 208 } |
208 | 209 |
| 210 class BlobWriteCallbackImpl : public IndexedDBBackingStore::BlobWriteCallback { |
| 211 public: |
| 212 BlobWriteCallbackImpl(scoped_refptr<IndexedDBTransaction> transaction) |
| 213 : transaction_(transaction) { |
| 214 } |
| 215 virtual ~BlobWriteCallbackImpl() { |
| 216 } |
| 217 virtual void didSucceed() { |
| 218 transaction_->BlobWriteComplete(true); |
| 219 } |
| 220 virtual void didFail() { |
| 221 transaction_->BlobWriteComplete(false); |
| 222 } |
| 223 private: |
| 224 scoped_refptr<IndexedDBTransaction> transaction_; |
| 225 }; |
| 226 |
| 227 void IndexedDBTransaction::BlobWriteComplete(bool success) |
| 228 { |
| 229 IDB_TRACE("IndexedDBTransaction::BlobWriteComplete"); |
| 230 if (state_ == FINISHED) // aborted |
| 231 return; |
| 232 DCHECK_EQ(state_, COMMITTING); |
| 233 if (success) |
| 234 CommitPhaseTwo(); |
| 235 else |
| 236 Abort(IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionDataError, |
| 237 "Failed to write blobs.")); |
| 238 } |
| 239 |
209 void IndexedDBTransaction::Commit() { | 240 void IndexedDBTransaction::Commit() { |
210 IDB_TRACE("IndexedDBTransaction::Commit"); | 241 IDB_TRACE("IndexedDBTransaction::Commit"); |
211 | 242 |
212 // In multiprocess ports, front-end may have requested a commit but | 243 // In multiprocess ports, front-end may have requested a commit but |
213 // an abort has already been initiated asynchronously by the | 244 // an abort has already been initiated asynchronously by the |
214 // back-end. | 245 // back-end. |
215 if (state_ == FINISHED) | 246 if (IsStatePastRunning()) |
216 return; | 247 return; |
217 | 248 |
218 DCHECK(!used_ || state_ == STARTED); | 249 DCHECK(!used_ || state_ == STARTED); |
219 commit_pending_ = true; | 250 commit_pending_ = true; |
220 | 251 |
221 // Front-end has requested a commit, but there may be tasks like | 252 // Front-end has requested a commit, but there may be tasks like |
222 // create_index which are considered synchronous by the front-end | 253 // create_index which are considered synchronous by the front-end |
223 // but are processed asynchronously. | 254 // but are processed asynchronously. |
224 if (HasPendingTasks()) | 255 if (HasPendingTasks()) |
225 return; | 256 return; |
226 | 257 |
| 258 state_ = COMMITTING; |
| 259 |
| 260 if (!used_) |
| 261 CommitPhaseTwo(); |
| 262 else { |
| 263 scoped_refptr<IndexedDBBackingStore::BlobWriteCallback> callback( |
| 264 new BlobWriteCallbackImpl(this)); |
| 265 // CommitPhaseOne will call the callback synchronously if there are no blobs |
| 266 // to write. |
| 267 if (!transaction_.CommitPhaseOne(callback)) |
| 268 Abort(IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionDataError, |
| 269 "Error processing blob journal.")); |
| 270 } |
| 271 } |
| 272 |
| 273 void IndexedDBTransaction::CommitPhaseTwo() { |
| 274 DCHECK_EQ(state_, COMMITTING); |
| 275 state_ = FINISHED; |
| 276 |
227 // The last reference to this object may be released while performing the | 277 // The last reference to this object may be released while performing the |
228 // commit steps below. We therefore take a self reference to keep ourselves | 278 // commit steps below. We therefore take a self reference to keep ourselves |
229 // alive while executing this method. | 279 // alive while executing this method. |
230 scoped_refptr<IndexedDBTransaction> protect(this); | 280 scoped_refptr<IndexedDBTransaction> protect(this); |
231 | 281 |
232 // TODO(jsbell): Run abort tasks if commit fails? http://crbug.com/241843 | 282 // TODO(jsbell): Run abort tasks if commit fails? http://crbug.com/241843 |
233 abort_task_stack_.clear(); | 283 abort_task_stack_.clear(); |
234 | 284 |
235 state_ = FINISHED; | 285 state_ = FINISHED; |
236 | 286 |
237 bool committed = !used_ || transaction_.Commit(); | 287 bool committed = !used_ || transaction_.CommitPhaseTwo(); |
238 | 288 |
239 // Backing store resources (held via cursors) must be released | 289 // Backing store resources (held via cursors) must be released |
240 // before script callbacks are fired, as the script callbacks may | 290 // before script callbacks are fired, as the script callbacks may |
241 // release references and allow the backing store itself to be | 291 // release references and allow the backing store itself to be |
242 // released, and order is critical. | 292 // released, and order is critical. |
243 CloseOpenCursors(); | 293 CloseOpenCursors(); |
244 transaction_.Reset(); | 294 transaction_.Reset(); |
245 | 295 |
246 // Transactions must also be marked as completed before the | 296 // Transactions must also be marked as completed before the |
247 // front-end is notified, as the transaction completion unblocks | 297 // front-end is notified, as the transaction completion unblocks |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
279 backing_store_transaction_begun_ = true; | 329 backing_store_transaction_begun_ = true; |
280 } | 330 } |
281 | 331 |
282 // The last reference to this object may be released while performing the | 332 // The last reference to this object may be released while performing the |
283 // tasks. Take take a self reference to keep this object alive so that | 333 // tasks. Take take a self reference to keep this object alive so that |
284 // the loop termination conditions can be checked. | 334 // the loop termination conditions can be checked. |
285 scoped_refptr<IndexedDBTransaction> protect(this); | 335 scoped_refptr<IndexedDBTransaction> protect(this); |
286 | 336 |
287 TaskQueue* task_queue = | 337 TaskQueue* task_queue = |
288 pending_preemptive_events_ ? &preemptive_task_queue_ : &task_queue_; | 338 pending_preemptive_events_ ? &preemptive_task_queue_ : &task_queue_; |
289 while (!task_queue->empty() && state_ != FINISHED) { | 339 while (!task_queue->empty() && !IsStatePastRunning()) { |
290 DCHECK_EQ(STARTED, state_); | 340 DCHECK_EQ(state_, STARTED); |
291 Operation task(task_queue->pop()); | 341 Operation task(task_queue->pop()); |
292 task.Run(this); | 342 task.Run(this); |
293 if (!pending_preemptive_events_) { | 343 if (!pending_preemptive_events_) { |
294 DCHECK(diagnostics_.tasks_completed < diagnostics_.tasks_scheduled); | 344 DCHECK(diagnostics_.tasks_completed < diagnostics_.tasks_scheduled); |
295 ++diagnostics_.tasks_completed; | 345 ++diagnostics_.tasks_completed; |
296 } | 346 } |
297 | 347 |
298 // Event itself may change which queue should be processed next. | 348 // Event itself may change which queue should be processed next. |
299 task_queue = | 349 task_queue = |
300 pending_preemptive_events_ ? &preemptive_task_queue_ : &task_queue_; | 350 pending_preemptive_events_ ? &preemptive_task_queue_ : &task_queue_; |
301 } | 351 } |
302 | 352 |
303 // If there are no pending tasks, we haven't already committed/aborted, | 353 // If there are no pending tasks, we haven't already committed/aborted, |
304 // and the front-end requested a commit, it is now safe to do so. | 354 // and the front-end requested a commit, it is now safe to do so. |
305 if (!HasPendingTasks() && state_ != FINISHED && commit_pending_) | 355 if (!HasPendingTasks() && !IsStatePastRunning() && commit_pending_) |
306 Commit(); | 356 Commit(); |
307 } | 357 } |
308 | 358 |
309 void IndexedDBTransaction::CloseOpenCursors() { | 359 void IndexedDBTransaction::CloseOpenCursors() { |
310 for (std::set<IndexedDBCursor*>::iterator i = open_cursors_.begin(); | 360 for (std::set<IndexedDBCursor*>::iterator i = open_cursors_.begin(); |
311 i != open_cursors_.end(); | 361 i != open_cursors_.end(); |
312 ++i) | 362 ++i) |
313 (*i)->Close(); | 363 (*i)->Close(); |
314 open_cursors_.clear(); | 364 open_cursors_.clear(); |
315 } | 365 } |
316 | 366 |
317 } // namespace content | 367 } // namespace content |
OLD | NEW |