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 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
81 // complete or aborted. | 81 // complete or aborted. |
82 DCHECK_EQ(state_, FINISHED); | 82 DCHECK_EQ(state_, FINISHED); |
83 DCHECK(preemptive_task_queue_.empty()); | 83 DCHECK(preemptive_task_queue_.empty()); |
84 DCHECK_EQ(pending_preemptive_events_, 0); | 84 DCHECK_EQ(pending_preemptive_events_, 0); |
85 DCHECK(task_queue_.empty()); | 85 DCHECK(task_queue_.empty()); |
86 DCHECK(abort_task_stack_.empty()); | 86 DCHECK(abort_task_stack_.empty()); |
87 } | 87 } |
88 | 88 |
89 void IndexedDBTransaction::ScheduleTask(IndexedDBDatabase::TaskType type, | 89 void IndexedDBTransaction::ScheduleTask(IndexedDBDatabase::TaskType type, |
90 Operation task) { | 90 Operation task) { |
| 91 DCHECK_NE(state_, COMMITTING); |
91 if (state_ == FINISHED) | 92 if (state_ == FINISHED) |
92 return; | 93 return; |
93 | 94 |
94 timeout_timer_.Stop(); | 95 timeout_timer_.Stop(); |
95 used_ = true; | 96 used_ = true; |
96 if (type == IndexedDBDatabase::NORMAL_TASK) { | 97 if (type == IndexedDBDatabase::NORMAL_TASK) { |
97 task_queue_.push(task); | 98 task_queue_.push(task); |
98 ++diagnostics_.tasks_scheduled; | 99 ++diagnostics_.tasks_scheduled; |
99 } else { | 100 } else { |
100 preemptive_task_queue_.push(task); | 101 preemptive_task_queue_.push(task); |
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
199 DCHECK_EQ(CREATED, state_); | 200 DCHECK_EQ(CREATED, state_); |
200 state_ = STARTED; | 201 state_ = STARTED; |
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 virtual void Run(bool succeeded) OVERRIDE { |
| 215 transaction_->BlobWriteComplete(succeeded); |
| 216 } |
| 217 |
| 218 protected: |
| 219 virtual ~BlobWriteCallbackImpl() {} |
| 220 |
| 221 private: |
| 222 scoped_refptr<IndexedDBTransaction> transaction_; |
| 223 }; |
| 224 |
| 225 void IndexedDBTransaction::BlobWriteComplete(bool success) { |
| 226 IDB_TRACE("IndexedDBTransaction::BlobWriteComplete"); |
| 227 if (state_ == FINISHED) // aborted |
| 228 return; |
| 229 DCHECK_EQ(state_, COMMITTING); |
| 230 if (success) |
| 231 CommitPhaseTwo(); |
| 232 else |
| 233 Abort(IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionDataError, |
| 234 "Failed to write blobs.")); |
| 235 } |
| 236 |
209 void IndexedDBTransaction::Commit() { | 237 void IndexedDBTransaction::Commit() { |
210 IDB_TRACE("IndexedDBTransaction::Commit"); | 238 IDB_TRACE("IndexedDBTransaction::Commit"); |
211 | 239 |
212 // In multiprocess ports, front-end may have requested a commit but | 240 // In multiprocess ports, front-end may have requested a commit but |
213 // an abort has already been initiated asynchronously by the | 241 // an abort has already been initiated asynchronously by the |
214 // back-end. | 242 // back-end. |
215 if (state_ == FINISHED) | 243 if (state_ == FINISHED) |
216 return; | 244 return; |
| 245 DCHECK_NE(state_, COMMITTING); |
217 | 246 |
218 DCHECK(!used_ || state_ == STARTED); | 247 DCHECK(!used_ || state_ == STARTED); |
219 commit_pending_ = true; | 248 commit_pending_ = true; |
220 | 249 |
221 // Front-end has requested a commit, but there may be tasks like | 250 // Front-end has requested a commit, but there may be tasks like |
222 // create_index which are considered synchronous by the front-end | 251 // create_index which are considered synchronous by the front-end |
223 // but are processed asynchronously. | 252 // but are processed asynchronously. |
224 if (HasPendingTasks()) | 253 if (HasPendingTasks()) |
225 return; | 254 return; |
226 | 255 |
| 256 state_ = COMMITTING; |
| 257 |
| 258 if (!used_) |
| 259 CommitPhaseTwo(); |
| 260 else { |
| 261 scoped_refptr<IndexedDBBackingStore::BlobWriteCallback> callback( |
| 262 new BlobWriteCallbackImpl(this)); |
| 263 // CommitPhaseOne will call the callback synchronously if there are no blobs |
| 264 // to write. |
| 265 if (!transaction_->CommitPhaseOne(callback).ok()) |
| 266 Abort(IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionDataError, |
| 267 "Error processing blob journal.")); |
| 268 } |
| 269 } |
| 270 |
| 271 void IndexedDBTransaction::CommitPhaseTwo() { |
| 272 // Abort may have been called just as the blob write completed. |
| 273 if (state_ == FINISHED) |
| 274 return; |
| 275 |
| 276 DCHECK_EQ(state_, COMMITTING); |
| 277 |
227 // The last reference to this object may be released while performing the | 278 // 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 | 279 // commit steps below. We therefore take a self reference to keep ourselves |
229 // alive while executing this method. | 280 // alive while executing this method. |
230 scoped_refptr<IndexedDBTransaction> protect(this); | 281 scoped_refptr<IndexedDBTransaction> protect(this); |
231 | 282 |
232 timeout_timer_.Stop(); | 283 timeout_timer_.Stop(); |
233 | 284 |
234 state_ = FINISHED; | 285 state_ = FINISHED; |
235 | 286 |
236 bool committed = !used_ || transaction_->Commit().ok(); | 287 bool committed = !used_ || transaction_->CommitPhaseTwo().ok(); |
237 | 288 |
238 // Backing store resources (held via cursors) must be released | 289 // Backing store resources (held via cursors) must be released |
239 // before script callbacks are fired, as the script callbacks may | 290 // before script callbacks are fired, as the script callbacks may |
240 // release references and allow the backing store itself to be | 291 // release references and allow the backing store itself to be |
241 // released, and order is critical. | 292 // released, and order is critical. |
242 CloseOpenCursors(); | 293 CloseOpenCursors(); |
243 transaction_->Reset(); | 294 transaction_->Reset(); |
244 | 295 |
245 // Transactions must also be marked as completed before the | 296 // Transactions must also be marked as completed before the |
246 // front-end is notified, as the transaction completion unblocks | 297 // front-end is notified, as the transaction completion unblocks |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
282 } | 333 } |
283 | 334 |
284 // The last reference to this object may be released while performing the | 335 // The last reference to this object may be released while performing the |
285 // tasks. Take take a self reference to keep this object alive so that | 336 // tasks. Take take a self reference to keep this object alive so that |
286 // the loop termination conditions can be checked. | 337 // the loop termination conditions can be checked. |
287 scoped_refptr<IndexedDBTransaction> protect(this); | 338 scoped_refptr<IndexedDBTransaction> protect(this); |
288 | 339 |
289 TaskQueue* task_queue = | 340 TaskQueue* task_queue = |
290 pending_preemptive_events_ ? &preemptive_task_queue_ : &task_queue_; | 341 pending_preemptive_events_ ? &preemptive_task_queue_ : &task_queue_; |
291 while (!task_queue->empty() && state_ != FINISHED) { | 342 while (!task_queue->empty() && state_ != FINISHED) { |
292 DCHECK_EQ(STARTED, state_); | 343 DCHECK_EQ(state_, STARTED); |
293 Operation task(task_queue->pop()); | 344 Operation task(task_queue->pop()); |
294 task.Run(this); | 345 task.Run(this); |
295 if (!pending_preemptive_events_) { | 346 if (!pending_preemptive_events_) { |
296 DCHECK(diagnostics_.tasks_completed < diagnostics_.tasks_scheduled); | 347 DCHECK(diagnostics_.tasks_completed < diagnostics_.tasks_scheduled); |
297 ++diagnostics_.tasks_completed; | 348 ++diagnostics_.tasks_completed; |
298 } | 349 } |
299 | 350 |
300 // Event itself may change which queue should be processed next. | 351 // Event itself may change which queue should be processed next. |
301 task_queue = | 352 task_queue = |
302 pending_preemptive_events_ ? &preemptive_task_queue_ : &task_queue_; | 353 pending_preemptive_events_ ? &preemptive_task_queue_ : &task_queue_; |
303 } | 354 } |
304 | 355 |
305 // If there are no pending tasks, we haven't already committed/aborted, | 356 // If there are no pending tasks, we haven't already committed/aborted, |
306 // and the front-end requested a commit, it is now safe to do so. | 357 // and the front-end requested a commit, it is now safe to do so. |
307 if (!HasPendingTasks() && state_ != FINISHED && commit_pending_) { | 358 if (!HasPendingTasks() && state_ != FINISHED && commit_pending_) { |
308 Commit(); | 359 Commit(); |
309 return; | 360 return; |
310 } | 361 } |
311 | 362 |
312 // The transaction may have been aborted while processing tasks. | 363 // The transaction may have been aborted while processing tasks. |
313 if (state_ == FINISHED) | 364 if (state_ == FINISHED) |
314 return; | 365 return; |
315 | 366 |
| 367 DCHECK(state_ == STARTED); |
| 368 |
316 // Otherwise, start a timer in case the front-end gets wedged and | 369 // Otherwise, start a timer in case the front-end gets wedged and |
317 // never requests further activity. Read-only transactions don't | 370 // never requests further activity. Read-only transactions don't |
318 // block other transactions, so don't time those out. | 371 // block other transactions, so don't time those out. |
319 if (mode_ != indexed_db::TRANSACTION_READ_ONLY) { | 372 if (mode_ != indexed_db::TRANSACTION_READ_ONLY) { |
320 timeout_timer_.Start( | 373 timeout_timer_.Start( |
321 FROM_HERE, | 374 FROM_HERE, |
322 base::TimeDelta::FromSeconds(kInactivityTimeoutPeriodSeconds), | 375 base::TimeDelta::FromSeconds(kInactivityTimeoutPeriodSeconds), |
323 base::Bind(&IndexedDBTransaction::Timeout, this)); | 376 base::Bind(&IndexedDBTransaction::Timeout, this)); |
324 } | 377 } |
325 } | 378 } |
326 | 379 |
327 void IndexedDBTransaction::Timeout() { | 380 void IndexedDBTransaction::Timeout() { |
328 Abort(IndexedDBDatabaseError( | 381 Abort(IndexedDBDatabaseError( |
329 blink::WebIDBDatabaseExceptionTimeoutError, | 382 blink::WebIDBDatabaseExceptionTimeoutError, |
330 base::ASCIIToUTF16("Transaction timed out due to inactivity."))); | 383 base::ASCIIToUTF16("Transaction timed out due to inactivity."))); |
331 } | 384 } |
332 | 385 |
333 void IndexedDBTransaction::CloseOpenCursors() { | 386 void IndexedDBTransaction::CloseOpenCursors() { |
334 for (std::set<IndexedDBCursor*>::iterator i = open_cursors_.begin(); | 387 for (std::set<IndexedDBCursor*>::iterator i = open_cursors_.begin(); |
335 i != open_cursors_.end(); | 388 i != open_cursors_.end(); |
336 ++i) | 389 ++i) |
337 (*i)->Close(); | 390 (*i)->Close(); |
338 open_cursors_.clear(); | 391 open_cursors_.clear(); |
339 } | 392 } |
340 | 393 |
341 } // namespace content | 394 } // namespace content |
OLD | NEW |