| Index: net/disk_cache/rankings.cc
|
| ===================================================================
|
| --- net/disk_cache/rankings.cc (revision 6843)
|
| +++ net/disk_cache/rankings.cc (working copy)
|
| @@ -16,14 +16,6 @@
|
|
|
| namespace {
|
|
|
| -enum Lists {
|
| - NO_USE = 0, // List of entries that have not been reused.
|
| - LOW_USE, // List of entries with low reuse.
|
| - HIGH_USE, // List of entries with high reuse.
|
| - DELETED, // List of recently deleted or doomed entries.
|
| - LAST_ELEMENT
|
| -};
|
| -
|
| enum Operation {
|
| INSERT = 1,
|
| REMOVE
|
| @@ -170,8 +162,8 @@
|
|
|
| control_data_ = backend_->GetLruData();
|
|
|
| - head_ = ReadHead();
|
| - tail_ = ReadTail();
|
| + ReadHeads();
|
| + ReadTails();
|
|
|
| if (control_data_->transaction)
|
| CompleteTransaction();
|
| @@ -182,8 +174,10 @@
|
|
|
| void Rankings::Reset() {
|
| init_ = false;
|
| - head_.set_value(0);
|
| - tail_.set_value(0);
|
| + for (int i = 0; i < LAST_ELEMENT; i++) {
|
| + heads_[i].set_value(0);
|
| + tails_[i].set_value(0);
|
| + }
|
| control_data_ = NULL;
|
| }
|
|
|
| @@ -223,16 +217,18 @@
|
| return true;
|
| }
|
|
|
| -void Rankings::Insert(CacheRankingsBlock* node, bool modified) {
|
| +void Rankings::Insert(CacheRankingsBlock* node, bool modified, List list) {
|
| Trace("Insert 0x%x", node->address().value());
|
| DCHECK(node->HasData());
|
| - Transaction lock(control_data_, node->address(), INSERT, NO_USE);
|
| - CacheRankingsBlock head(backend_->File(head_), head_);
|
| - if (head_.is_initialized()) {
|
| + Addr& my_head = heads_[list];
|
| + Addr& my_tail = tails_[list];
|
| + Transaction lock(control_data_, node->address(), INSERT, list);
|
| + CacheRankingsBlock head(backend_->File(my_head), my_head);
|
| + if (my_head.is_initialized()) {
|
| if (!GetRanking(&head))
|
| return;
|
|
|
| - if (head.Data()->prev != head_.value() && // Normal path.
|
| + if (head.Data()->prev != my_head.value() && // Normal path.
|
| head.Data()->prev != node->address().value()) { // FinishInsert().
|
| backend_->CriticalError(ERR_INVALID_LINKS);
|
| return;
|
| @@ -244,14 +240,14 @@
|
| UpdateIterators(&head);
|
| }
|
|
|
| - node->Data()->next = head_.value();
|
| + node->Data()->next = my_head.value();
|
| node->Data()->prev = node->address().value();
|
| - head_.set_value(node->address().value());
|
| + my_head.set_value(node->address().value());
|
|
|
| - if (!tail_.is_initialized() || tail_.value() == node->address().value()) {
|
| - tail_.set_value(node->address().value());
|
| - node->Data()->next = tail_.value();
|
| - WriteTail();
|
| + if (!my_tail.is_initialized() || my_tail.value() == node->address().value()) {
|
| + my_tail.set_value(node->address().value());
|
| + node->Data()->next = my_tail.value();
|
| + WriteTail(list);
|
| GenerateCrash(ON_INSERT_2);
|
| }
|
|
|
| @@ -263,7 +259,7 @@
|
| GenerateCrash(ON_INSERT_3);
|
|
|
| // The last thing to do is move our head to point to a node already stored.
|
| - WriteHead();
|
| + WriteHead(list);
|
| GenerateCrash(ON_INSERT_4);
|
| }
|
|
|
| @@ -293,7 +289,7 @@
|
| // 2. a(x, r), r(a, r), head(x), tail(a) WriteTail()
|
| // 3. a(x, a), r(a, r), head(x), tail(a) prev.Store()
|
| // 4. a(x, a), r(0, 0), head(x), tail(a) next.Store()
|
| -void Rankings::Remove(CacheRankingsBlock* node) {
|
| +void Rankings::Remove(CacheRankingsBlock* node, List list) {
|
| Trace("Remove 0x%x (0x%x 0x%x)", node->address().value(), node->Data()->next,
|
| node->Data()->prev);
|
| DCHECK(node->HasData());
|
| @@ -310,35 +306,37 @@
|
| if (!GetRanking(&next) || !GetRanking(&prev))
|
| return;
|
|
|
| - if (!CheckLinks(node, &prev, &next))
|
| + if (!CheckLinks(node, &prev, &next, list))
|
| return;
|
|
|
| - Transaction lock(control_data_, node->address(), REMOVE, NO_USE);
|
| + Transaction lock(control_data_, node->address(), REMOVE, list);
|
| prev.Data()->next = next.address().value();
|
| next.Data()->prev = prev.address().value();
|
| GenerateCrash(ON_REMOVE_1);
|
|
|
| CacheAddr node_value = node->address().value();
|
| - if (node_value == head_.value() || node_value == tail_.value()) {
|
| - if (head_.value() == tail_.value()) {
|
| - head_.set_value(0);
|
| - tail_.set_value(0);
|
| + Addr& my_head = heads_[list];
|
| + Addr& my_tail = tails_[list];
|
| + if (node_value == my_head.value() || node_value == my_tail.value()) {
|
| + if (my_head.value() == my_tail.value()) {
|
| + my_head.set_value(0);
|
| + my_tail.set_value(0);
|
|
|
| - WriteHead();
|
| + WriteHead(list);
|
| GenerateCrash(ON_REMOVE_2);
|
| - WriteTail();
|
| + WriteTail(list);
|
| GenerateCrash(ON_REMOVE_3);
|
| - } else if (node_value == head_.value()) {
|
| - head_.set_value(next.address().value());
|
| + } else if (node_value == my_head.value()) {
|
| + my_head.set_value(next.address().value());
|
| next.Data()->prev = next.address().value();
|
|
|
| - WriteHead();
|
| + WriteHead(list);
|
| GenerateCrash(ON_REMOVE_4);
|
| - } else if (node_value == tail_.value()) {
|
| - tail_.set_value(prev.address().value());
|
| + } else if (node_value == my_tail.value()) {
|
| + my_tail.set_value(prev.address().value());
|
| prev.Data()->next = prev.address().value();
|
|
|
| - WriteTail();
|
| + WriteTail(list);
|
| GenerateCrash(ON_REMOVE_5);
|
|
|
| // Store the new tail to make sure we can undo the operation if we crash.
|
| @@ -366,10 +364,10 @@
|
| // list. We want to avoid that case as much as we can (as while waiting for IO),
|
| // but the net effect is just an assert on debug when attempting to remove the
|
| // entry. Otherwise we'll need reentrant transactions, which is an overkill.
|
| -void Rankings::UpdateRank(CacheRankingsBlock* node, bool modified) {
|
| +void Rankings::UpdateRank(CacheRankingsBlock* node, bool modified, List list) {
|
| Time start = Time::Now();
|
| - Remove(node);
|
| - Insert(node, modified);
|
| + Remove(node, list);
|
| + Insert(node, modified, list);
|
| UMA_HISTOGRAM_TIMES(L"DiskCache.UpdateRank", Time::Now() - start);
|
| }
|
|
|
| @@ -390,14 +388,17 @@
|
| node.Data()->pointer = NULL;
|
| node.Store();
|
|
|
| + Addr& my_head = heads_[control_data_->operation_list];
|
| + Addr& my_tail = tails_[control_data_->operation_list];
|
| +
|
| // We want to leave the node inside the list. The entry must me marked as
|
| // dirty, and will be removed later. Otherwise, we'll get assertions when
|
| // attempting to remove the dirty entry.
|
| if (INSERT == control_data_->operation) {
|
| - Trace("FinishInsert h:0x%x t:0x%x", head_.value(), tail_.value());
|
| + Trace("FinishInsert h:0x%x t:0x%x", my_head.value(), my_tail.value());
|
| FinishInsert(&node);
|
| } else if (REMOVE == control_data_->operation) {
|
| - Trace("RevertRemove h:0x%x t:0x%x", head_.value(), tail_.value());
|
| + Trace("RevertRemove h:0x%x t:0x%x", my_head.value(), my_tail.value());
|
| RevertRemove(&node);
|
| } else {
|
| NOTREACHED();
|
| @@ -408,13 +409,15 @@
|
| void Rankings::FinishInsert(CacheRankingsBlock* node) {
|
| control_data_->transaction = 0;
|
| control_data_->operation = 0;
|
| - if (head_.value() != node->address().value()) {
|
| - if (tail_.value() == node->address().value()) {
|
| + Addr& my_head = heads_[control_data_->operation_list];
|
| + Addr& my_tail = tails_[control_data_->operation_list];
|
| + if (my_head.value() != node->address().value()) {
|
| + if (my_tail.value() == node->address().value()) {
|
| // This part will be skipped by the logic of Insert.
|
| - node->Data()->next = tail_.value();
|
| + node->Data()->next = my_tail.value();
|
| }
|
|
|
| - Insert(node, true);
|
| + Insert(node, true, static_cast<List>(control_data_->operation_list));
|
| }
|
|
|
| // Tell the backend about this entry.
|
| @@ -454,19 +457,22 @@
|
| if (node_value != next_addr.value())
|
| next.Data()->prev = node_value;
|
|
|
| - if (!head_.is_initialized() || !tail_.is_initialized()) {
|
| - head_.set_value(node_value);
|
| - tail_.set_value(node_value);
|
| - WriteHead();
|
| - WriteTail();
|
| - } else if (head_.value() == next.address().value()) {
|
| - head_.set_value(node_value);
|
| + List my_list = static_cast<List>(control_data_->operation_list);
|
| + Addr& my_head = heads_[my_list];
|
| + Addr& my_tail = tails_[my_list];
|
| + if (!my_head.is_initialized() || !my_tail.is_initialized()) {
|
| + my_head.set_value(node_value);
|
| + my_tail.set_value(node_value);
|
| + WriteHead(my_list);
|
| + WriteTail(my_list);
|
| + } else if (my_head.value() == next.address().value()) {
|
| + my_head.set_value(node_value);
|
| prev.Data()->next = next.address().value();
|
| - WriteHead();
|
| - } else if (tail_.value() == prev.address().value()) {
|
| - tail_.set_value(node_value);
|
| + WriteHead(my_list);
|
| + } else if (my_tail.value() == prev.address().value()) {
|
| + my_tail.set_value(node_value);
|
| next.Data()->prev = prev.address().value();
|
| - WriteTail();
|
| + WriteTail(my_list);
|
| }
|
|
|
| next.Store();
|
| @@ -475,16 +481,18 @@
|
| control_data_->operation = 0;
|
| }
|
|
|
| -CacheRankingsBlock* Rankings::GetNext(CacheRankingsBlock* node) {
|
| +CacheRankingsBlock* Rankings::GetNext(CacheRankingsBlock* node, List list) {
|
| ScopedRankingsBlock next(this);
|
| if (!node) {
|
| - if (!head_.is_initialized())
|
| + Addr& my_head = heads_[list];
|
| + if (!my_head.is_initialized())
|
| return NULL;
|
| - next.reset(new CacheRankingsBlock(backend_->File(head_), head_));
|
| + next.reset(new CacheRankingsBlock(backend_->File(my_head), my_head));
|
| } else {
|
| - if (!tail_.is_initialized())
|
| + Addr& my_tail = tails_[list];
|
| + if (!my_tail.is_initialized())
|
| return NULL;
|
| - if (tail_.value() == node->address().value())
|
| + if (my_tail.value() == node->address().value())
|
| return NULL;
|
| Addr address(node->Data()->next);
|
| next.reset(new CacheRankingsBlock(backend_->File(address), address));
|
| @@ -501,16 +509,18 @@
|
| return next.release();
|
| }
|
|
|
| -CacheRankingsBlock* Rankings::GetPrev(CacheRankingsBlock* node) {
|
| +CacheRankingsBlock* Rankings::GetPrev(CacheRankingsBlock* node, List list) {
|
| ScopedRankingsBlock prev(this);
|
| if (!node) {
|
| - if (!tail_.is_initialized())
|
| + Addr& my_tail = tails_[list];
|
| + if (!my_tail.is_initialized())
|
| return NULL;
|
| - prev.reset(new CacheRankingsBlock(backend_->File(tail_), tail_));
|
| + prev.reset(new CacheRankingsBlock(backend_->File(my_tail), my_tail));
|
| } else {
|
| - if (!head_.is_initialized())
|
| + Addr& my_head = heads_[list];
|
| + if (!my_head.is_initialized())
|
| return NULL;
|
| - if (head_.value() == node->address().value())
|
| + if (my_head.value() == node->address().value())
|
| return NULL;
|
| Addr address(node->Data()->prev);
|
| prev.reset(new CacheRankingsBlock(backend_->File(address), address));
|
| @@ -532,40 +542,14 @@
|
| }
|
|
|
| int Rankings::SelfCheck() {
|
| - if (!head_.is_initialized()) {
|
| - if (!tail_.is_initialized())
|
| - return 0;
|
| - return ERR_INVALID_TAIL;
|
| + int total = 0;
|
| + for (int i = 0; i < LAST_ELEMENT; i++) {
|
| + int partial = CheckList(static_cast<List>(i));
|
| + if (partial < 0)
|
| + return partial;
|
| + total += partial;
|
| }
|
| - if (!tail_.is_initialized())
|
| - return ERR_INVALID_HEAD;
|
| -
|
| - if (tail_.is_separate_file())
|
| - return ERR_INVALID_TAIL;
|
| -
|
| - if (head_.is_separate_file())
|
| - return ERR_INVALID_HEAD;
|
| -
|
| - int num_items = 0;
|
| - Addr address(head_.value());
|
| - Addr prev(head_.value());
|
| - scoped_ptr<CacheRankingsBlock> node;
|
| - do {
|
| - node.reset(new CacheRankingsBlock(backend_->File(address), address));
|
| - node->Load();
|
| - if (node->Data()->prev != prev.value())
|
| - return ERR_INVALID_PREV;
|
| - if (!CheckEntry(node.get()))
|
| - return ERR_INVALID_ENTRY;
|
| -
|
| - prev.set_value(address.value());
|
| - address.set_value(node->Data()->next);
|
| - if (!address.is_initialized() || address.is_separate_file())
|
| - return ERR_INVALID_NEXT;
|
| -
|
| - num_items++;
|
| - } while (node->address().value() != address.value());
|
| - return num_items;
|
| + return total;
|
| }
|
|
|
| bool Rankings::SanityCheck(CacheRankingsBlock* node, bool from_list) {
|
| @@ -584,29 +568,31 @@
|
| if (!data->next && !data->prev && from_list)
|
| return false;
|
|
|
| - if ((node->address().value() == data->prev) && (head_.value() != data->prev))
|
| + if ((node->address().value() == data->prev) && !IsHead(data->prev))
|
| return false;
|
|
|
| - if ((node->address().value() == data->next) && (tail_.value() != data->next))
|
| + if ((node->address().value() == data->next) && !IsTail(data->next))
|
| return false;
|
|
|
| return true;
|
| }
|
|
|
| -Addr Rankings::ReadHead() {
|
| - return Addr(control_data_->heads[NO_USE]);
|
| +void Rankings::ReadHeads() {
|
| + for (int i = 0; i < LAST_ELEMENT; i++)
|
| + heads_[i] = Addr(control_data_->heads[i]);
|
| }
|
|
|
| -Addr Rankings::ReadTail() {
|
| - return Addr(control_data_->tails[NO_USE]);
|
| +void Rankings::ReadTails() {
|
| + for (int i = 0; i < LAST_ELEMENT; i++)
|
| + tails_[i] = Addr(control_data_->tails[i]);
|
| }
|
|
|
| -void Rankings::WriteHead() {
|
| - control_data_->heads[NO_USE] = head_.value();
|
| +void Rankings::WriteHead(List list) {
|
| + control_data_->heads[list] = heads_[list].value();
|
| }
|
|
|
| -void Rankings::WriteTail() {
|
| - control_data_->tails[NO_USE] = tail_.value();
|
| +void Rankings::WriteTail(List list) {
|
| + control_data_->tails[list] = tails_[list].value();
|
| }
|
|
|
| bool Rankings::CheckEntry(CacheRankingsBlock* rankings) {
|
| @@ -618,11 +604,11 @@
|
| }
|
|
|
| bool Rankings::CheckLinks(CacheRankingsBlock* node, CacheRankingsBlock* prev,
|
| - CacheRankingsBlock* next) {
|
| + CacheRankingsBlock* next, List list) {
|
| if ((prev->Data()->next != node->address().value() &&
|
| - head_.value() != node->address().value()) ||
|
| + heads_[list].value() != node->address().value()) ||
|
| (next->Data()->prev != node->address().value() &&
|
| - tail_.value() != node->address().value())) {
|
| + tails_[list].value() != node->address().value())) {
|
| LOG(ERROR) << "Inconsistent LRU.";
|
|
|
| if (prev->Data()->next == next->address().value() &&
|
| @@ -653,6 +639,61 @@
|
| return true;
|
| }
|
|
|
| +int Rankings::CheckList(List list) {
|
| + Addr& my_head = heads_[list];
|
| + Addr& my_tail = tails_[list];
|
| + if (!my_head.is_initialized()) {
|
| + if (!my_tail.is_initialized())
|
| + return 0;
|
| + // If there is no head, having a tail is an error.
|
| + return ERR_INVALID_TAIL;
|
| + }
|
| + // If there is no tail, having a head is an error.
|
| + if (!my_tail.is_initialized())
|
| + return ERR_INVALID_HEAD;
|
| +
|
| + if (my_tail.is_separate_file())
|
| + return ERR_INVALID_TAIL;
|
| +
|
| + if (my_head.is_separate_file())
|
| + return ERR_INVALID_HEAD;
|
| +
|
| + int num_items = 0;
|
| + Addr address(my_head.value());
|
| + Addr prev(my_head.value());
|
| + scoped_ptr<CacheRankingsBlock> node;
|
| + do {
|
| + node.reset(new CacheRankingsBlock(backend_->File(address), address));
|
| + node->Load();
|
| + if (node->Data()->prev != prev.value())
|
| + return ERR_INVALID_PREV;
|
| + if (!CheckEntry(node.get()))
|
| + return ERR_INVALID_ENTRY;
|
| +
|
| + prev.set_value(address.value());
|
| + address.set_value(node->Data()->next);
|
| + if (!address.is_initialized() || address.is_separate_file())
|
| + return ERR_INVALID_NEXT;
|
| +
|
| + num_items++;
|
| + } while (node->address().value() != address.value());
|
| + return num_items;
|
| +}
|
| +
|
| +bool Rankings::IsHead(CacheAddr addr) {
|
| + for (int i = 0; i < LAST_ELEMENT; i++)
|
| + if (addr == heads_[i].value())
|
| + return true;
|
| + return false;
|
| +}
|
| +
|
| +bool Rankings::IsTail(CacheAddr addr) {
|
| + for (int i = 0; i < LAST_ELEMENT; i++)
|
| + if (addr == tails_[i].value())
|
| + return true;
|
| + return false;
|
| +}
|
| +
|
| void Rankings::TrackRankingsBlock(CacheRankingsBlock* node,
|
| bool start_tracking) {
|
| if (!node)
|
|
|