OLD | NEW |
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2006-2008 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 "net/disk_cache/rankings.h" | 5 #include "net/disk_cache/rankings.h" |
6 | 6 |
7 #include "base/metrics/histogram.h" | 7 #include "base/metrics/histogram.h" |
8 #include "net/disk_cache/backend_impl.h" | 8 #include "net/disk_cache/backend_impl.h" |
9 #include "net/disk_cache/entry_impl.h" | 9 #include "net/disk_cache/entry_impl.h" |
10 #include "net/disk_cache/errors.h" | 10 #include "net/disk_cache/errors.h" |
(...skipping 210 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
221 | 221 |
222 void Rankings::Reset() { | 222 void Rankings::Reset() { |
223 init_ = false; | 223 init_ = false; |
224 for (int i = 0; i < LAST_ELEMENT; i++) { | 224 for (int i = 0; i < LAST_ELEMENT; i++) { |
225 heads_[i].set_value(0); | 225 heads_[i].set_value(0); |
226 tails_[i].set_value(0); | 226 tails_[i].set_value(0); |
227 } | 227 } |
228 control_data_ = NULL; | 228 control_data_ = NULL; |
229 } | 229 } |
230 | 230 |
231 bool Rankings::GetRanking(CacheRankingsBlock* rankings) { | |
232 if (!rankings->address().is_initialized()) | |
233 return false; | |
234 | |
235 TimeTicks start = TimeTicks::Now(); | |
236 if (!rankings->Load()) | |
237 return false; | |
238 | |
239 if (!SanityCheck(rankings, true)) { | |
240 backend_->CriticalError(ERR_INVALID_LINKS); | |
241 return false; | |
242 } | |
243 | |
244 backend_->OnEvent(Stats::OPEN_RANKINGS); | |
245 | |
246 // "dummy" is the old "pointer" value, so it has to be 0. | |
247 if (!rankings->Data()->dirty && !rankings->Data()->dummy) | |
248 return true; | |
249 | |
250 EntryImpl* entry = backend_->GetOpenEntry(rankings); | |
251 if (backend_->GetCurrentEntryId() != rankings->Data()->dirty || !entry) { | |
252 // We cannot trust this entry, but we cannot initiate a cleanup from this | |
253 // point (we may be in the middle of a cleanup already). Just get rid of | |
254 // the invalid pointer and continue; the entry will be deleted when detected | |
255 // from a regular open/create path. | |
256 rankings->Data()->dummy = 0; | |
257 rankings->Data()->dirty = backend_->GetCurrentEntryId() - 1; | |
258 if (!rankings->Data()->dirty) | |
259 rankings->Data()->dirty--; | |
260 return true; | |
261 } | |
262 | |
263 // Note that we should not leave this module without deleting rankings first. | |
264 rankings->SetData(entry->rankings()->Data()); | |
265 | |
266 CACHE_UMA(AGE_MS, "GetRankings", 0, start); | |
267 return true; | |
268 } | |
269 | |
270 void Rankings::ConvertToLongLived(CacheRankingsBlock* rankings) { | |
271 if (rankings->own_data()) | |
272 return; | |
273 | |
274 // We cannot return a shared node because we are not keeping a reference | |
275 // to the entry that owns the buffer. Make this node a copy of the one that | |
276 // we have, and let the iterator logic update it when the entry changes. | |
277 CacheRankingsBlock temp(NULL, Addr(0)); | |
278 *temp.Data() = *rankings->Data(); | |
279 rankings->StopSharingData(); | |
280 *rankings->Data() = *temp.Data(); | |
281 } | |
282 | |
283 void Rankings::Insert(CacheRankingsBlock* node, bool modified, List list) { | 231 void Rankings::Insert(CacheRankingsBlock* node, bool modified, List list) { |
284 Trace("Insert 0x%x l %d", node->address().value(), list); | 232 Trace("Insert 0x%x l %d", node->address().value(), list); |
285 DCHECK(node->HasData()); | 233 DCHECK(node->HasData()); |
286 Addr& my_head = heads_[list]; | 234 Addr& my_head = heads_[list]; |
287 Addr& my_tail = tails_[list]; | 235 Addr& my_tail = tails_[list]; |
288 Transaction lock(control_data_, node->address(), INSERT, list); | 236 Transaction lock(control_data_, node->address(), INSERT, list); |
289 CacheRankingsBlock head(backend_->File(my_head), my_head); | 237 CacheRankingsBlock head(backend_->File(my_head), my_head); |
290 if (my_head.is_initialized()) { | 238 if (my_head.is_initialized()) { |
291 if (!GetRanking(&head)) | 239 if (!GetRanking(&head)) |
292 return; | 240 return; |
(...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
436 node->set_modified(); | 384 node->set_modified(); |
437 return; | 385 return; |
438 } | 386 } |
439 | 387 |
440 TimeTicks start = TimeTicks::Now(); | 388 TimeTicks start = TimeTicks::Now(); |
441 Remove(node, list); | 389 Remove(node, list); |
442 Insert(node, modified, list); | 390 Insert(node, modified, list); |
443 CACHE_UMA(AGE_MS, "UpdateRank", 0, start); | 391 CACHE_UMA(AGE_MS, "UpdateRank", 0, start); |
444 } | 392 } |
445 | 393 |
446 void Rankings::CompleteTransaction() { | |
447 Addr node_addr(static_cast<CacheAddr>(control_data_->transaction)); | |
448 if (!node_addr.is_initialized() || node_addr.is_separate_file()) { | |
449 NOTREACHED(); | |
450 LOG(ERROR) << "Invalid rankings info."; | |
451 return; | |
452 } | |
453 | |
454 Trace("CompleteTransaction 0x%x", node_addr.value()); | |
455 | |
456 CacheRankingsBlock node(backend_->File(node_addr), node_addr); | |
457 if (!node.Load()) | |
458 return; | |
459 | |
460 node.Data()->dummy = 0; | |
461 node.Store(); | |
462 | |
463 Addr& my_head = heads_[control_data_->operation_list]; | |
464 Addr& my_tail = tails_[control_data_->operation_list]; | |
465 | |
466 // We want to leave the node inside the list. The entry must me marked as | |
467 // dirty, and will be removed later. Otherwise, we'll get assertions when | |
468 // attempting to remove the dirty entry. | |
469 if (INSERT == control_data_->operation) { | |
470 Trace("FinishInsert h:0x%x t:0x%x", my_head.value(), my_tail.value()); | |
471 FinishInsert(&node); | |
472 } else if (REMOVE == control_data_->operation) { | |
473 Trace("RevertRemove h:0x%x t:0x%x", my_head.value(), my_tail.value()); | |
474 RevertRemove(&node); | |
475 } else { | |
476 NOTREACHED(); | |
477 LOG(ERROR) << "Invalid operation to recover."; | |
478 } | |
479 } | |
480 | |
481 void Rankings::FinishInsert(CacheRankingsBlock* node) { | |
482 control_data_->transaction = 0; | |
483 control_data_->operation = 0; | |
484 Addr& my_head = heads_[control_data_->operation_list]; | |
485 Addr& my_tail = tails_[control_data_->operation_list]; | |
486 if (my_head.value() != node->address().value()) { | |
487 if (my_tail.value() == node->address().value()) { | |
488 // This part will be skipped by the logic of Insert. | |
489 node->Data()->next = my_tail.value(); | |
490 } | |
491 | |
492 Insert(node, true, static_cast<List>(control_data_->operation_list)); | |
493 } | |
494 | |
495 // Tell the backend about this entry. | |
496 backend_->RecoveredEntry(node); | |
497 } | |
498 | |
499 void Rankings::RevertRemove(CacheRankingsBlock* node) { | |
500 Addr next_addr(node->Data()->next); | |
501 Addr prev_addr(node->Data()->prev); | |
502 if (!next_addr.is_initialized() || !prev_addr.is_initialized()) { | |
503 // The operation actually finished. Nothing to do. | |
504 control_data_->transaction = 0; | |
505 return; | |
506 } | |
507 if (next_addr.is_separate_file() || prev_addr.is_separate_file()) { | |
508 NOTREACHED(); | |
509 LOG(WARNING) << "Invalid rankings info."; | |
510 control_data_->transaction = 0; | |
511 return; | |
512 } | |
513 | |
514 CacheRankingsBlock next(backend_->File(next_addr), next_addr); | |
515 CacheRankingsBlock prev(backend_->File(prev_addr), prev_addr); | |
516 if (!next.Load() || !prev.Load()) | |
517 return; | |
518 | |
519 CacheAddr node_value = node->address().value(); | |
520 DCHECK(prev.Data()->next == node_value || | |
521 prev.Data()->next == prev_addr.value() || | |
522 prev.Data()->next == next.address().value()); | |
523 DCHECK(next.Data()->prev == node_value || | |
524 next.Data()->prev == next_addr.value() || | |
525 next.Data()->prev == prev.address().value()); | |
526 | |
527 if (node_value != prev_addr.value()) | |
528 prev.Data()->next = node_value; | |
529 if (node_value != next_addr.value()) | |
530 next.Data()->prev = node_value; | |
531 | |
532 List my_list = static_cast<List>(control_data_->operation_list); | |
533 Addr& my_head = heads_[my_list]; | |
534 Addr& my_tail = tails_[my_list]; | |
535 if (!my_head.is_initialized() || !my_tail.is_initialized()) { | |
536 my_head.set_value(node_value); | |
537 my_tail.set_value(node_value); | |
538 WriteHead(my_list); | |
539 WriteTail(my_list); | |
540 } else if (my_head.value() == next.address().value()) { | |
541 my_head.set_value(node_value); | |
542 prev.Data()->next = next.address().value(); | |
543 WriteHead(my_list); | |
544 } else if (my_tail.value() == prev.address().value()) { | |
545 my_tail.set_value(node_value); | |
546 next.Data()->prev = prev.address().value(); | |
547 WriteTail(my_list); | |
548 } | |
549 | |
550 next.Store(); | |
551 prev.Store(); | |
552 control_data_->transaction = 0; | |
553 control_data_->operation = 0; | |
554 } | |
555 | |
556 CacheRankingsBlock* Rankings::GetNext(CacheRankingsBlock* node, List list) { | 394 CacheRankingsBlock* Rankings::GetNext(CacheRankingsBlock* node, List list) { |
557 ScopedRankingsBlock next(this); | 395 ScopedRankingsBlock next(this); |
558 if (!node) { | 396 if (!node) { |
559 Addr& my_head = heads_[list]; | 397 Addr& my_head = heads_[list]; |
560 if (!my_head.is_initialized()) | 398 if (!my_head.is_initialized()) |
561 return NULL; | 399 return NULL; |
562 next.reset(new CacheRankingsBlock(backend_->File(my_head), my_head)); | 400 next.reset(new CacheRankingsBlock(backend_->File(my_head), my_head)); |
563 } else { | 401 } else { |
564 if (!node->HasData()) | 402 if (!node->HasData()) |
565 node->Load(); | 403 node->Load(); |
(...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
684 } | 522 } |
685 | 523 |
686 void Rankings::WriteHead(List list) { | 524 void Rankings::WriteHead(List list) { |
687 control_data_->heads[list] = heads_[list].value(); | 525 control_data_->heads[list] = heads_[list].value(); |
688 } | 526 } |
689 | 527 |
690 void Rankings::WriteTail(List list) { | 528 void Rankings::WriteTail(List list) { |
691 control_data_->tails[list] = tails_[list].value(); | 529 control_data_->tails[list] = tails_[list].value(); |
692 } | 530 } |
693 | 531 |
| 532 bool Rankings::GetRanking(CacheRankingsBlock* rankings) { |
| 533 if (!rankings->address().is_initialized()) |
| 534 return false; |
| 535 |
| 536 TimeTicks start = TimeTicks::Now(); |
| 537 if (!rankings->Load()) |
| 538 return false; |
| 539 |
| 540 if (!SanityCheck(rankings, true)) { |
| 541 backend_->CriticalError(ERR_INVALID_LINKS); |
| 542 return false; |
| 543 } |
| 544 |
| 545 backend_->OnEvent(Stats::OPEN_RANKINGS); |
| 546 |
| 547 // "dummy" is the old "pointer" value, so it has to be 0. |
| 548 if (!rankings->Data()->dirty && !rankings->Data()->dummy) |
| 549 return true; |
| 550 |
| 551 EntryImpl* entry = backend_->GetOpenEntry(rankings); |
| 552 if (backend_->GetCurrentEntryId() != rankings->Data()->dirty || !entry) { |
| 553 // We cannot trust this entry, but we cannot initiate a cleanup from this |
| 554 // point (we may be in the middle of a cleanup already). Just get rid of |
| 555 // the invalid pointer and continue; the entry will be deleted when detected |
| 556 // from a regular open/create path. |
| 557 rankings->Data()->dummy = 0; |
| 558 rankings->Data()->dirty = backend_->GetCurrentEntryId() - 1; |
| 559 if (!rankings->Data()->dirty) |
| 560 rankings->Data()->dirty--; |
| 561 return true; |
| 562 } |
| 563 |
| 564 // Note that we should not leave this module without deleting rankings first. |
| 565 rankings->SetData(entry->rankings()->Data()); |
| 566 |
| 567 CACHE_UMA(AGE_MS, "GetRankings", 0, start); |
| 568 return true; |
| 569 } |
| 570 |
| 571 void Rankings::ConvertToLongLived(CacheRankingsBlock* rankings) { |
| 572 if (rankings->own_data()) |
| 573 return; |
| 574 |
| 575 // We cannot return a shared node because we are not keeping a reference |
| 576 // to the entry that owns the buffer. Make this node a copy of the one that |
| 577 // we have, and let the iterator logic update it when the entry changes. |
| 578 CacheRankingsBlock temp(NULL, Addr(0)); |
| 579 *temp.Data() = *rankings->Data(); |
| 580 rankings->StopSharingData(); |
| 581 *rankings->Data() = *temp.Data(); |
| 582 } |
| 583 |
| 584 void Rankings::CompleteTransaction() { |
| 585 Addr node_addr(static_cast<CacheAddr>(control_data_->transaction)); |
| 586 if (!node_addr.is_initialized() || node_addr.is_separate_file()) { |
| 587 NOTREACHED(); |
| 588 LOG(ERROR) << "Invalid rankings info."; |
| 589 return; |
| 590 } |
| 591 |
| 592 Trace("CompleteTransaction 0x%x", node_addr.value()); |
| 593 |
| 594 CacheRankingsBlock node(backend_->File(node_addr), node_addr); |
| 595 if (!node.Load()) |
| 596 return; |
| 597 |
| 598 node.Data()->dummy = 0; |
| 599 node.Store(); |
| 600 |
| 601 Addr& my_head = heads_[control_data_->operation_list]; |
| 602 Addr& my_tail = tails_[control_data_->operation_list]; |
| 603 |
| 604 // We want to leave the node inside the list. The entry must me marked as |
| 605 // dirty, and will be removed later. Otherwise, we'll get assertions when |
| 606 // attempting to remove the dirty entry. |
| 607 if (INSERT == control_data_->operation) { |
| 608 Trace("FinishInsert h:0x%x t:0x%x", my_head.value(), my_tail.value()); |
| 609 FinishInsert(&node); |
| 610 } else if (REMOVE == control_data_->operation) { |
| 611 Trace("RevertRemove h:0x%x t:0x%x", my_head.value(), my_tail.value()); |
| 612 RevertRemove(&node); |
| 613 } else { |
| 614 NOTREACHED(); |
| 615 LOG(ERROR) << "Invalid operation to recover."; |
| 616 } |
| 617 } |
| 618 |
| 619 void Rankings::FinishInsert(CacheRankingsBlock* node) { |
| 620 control_data_->transaction = 0; |
| 621 control_data_->operation = 0; |
| 622 Addr& my_head = heads_[control_data_->operation_list]; |
| 623 Addr& my_tail = tails_[control_data_->operation_list]; |
| 624 if (my_head.value() != node->address().value()) { |
| 625 if (my_tail.value() == node->address().value()) { |
| 626 // This part will be skipped by the logic of Insert. |
| 627 node->Data()->next = my_tail.value(); |
| 628 } |
| 629 |
| 630 Insert(node, true, static_cast<List>(control_data_->operation_list)); |
| 631 } |
| 632 |
| 633 // Tell the backend about this entry. |
| 634 backend_->RecoveredEntry(node); |
| 635 } |
| 636 |
| 637 void Rankings::RevertRemove(CacheRankingsBlock* node) { |
| 638 Addr next_addr(node->Data()->next); |
| 639 Addr prev_addr(node->Data()->prev); |
| 640 if (!next_addr.is_initialized() || !prev_addr.is_initialized()) { |
| 641 // The operation actually finished. Nothing to do. |
| 642 control_data_->transaction = 0; |
| 643 return; |
| 644 } |
| 645 if (next_addr.is_separate_file() || prev_addr.is_separate_file()) { |
| 646 NOTREACHED(); |
| 647 LOG(WARNING) << "Invalid rankings info."; |
| 648 control_data_->transaction = 0; |
| 649 return; |
| 650 } |
| 651 |
| 652 CacheRankingsBlock next(backend_->File(next_addr), next_addr); |
| 653 CacheRankingsBlock prev(backend_->File(prev_addr), prev_addr); |
| 654 if (!next.Load() || !prev.Load()) |
| 655 return; |
| 656 |
| 657 CacheAddr node_value = node->address().value(); |
| 658 DCHECK(prev.Data()->next == node_value || |
| 659 prev.Data()->next == prev_addr.value() || |
| 660 prev.Data()->next == next.address().value()); |
| 661 DCHECK(next.Data()->prev == node_value || |
| 662 next.Data()->prev == next_addr.value() || |
| 663 next.Data()->prev == prev.address().value()); |
| 664 |
| 665 if (node_value != prev_addr.value()) |
| 666 prev.Data()->next = node_value; |
| 667 if (node_value != next_addr.value()) |
| 668 next.Data()->prev = node_value; |
| 669 |
| 670 List my_list = static_cast<List>(control_data_->operation_list); |
| 671 Addr& my_head = heads_[my_list]; |
| 672 Addr& my_tail = tails_[my_list]; |
| 673 if (!my_head.is_initialized() || !my_tail.is_initialized()) { |
| 674 my_head.set_value(node_value); |
| 675 my_tail.set_value(node_value); |
| 676 WriteHead(my_list); |
| 677 WriteTail(my_list); |
| 678 } else if (my_head.value() == next.address().value()) { |
| 679 my_head.set_value(node_value); |
| 680 prev.Data()->next = next.address().value(); |
| 681 WriteHead(my_list); |
| 682 } else if (my_tail.value() == prev.address().value()) { |
| 683 my_tail.set_value(node_value); |
| 684 next.Data()->prev = prev.address().value(); |
| 685 WriteTail(my_list); |
| 686 } |
| 687 |
| 688 next.Store(); |
| 689 prev.Store(); |
| 690 control_data_->transaction = 0; |
| 691 control_data_->operation = 0; |
| 692 } |
| 693 |
694 bool Rankings::CheckEntry(CacheRankingsBlock* rankings) { | 694 bool Rankings::CheckEntry(CacheRankingsBlock* rankings) { |
695 if (!rankings->Data()->dummy) | 695 if (!rankings->Data()->dummy) |
696 return true; | 696 return true; |
697 | 697 |
698 // If this entry is not dirty, it is a serious problem. | 698 // If this entry is not dirty, it is a serious problem. |
699 return backend_->GetCurrentEntryId() != rankings->Data()->dirty; | 699 return backend_->GetCurrentEntryId() != rankings->Data()->dirty; |
700 } | 700 } |
701 | 701 |
702 bool Rankings::CheckLinks(CacheRankingsBlock* node, CacheRankingsBlock* prev, | 702 bool Rankings::CheckLinks(CacheRankingsBlock* node, CacheRankingsBlock* prev, |
703 CacheRankingsBlock* next, List* list) { | 703 CacheRankingsBlock* next, List* list) { |
(...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
853 void Rankings::DecrementCounter(List list) { | 853 void Rankings::DecrementCounter(List list) { |
854 if (!count_lists_) | 854 if (!count_lists_) |
855 return; | 855 return; |
856 | 856 |
857 DCHECK(control_data_->sizes[list] > 0); | 857 DCHECK(control_data_->sizes[list] > 0); |
858 if (control_data_->sizes[list] > 0) | 858 if (control_data_->sizes[list] > 0) |
859 control_data_->sizes[list]--; | 859 control_data_->sizes[list]--; |
860 } | 860 } |
861 | 861 |
862 } // namespace disk_cache | 862 } // namespace disk_cache |
OLD | NEW |