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 |