| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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/blockfile/rankings.h" | 5 #include "net/disk_cache/blockfile/rankings.h" |
| 6 | 6 |
| 7 #include "base/metrics/histogram.h" | 7 #include "base/metrics/histogram.h" |
| 8 #include "net/disk_cache/blockfile/backend_impl.h" | 8 #include "net/disk_cache/blockfile/backend_impl.h" |
| 9 #include "net/disk_cache/blockfile/disk_format.h" | 9 #include "net/disk_cache/blockfile/disk_format.h" |
| 10 #include "net/disk_cache/blockfile/entry_impl.h" | 10 #include "net/disk_cache/blockfile/entry_impl.h" |
| 11 #include "net/disk_cache/blockfile/errors.h" | 11 #include "net/disk_cache/blockfile/errors.h" |
| 12 #include "net/disk_cache/blockfile/histogram_macros.h" | 12 #include "net/disk_cache/blockfile/histogram_macros.h" |
| 13 #include "net/disk_cache/blockfile/stress_support.h" | 13 #include "net/disk_cache/blockfile/stress_support.h" |
| 14 | 14 |
| 15 // Provide a BackendImpl object to macros from histogram_macros.h. | 15 // Provide a BackendImpl object to macros from histogram_macros.h. |
| 16 #define CACHE_UMA_BACKEND_IMPL_OBJ backend_ | 16 #define CACHE_UMA_BACKEND_IMPL_OBJ backend_ |
| 17 | 17 |
| 18 using base::Time; | 18 using base::Time; |
| 19 using base::TimeTicks; | 19 using base::TimeTicks; |
| 20 | 20 |
| 21 namespace disk_cache { | 21 namespace disk_cache { |
| 22 // This is used by crash_cache.exe to generate unit test files. | 22 // This is used by crash_cache.exe to generate unit test files. |
| 23 NET_EXPORT_PRIVATE RankCrashes g_rankings_crash = NO_CRASH; | 23 NET_EXPORT_PRIVATE RankCrashes g_rankings_crash = NO_CRASH; |
| 24 } | 24 } |
| 25 | 25 |
| 26 namespace { | 26 namespace { |
| 27 | 27 |
| 28 enum Operation { | 28 enum Operation { INSERT = 1, REMOVE }; |
| 29 INSERT = 1, | |
| 30 REMOVE | |
| 31 }; | |
| 32 | 29 |
| 33 // This class provides a simple lock for the LRU list of rankings. Whenever an | 30 // This class provides a simple lock for the LRU list of rankings. Whenever an |
| 34 // entry is to be inserted or removed from the list, a transaction object should | 31 // entry is to be inserted or removed from the list, a transaction object should |
| 35 // be created to keep track of the operation. If the process crashes before | 32 // be created to keep track of the operation. If the process crashes before |
| 36 // finishing the operation, the transaction record (stored as part of the user | 33 // finishing the operation, the transaction record (stored as part of the user |
| 37 // data on the file header) can be used to finish the operation. | 34 // data on the file header) can be used to finish the operation. |
| 38 class Transaction { | 35 class Transaction { |
| 39 public: | 36 public: |
| 40 // addr is the cache addres of the node being inserted or removed. We want to | 37 // addr is the cache addres of the node being inserted or removed. We want to |
| 41 // avoid having the compiler doing optimizations on when to read or write | 38 // avoid having the compiler doing optimizations on when to read or write |
| 42 // from user_data because it is the basis of the crash detection. Maybe | 39 // from user_data because it is the basis of the crash detection. Maybe |
| 43 // volatile is not enough for that, but it should be a good hint. | 40 // volatile is not enough for that, but it should be a good hint. |
| 44 Transaction(volatile disk_cache::LruData* data, disk_cache::Addr addr, | 41 Transaction(volatile disk_cache::LruData* data, |
| 45 Operation op, int list); | 42 disk_cache::Addr addr, |
| 43 Operation op, |
| 44 int list); |
| 46 ~Transaction(); | 45 ~Transaction(); |
| 46 |
| 47 private: | 47 private: |
| 48 volatile disk_cache::LruData* data_; | 48 volatile disk_cache::LruData* data_; |
| 49 DISALLOW_COPY_AND_ASSIGN(Transaction); | 49 DISALLOW_COPY_AND_ASSIGN(Transaction); |
| 50 }; | 50 }; |
| 51 | 51 |
| 52 Transaction::Transaction(volatile disk_cache::LruData* data, | 52 Transaction::Transaction(volatile disk_cache::LruData* data, |
| 53 disk_cache::Addr addr, Operation op, int list) | 53 disk_cache::Addr addr, |
| 54 Operation op, |
| 55 int list) |
| 54 : data_(data) { | 56 : data_(data) { |
| 55 DCHECK(!data_->transaction); | 57 DCHECK(!data_->transaction); |
| 56 DCHECK(addr.is_initialized()); | 58 DCHECK(addr.is_initialized()); |
| 57 data_->operation = op; | 59 data_->operation = op; |
| 58 data_->operation_list = list; | 60 data_->operation_list = list; |
| 59 data_->transaction = addr.value(); | 61 data_->transaction = addr.value(); |
| 60 } | 62 } |
| 61 | 63 |
| 62 Transaction::~Transaction() { | 64 Transaction::~Transaction() { |
| 63 DCHECK(data_->transaction); | 65 DCHECK(data_->transaction); |
| 64 data_->transaction = 0; | 66 data_->transaction = 0; |
| 65 data_->operation = 0; | 67 data_->operation = 0; |
| 66 data_->operation_list = 0; | 68 data_->operation_list = 0; |
| 67 } | 69 } |
| 68 | 70 |
| 69 // Code locations that can generate crashes. | 71 // Code locations that can generate crashes. |
| 70 enum CrashLocation { | 72 enum CrashLocation { |
| 71 ON_INSERT_1, ON_INSERT_2, ON_INSERT_3, ON_INSERT_4, ON_REMOVE_1, ON_REMOVE_2, | 73 ON_INSERT_1, |
| 72 ON_REMOVE_3, ON_REMOVE_4, ON_REMOVE_5, ON_REMOVE_6, ON_REMOVE_7, ON_REMOVE_8 | 74 ON_INSERT_2, |
| 75 ON_INSERT_3, |
| 76 ON_INSERT_4, |
| 77 ON_REMOVE_1, |
| 78 ON_REMOVE_2, |
| 79 ON_REMOVE_3, |
| 80 ON_REMOVE_4, |
| 81 ON_REMOVE_5, |
| 82 ON_REMOVE_6, |
| 83 ON_REMOVE_7, |
| 84 ON_REMOVE_8 |
| 73 }; | 85 }; |
| 74 | 86 |
| 75 #ifndef NDEBUG | 87 #ifndef NDEBUG |
| 76 void TerminateSelf() { | 88 void TerminateSelf() { |
| 77 #if defined(OS_WIN) | 89 #if defined(OS_WIN) |
| 78 // Windows does more work on _exit() than we would like, so we force exit. | 90 // Windows does more work on _exit() than we would like, so we force exit. |
| 79 TerminateProcess(GetCurrentProcess(), 0); | 91 TerminateProcess(GetCurrentProcess(), 0); |
| 80 #elif defined(OS_POSIX) | 92 #elif defined(OS_POSIX) |
| 81 // On POSIX, _exit() will terminate the process with minimal cleanup, | 93 // On POSIX, _exit() will terminate the process with minimal cleanup, |
| 82 // and it is cleaner than killing. | 94 // and it is cleaner than killing. |
| (...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 186 base::Time now = base::Time::Now(); | 198 base::Time now = base::Time::Now(); |
| 187 node->Data()->last_used = now.ToInternalValue(); | 199 node->Data()->last_used = now.ToInternalValue(); |
| 188 if (modified) | 200 if (modified) |
| 189 node->Data()->last_modified = now.ToInternalValue(); | 201 node->Data()->last_modified = now.ToInternalValue(); |
| 190 } | 202 } |
| 191 | 203 |
| 192 } // namespace | 204 } // namespace |
| 193 | 205 |
| 194 namespace disk_cache { | 206 namespace disk_cache { |
| 195 | 207 |
| 196 Rankings::ScopedRankingsBlock::ScopedRankingsBlock() : rankings_(NULL) {} | 208 Rankings::ScopedRankingsBlock::ScopedRankingsBlock() : rankings_(NULL) { |
| 209 } |
| 197 | 210 |
| 198 Rankings::ScopedRankingsBlock::ScopedRankingsBlock(Rankings* rankings) | 211 Rankings::ScopedRankingsBlock::ScopedRankingsBlock(Rankings* rankings) |
| 199 : rankings_(rankings) {} | 212 : rankings_(rankings) { |
| 213 } |
| 200 | 214 |
| 201 Rankings::ScopedRankingsBlock::ScopedRankingsBlock( | 215 Rankings::ScopedRankingsBlock::ScopedRankingsBlock(Rankings* rankings, |
| 202 Rankings* rankings, CacheRankingsBlock* node) | 216 CacheRankingsBlock* node) |
| 203 : scoped_ptr<CacheRankingsBlock>(node), rankings_(rankings) {} | 217 : scoped_ptr<CacheRankingsBlock>(node), rankings_(rankings) { |
| 218 } |
| 204 | 219 |
| 205 Rankings::Iterator::Iterator(Rankings* rankings) { | 220 Rankings::Iterator::Iterator(Rankings* rankings) { |
| 206 memset(this, 0, sizeof(Iterator)); | 221 memset(this, 0, sizeof(Iterator)); |
| 207 my_rankings = rankings; | 222 my_rankings = rankings; |
| 208 } | 223 } |
| 209 | 224 |
| 210 Rankings::Iterator::~Iterator() { | 225 Rankings::Iterator::~Iterator() { |
| 211 for (int i = 0; i < 3; i++) | 226 for (int i = 0; i < 3; i++) |
| 212 ScopedRankingsBlock(my_rankings, nodes[i]); | 227 ScopedRankingsBlock(my_rankings, nodes[i]); |
| 213 } | 228 } |
| 214 | 229 |
| 215 Rankings::Rankings() : init_(false) {} | 230 Rankings::Rankings() : init_(false) { |
| 231 } |
| 216 | 232 |
| 217 Rankings::~Rankings() {} | 233 Rankings::~Rankings() { |
| 234 } |
| 218 | 235 |
| 219 bool Rankings::Init(BackendImpl* backend, bool count_lists) { | 236 bool Rankings::Init(BackendImpl* backend, bool count_lists) { |
| 220 DCHECK(!init_); | 237 DCHECK(!init_); |
| 221 if (init_) | 238 if (init_) |
| 222 return false; | 239 return false; |
| 223 | 240 |
| 224 backend_ = backend; | 241 backend_ = backend; |
| 225 control_data_ = backend_->GetLruData(); | 242 control_data_ = backend_->GetLruData(); |
| 226 count_lists_ = count_lists; | 243 count_lists_ = count_lists; |
| 227 | 244 |
| (...skipping 20 matching lines...) Expand all Loading... |
| 248 Trace("Insert 0x%x l %d", node->address().value(), list); | 265 Trace("Insert 0x%x l %d", node->address().value(), list); |
| 249 DCHECK(node->HasData()); | 266 DCHECK(node->HasData()); |
| 250 Addr& my_head = heads_[list]; | 267 Addr& my_head = heads_[list]; |
| 251 Addr& my_tail = tails_[list]; | 268 Addr& my_tail = tails_[list]; |
| 252 Transaction lock(control_data_, node->address(), INSERT, list); | 269 Transaction lock(control_data_, node->address(), INSERT, list); |
| 253 CacheRankingsBlock head(backend_->File(my_head), my_head); | 270 CacheRankingsBlock head(backend_->File(my_head), my_head); |
| 254 if (my_head.is_initialized()) { | 271 if (my_head.is_initialized()) { |
| 255 if (!GetRanking(&head)) | 272 if (!GetRanking(&head)) |
| 256 return; | 273 return; |
| 257 | 274 |
| 258 if (head.Data()->prev != my_head.value() && // Normal path. | 275 if (head.Data()->prev != my_head.value() && // Normal path. |
| 259 head.Data()->prev != node->address().value()) { // FinishInsert(). | 276 head.Data()->prev != node->address().value()) { // FinishInsert(). |
| 260 backend_->CriticalError(ERR_INVALID_LINKS); | 277 backend_->CriticalError(ERR_INVALID_LINKS); |
| 261 return; | 278 return; |
| 262 } | 279 } |
| 263 | 280 |
| 264 head.Data()->prev = node->address().value(); | 281 head.Data()->prev = node->address().value(); |
| 265 head.Store(); | 282 head.Store(); |
| 266 GenerateCrash(ON_INSERT_1); | 283 GenerateCrash(ON_INSERT_1); |
| 267 UpdateIterators(&head); | 284 UpdateIterators(&head); |
| 268 } | 285 } |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 309 // 2. r(r, b), b(r, y), head(b), tail(y) WriteHead() | 326 // 2. r(r, b), b(r, y), head(b), tail(y) WriteHead() |
| 310 // 3. r(r, b), b(b, y), head(b), tail(y) next.Store() | 327 // 3. r(r, b), b(b, y), head(b), tail(y) next.Store() |
| 311 // 4. r(0, 0), b(b, y), head(b), tail(y) prev.Store() | 328 // 4. r(0, 0), b(b, y), head(b), tail(y) prev.Store() |
| 312 // | 329 // |
| 313 // D. Remove tail: | 330 // D. Remove tail: |
| 314 // 1. a(x, r), r(a, r), head(x), tail(r) initial state | 331 // 1. a(x, r), r(a, r), head(x), tail(r) initial state |
| 315 // 2. a(x, r), r(a, r), head(x), tail(a) WriteTail() | 332 // 2. a(x, r), r(a, r), head(x), tail(a) WriteTail() |
| 316 // 3. a(x, a), r(a, r), head(x), tail(a) prev.Store() | 333 // 3. a(x, a), r(a, r), head(x), tail(a) prev.Store() |
| 317 // 4. a(x, a), r(0, 0), head(x), tail(a) next.Store() | 334 // 4. a(x, a), r(0, 0), head(x), tail(a) next.Store() |
| 318 void Rankings::Remove(CacheRankingsBlock* node, List list, bool strict) { | 335 void Rankings::Remove(CacheRankingsBlock* node, List list, bool strict) { |
| 319 Trace("Remove 0x%x (0x%x 0x%x) l %d", node->address().value(), | 336 Trace("Remove 0x%x (0x%x 0x%x) l %d", |
| 320 node->Data()->next, node->Data()->prev, list); | 337 node->address().value(), |
| 338 node->Data()->next, |
| 339 node->Data()->prev, |
| 340 list); |
| 321 DCHECK(node->HasData()); | 341 DCHECK(node->HasData()); |
| 322 if (strict) | 342 if (strict) |
| 323 InvalidateIterators(node); | 343 InvalidateIterators(node); |
| 324 | 344 |
| 325 Addr next_addr(node->Data()->next); | 345 Addr next_addr(node->Data()->next); |
| 326 Addr prev_addr(node->Data()->prev); | 346 Addr prev_addr(node->Data()->prev); |
| 327 if (!next_addr.is_initialized() || next_addr.is_separate_file() || | 347 if (!next_addr.is_initialized() || next_addr.is_separate_file() || |
| 328 !prev_addr.is_initialized() || prev_addr.is_separate_file()) { | 348 !prev_addr.is_initialized() || prev_addr.is_separate_file()) { |
| 329 if (next_addr.is_initialized() || prev_addr.is_initialized()) { | 349 if (next_addr.is_initialized() || prev_addr.is_initialized()) { |
| 330 LOG(ERROR) << "Invalid rankings info."; | 350 LOG(ERROR) << "Invalid rankings info."; |
| (...skipping 404 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 735 WriteTail(my_list); | 755 WriteTail(my_list); |
| 736 } | 756 } |
| 737 | 757 |
| 738 next.Store(); | 758 next.Store(); |
| 739 prev.Store(); | 759 prev.Store(); |
| 740 control_data_->transaction = 0; | 760 control_data_->transaction = 0; |
| 741 control_data_->operation = 0; | 761 control_data_->operation = 0; |
| 742 backend_->FlushIndex(); | 762 backend_->FlushIndex(); |
| 743 } | 763 } |
| 744 | 764 |
| 745 bool Rankings::CheckLinks(CacheRankingsBlock* node, CacheRankingsBlock* prev, | 765 bool Rankings::CheckLinks(CacheRankingsBlock* node, |
| 746 CacheRankingsBlock* next, List* list) { | 766 CacheRankingsBlock* prev, |
| 767 CacheRankingsBlock* next, |
| 768 List* list) { |
| 747 CacheAddr node_addr = node->address().value(); | 769 CacheAddr node_addr = node->address().value(); |
| 748 if (prev->Data()->next == node_addr && | 770 if (prev->Data()->next == node_addr && next->Data()->prev == node_addr) { |
| 749 next->Data()->prev == node_addr) { | |
| 750 // A regular linked node. | 771 // A regular linked node. |
| 751 return true; | 772 return true; |
| 752 } | 773 } |
| 753 | 774 |
| 754 Trace("CheckLinks 0x%x (0x%x 0x%x)", node_addr, | 775 Trace("CheckLinks 0x%x (0x%x 0x%x)", |
| 755 prev->Data()->next, next->Data()->prev); | 776 node_addr, |
| 777 prev->Data()->next, |
| 778 next->Data()->prev); |
| 756 | 779 |
| 757 if (node_addr != prev->address().value() && | 780 if (node_addr != prev->address().value() && |
| 758 node_addr != next->address().value() && | 781 node_addr != next->address().value() && |
| 759 prev->Data()->next == next->address().value() && | 782 prev->Data()->next == next->address().value() && |
| 760 next->Data()->prev == prev->address().value()) { | 783 next->Data()->prev == prev->address().value()) { |
| 761 // The list is actually ok, node is wrong. | 784 // The list is actually ok, node is wrong. |
| 762 Trace("node 0x%x out of list %d", node_addr, list); | 785 Trace("node 0x%x out of list %d", node_addr, list); |
| 763 node->Data()->next = 0; | 786 node->Data()->next = 0; |
| 764 node->Data()->prev = 0; | 787 node->Data()->prev = 0; |
| 765 node->Store(); | 788 node->Store(); |
| 766 return false; | 789 return false; |
| 767 } | 790 } |
| 768 | 791 |
| 769 if (prev->Data()->next == node_addr || | 792 if (prev->Data()->next == node_addr || next->Data()->prev == node_addr) { |
| 770 next->Data()->prev == node_addr) { | |
| 771 // Only one link is weird, lets double check. | 793 // Only one link is weird, lets double check. |
| 772 if (prev->Data()->next != node_addr && IsHead(node_addr, list)) | 794 if (prev->Data()->next != node_addr && IsHead(node_addr, list)) |
| 773 return true; | 795 return true; |
| 774 | 796 |
| 775 if (next->Data()->prev != node_addr && IsTail(node_addr, list)) | 797 if (next->Data()->prev != node_addr && IsTail(node_addr, list)) |
| 776 return true; | 798 return true; |
| 777 } | 799 } |
| 778 | 800 |
| 779 LOG(ERROR) << "Inconsistent LRU."; | 801 LOG(ERROR) << "Inconsistent LRU."; |
| 780 STRESS_NOTREACHED(); | 802 STRESS_NOTREACHED(); |
| (...skipping 11 matching lines...) Expand all Loading... |
| 792 backend_->CriticalError(ERR_INVALID_LINKS); | 814 backend_->CriticalError(ERR_INVALID_LINKS); |
| 793 return false; | 815 return false; |
| 794 } | 816 } |
| 795 | 817 |
| 796 return true; | 818 return true; |
| 797 } | 819 } |
| 798 | 820 |
| 799 int Rankings::CheckList(List list) { | 821 int Rankings::CheckList(List list) { |
| 800 Addr last1, last2; | 822 Addr last1, last2; |
| 801 int head_items; | 823 int head_items; |
| 802 int rv = CheckListSection(list, last1, last2, true, // Head to tail. | 824 int rv = CheckListSection(list, |
| 803 &last1, &last2, &head_items); | 825 last1, |
| 826 last2, |
| 827 true, // Head to tail. |
| 828 &last1, |
| 829 &last2, |
| 830 &head_items); |
| 804 if (rv == ERR_NO_ERROR) | 831 if (rv == ERR_NO_ERROR) |
| 805 return head_items; | 832 return head_items; |
| 806 | 833 |
| 807 return rv; | 834 return rv; |
| 808 } | 835 } |
| 809 | 836 |
| 810 // Note that the returned error codes assume a forward walk (from head to tail) | 837 // Note that the returned error codes assume a forward walk (from head to tail) |
| 811 // so they have to be adjusted accordingly by the caller. We use two stop values | 838 // so they have to be adjusted accordingly by the caller. We use two stop values |
| 812 // to be able to detect a corrupt node at the end that is not linked going back. | 839 // to be able to detect a corrupt node at the end that is not linked going back. |
| 813 int Rankings::CheckListSection(List list, Addr end1, Addr end2, bool forward, | 840 int Rankings::CheckListSection(List list, |
| 814 Addr* last, Addr* second_last, int* num_items) { | 841 Addr end1, |
| 842 Addr end2, |
| 843 bool forward, |
| 844 Addr* last, |
| 845 Addr* second_last, |
| 846 int* num_items) { |
| 815 Addr current = forward ? heads_[list] : tails_[list]; | 847 Addr current = forward ? heads_[list] : tails_[list]; |
| 816 *last = *second_last = current; | 848 *last = *second_last = current; |
| 817 *num_items = 0; | 849 *num_items = 0; |
| 818 if (!current.is_initialized()) | 850 if (!current.is_initialized()) |
| 819 return ERR_NO_ERROR; | 851 return ERR_NO_ERROR; |
| 820 | 852 |
| 821 if (!current.SanityCheckForRankings()) | 853 if (!current.SanityCheckForRankings()) |
| 822 return ERR_INVALID_HEAD; | 854 return ERR_INVALID_HEAD; |
| 823 | 855 |
| 824 scoped_ptr<CacheRankingsBlock> node; | 856 scoped_ptr<CacheRankingsBlock> node; |
| (...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 916 void Rankings::DecrementCounter(List list) { | 948 void Rankings::DecrementCounter(List list) { |
| 917 if (!count_lists_) | 949 if (!count_lists_) |
| 918 return; | 950 return; |
| 919 | 951 |
| 920 DCHECK(control_data_->sizes[list] > 0); | 952 DCHECK(control_data_->sizes[list] > 0); |
| 921 if (control_data_->sizes[list] > 0) | 953 if (control_data_->sizes[list] > 0) |
| 922 control_data_->sizes[list]--; | 954 control_data_->sizes[list]--; |
| 923 } | 955 } |
| 924 | 956 |
| 925 } // namespace disk_cache | 957 } // namespace disk_cache |
| OLD | NEW |