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/histogram.h" | 7 #include "base/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" |
11 | 11 |
12 using base::Time; | 12 using base::Time; |
13 | 13 |
14 // This is used by crash_cache.exe to generate unit test files. | 14 // This is used by crash_cache.exe to generate unit test files. |
15 disk_cache::RankCrashes g_rankings_crash = disk_cache::NO_CRASH; | 15 disk_cache::RankCrashes g_rankings_crash = disk_cache::NO_CRASH; |
16 | 16 |
17 namespace { | 17 namespace { |
18 | 18 |
19 const int kHeadIndex = 0; | 19 enum Lists { |
20 const int kTailIndex = 1; | 20 NO_USE = 0, // List of entries that have not been reused. |
21 const int kTransactionIndex = 2; | 21 LOW_USE, // List of entries with low reuse. |
22 const int kOperationIndex = 3; | 22 HIGH_USE, // List of entries with high reuse. |
| 23 DELETED, // List of recently deleted or doomed entries. |
| 24 LAST_ELEMENT |
| 25 }; |
23 | 26 |
24 enum Operation { | 27 enum Operation { |
25 INSERT = 1, | 28 INSERT = 1, |
26 REMOVE | 29 REMOVE |
27 }; | 30 }; |
28 | 31 |
29 // This class provides a simple lock for the LRU list of rankings. Whenever an | 32 // This class provides a simple lock for the LRU list of rankings. Whenever an |
30 // entry is to be inserted or removed from the list, a transaction object should | 33 // entry is to be inserted or removed from the list, a transaction object should |
31 // be created to keep track of the operation. If the process crashes before | 34 // be created to keep track of the operation. If the process crashes before |
32 // finishing the operation, the transaction record (stored as part of the user | 35 // finishing the operation, the transaction record (stored as part of the user |
33 // data on the file header) can be used to finish the operation. | 36 // data on the file header) can be used to finish the operation. |
34 class Transaction { | 37 class Transaction { |
35 public: | 38 public: |
36 // addr is the cache addres of the node being inserted or removed. We want to | 39 // addr is the cache addres of the node being inserted or removed. We want to |
37 // avoid having the compiler doing optimizations on when to read or write | 40 // avoid having the compiler doing optimizations on when to read or write |
38 // from user_data because it is the basis of the crash detection. Maybe | 41 // from user_data because it is the basis of the crash detection. Maybe |
39 // volatile is not enough for that, but it should be a good hint. | 42 // volatile is not enough for that, but it should be a good hint. |
40 Transaction(volatile int32* user_data, disk_cache::Addr addr, Operation op); | 43 Transaction(volatile disk_cache::LruData* data, disk_cache::Addr addr, |
| 44 Operation op, int list); |
41 ~Transaction(); | 45 ~Transaction(); |
42 private: | 46 private: |
43 volatile int32* user_data_; | 47 volatile disk_cache::LruData* data_; |
44 DISALLOW_EVIL_CONSTRUCTORS(Transaction); | 48 DISALLOW_COPY_AND_ASSIGN(Transaction); |
45 }; | 49 }; |
46 | 50 |
47 Transaction::Transaction(volatile int32* user_data, disk_cache::Addr addr, | 51 Transaction::Transaction(volatile disk_cache::LruData* data, |
48 Operation op) : user_data_(user_data) { | 52 disk_cache::Addr addr, Operation op, int list) |
49 DCHECK(!user_data_[kTransactionIndex]); | 53 : data_(data) { |
| 54 DCHECK(!data_->transaction); |
50 DCHECK(addr.is_initialized()); | 55 DCHECK(addr.is_initialized()); |
51 user_data_[kOperationIndex] = op; | 56 data_->operation = op; |
52 user_data_[kTransactionIndex] = static_cast<int32>(addr.value()); | 57 data_->operation_list = list; |
| 58 data_->transaction = addr.value(); |
53 } | 59 } |
54 | 60 |
55 Transaction::~Transaction() { | 61 Transaction::~Transaction() { |
56 DCHECK(user_data_[kTransactionIndex]); | 62 DCHECK(data_->transaction); |
57 user_data_[kTransactionIndex] = 0; | 63 data_->transaction = 0; |
58 user_data_[kOperationIndex] = 0; | 64 data_->operation = 0; |
| 65 data_->operation_list = 0; |
59 } | 66 } |
60 | 67 |
61 // Code locations that can generate crashes. | 68 // Code locations that can generate crashes. |
62 enum CrashLocation { | 69 enum CrashLocation { |
63 ON_INSERT_1, ON_INSERT_2, ON_INSERT_3, ON_INSERT_4, ON_REMOVE_1, ON_REMOVE_2, | 70 ON_INSERT_1, ON_INSERT_2, ON_INSERT_3, ON_INSERT_4, ON_REMOVE_1, ON_REMOVE_2, |
64 ON_REMOVE_3, ON_REMOVE_4, ON_REMOVE_5, ON_REMOVE_6, ON_REMOVE_7, ON_REMOVE_8 | 71 ON_REMOVE_3, ON_REMOVE_4, ON_REMOVE_5, ON_REMOVE_6, ON_REMOVE_7, ON_REMOVE_8 |
65 }; | 72 }; |
66 | 73 |
67 // Generates a crash on debug builds, acording to the value of g_rankings_crash. | 74 // Generates a crash on debug builds, acording to the value of g_rankings_crash. |
68 // This used by crash_cache.exe to generate unit-test files. | 75 // This used by crash_cache.exe to generate unit-test files. |
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
153 } // namespace | 160 } // namespace |
154 | 161 |
155 namespace disk_cache { | 162 namespace disk_cache { |
156 | 163 |
157 bool Rankings::Init(BackendImpl* backend) { | 164 bool Rankings::Init(BackendImpl* backend) { |
158 DCHECK(!init_); | 165 DCHECK(!init_); |
159 if (init_) | 166 if (init_) |
160 return false; | 167 return false; |
161 | 168 |
162 backend_ = backend; | 169 backend_ = backend; |
163 MappedFile* file = backend_->File(Addr(RANKINGS, 0, 0, 0)); | |
164 | 170 |
165 header_ = reinterpret_cast<BlockFileHeader*>(file->buffer()); | 171 control_data_ = backend_->GetLruData(); |
166 | 172 |
167 head_ = ReadHead(); | 173 head_ = ReadHead(); |
168 tail_ = ReadTail(); | 174 tail_ = ReadTail(); |
169 | 175 |
170 if (header_->user[kTransactionIndex]) | 176 if (control_data_->transaction) |
171 CompleteTransaction(); | 177 CompleteTransaction(); |
172 | 178 |
173 init_ = true; | 179 init_ = true; |
174 return true; | 180 return true; |
175 } | 181 } |
176 | 182 |
177 void Rankings::Reset() { | 183 void Rankings::Reset() { |
178 init_ = false; | 184 init_ = false; |
179 head_.set_value(0); | 185 head_.set_value(0); |
180 tail_.set_value(0); | 186 tail_.set_value(0); |
181 header_ = NULL; | 187 control_data_ = NULL; |
182 } | 188 } |
183 | 189 |
184 bool Rankings::GetRanking(CacheRankingsBlock* rankings) { | 190 bool Rankings::GetRanking(CacheRankingsBlock* rankings) { |
185 Time start = Time::Now(); | 191 Time start = Time::Now(); |
186 if (!rankings->address().is_initialized()) | 192 if (!rankings->address().is_initialized()) |
187 return false; | 193 return false; |
188 | 194 |
189 if (!rankings->Load()) | 195 if (!rankings->Load()) |
190 return false; | 196 return false; |
191 | 197 |
(...skipping 21 matching lines...) Expand all Loading... |
213 EntryImpl* cache_entry = | 219 EntryImpl* cache_entry = |
214 reinterpret_cast<EntryImpl*>(rankings->Data()->pointer); | 220 reinterpret_cast<EntryImpl*>(rankings->Data()->pointer); |
215 rankings->SetData(cache_entry->rankings()->Data()); | 221 rankings->SetData(cache_entry->rankings()->Data()); |
216 UMA_HISTOGRAM_TIMES(L"DiskCache.GetRankings", Time::Now() - start); | 222 UMA_HISTOGRAM_TIMES(L"DiskCache.GetRankings", Time::Now() - start); |
217 return true; | 223 return true; |
218 } | 224 } |
219 | 225 |
220 void Rankings::Insert(CacheRankingsBlock* node, bool modified) { | 226 void Rankings::Insert(CacheRankingsBlock* node, bool modified) { |
221 Trace("Insert 0x%x", node->address().value()); | 227 Trace("Insert 0x%x", node->address().value()); |
222 DCHECK(node->HasData()); | 228 DCHECK(node->HasData()); |
223 Transaction lock(header_->user, node->address(), INSERT); | 229 Transaction lock(control_data_, node->address(), INSERT, NO_USE); |
224 CacheRankingsBlock head(backend_->File(head_), head_); | 230 CacheRankingsBlock head(backend_->File(head_), head_); |
225 if (head_.is_initialized()) { | 231 if (head_.is_initialized()) { |
226 if (!GetRanking(&head)) | 232 if (!GetRanking(&head)) |
227 return; | 233 return; |
228 | 234 |
229 if (head.Data()->prev != head_.value() && // Normal path. | 235 if (head.Data()->prev != head_.value() && // Normal path. |
230 head.Data()->prev != node->address().value()) { // FinishInsert(). | 236 head.Data()->prev != node->address().value()) { // FinishInsert(). |
231 backend_->CriticalError(ERR_INVALID_LINKS); | 237 backend_->CriticalError(ERR_INVALID_LINKS); |
232 return; | 238 return; |
233 } | 239 } |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
300 } | 306 } |
301 | 307 |
302 CacheRankingsBlock next(backend_->File(next_addr), next_addr); | 308 CacheRankingsBlock next(backend_->File(next_addr), next_addr); |
303 CacheRankingsBlock prev(backend_->File(prev_addr), prev_addr); | 309 CacheRankingsBlock prev(backend_->File(prev_addr), prev_addr); |
304 if (!GetRanking(&next) || !GetRanking(&prev)) | 310 if (!GetRanking(&next) || !GetRanking(&prev)) |
305 return; | 311 return; |
306 | 312 |
307 if (!CheckLinks(node, &prev, &next)) | 313 if (!CheckLinks(node, &prev, &next)) |
308 return; | 314 return; |
309 | 315 |
310 Transaction lock(header_->user, node->address(), REMOVE); | 316 Transaction lock(control_data_, node->address(), REMOVE, NO_USE); |
311 prev.Data()->next = next.address().value(); | 317 prev.Data()->next = next.address().value(); |
312 next.Data()->prev = prev.address().value(); | 318 next.Data()->prev = prev.address().value(); |
313 GenerateCrash(ON_REMOVE_1); | 319 GenerateCrash(ON_REMOVE_1); |
314 | 320 |
315 CacheAddr node_value = node->address().value(); | 321 CacheAddr node_value = node->address().value(); |
316 if (node_value == head_.value() || node_value == tail_.value()) { | 322 if (node_value == head_.value() || node_value == tail_.value()) { |
317 if (head_.value() == tail_.value()) { | 323 if (head_.value() == tail_.value()) { |
318 head_.set_value(0); | 324 head_.set_value(0); |
319 tail_.set_value(0); | 325 tail_.set_value(0); |
320 | 326 |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
361 // but the net effect is just an assert on debug when attempting to remove the | 367 // but the net effect is just an assert on debug when attempting to remove the |
362 // entry. Otherwise we'll need reentrant transactions, which is an overkill. | 368 // entry. Otherwise we'll need reentrant transactions, which is an overkill. |
363 void Rankings::UpdateRank(CacheRankingsBlock* node, bool modified) { | 369 void Rankings::UpdateRank(CacheRankingsBlock* node, bool modified) { |
364 Time start = Time::Now(); | 370 Time start = Time::Now(); |
365 Remove(node); | 371 Remove(node); |
366 Insert(node, modified); | 372 Insert(node, modified); |
367 UMA_HISTOGRAM_TIMES(L"DiskCache.UpdateRank", Time::Now() - start); | 373 UMA_HISTOGRAM_TIMES(L"DiskCache.UpdateRank", Time::Now() - start); |
368 } | 374 } |
369 | 375 |
370 void Rankings::CompleteTransaction() { | 376 void Rankings::CompleteTransaction() { |
371 Addr node_addr(static_cast<CacheAddr>(header_->user[kTransactionIndex])); | 377 Addr node_addr(static_cast<CacheAddr>(control_data_->transaction)); |
372 if (!node_addr.is_initialized() || node_addr.is_separate_file()) { | 378 if (!node_addr.is_initialized() || node_addr.is_separate_file()) { |
373 NOTREACHED(); | 379 NOTREACHED(); |
374 LOG(ERROR) << "Invalid rankings info."; | 380 LOG(ERROR) << "Invalid rankings info."; |
375 return; | 381 return; |
376 } | 382 } |
377 | 383 |
378 Trace("CompleteTransaction 0x%x", node_addr.value()); | 384 Trace("CompleteTransaction 0x%x", node_addr.value()); |
379 | 385 |
380 CacheRankingsBlock node(backend_->File(node_addr), node_addr); | 386 CacheRankingsBlock node(backend_->File(node_addr), node_addr); |
381 if (!node.Load()) | 387 if (!node.Load()) |
382 return; | 388 return; |
383 | 389 |
384 node.Data()->pointer = NULL; | 390 node.Data()->pointer = NULL; |
385 node.Store(); | 391 node.Store(); |
386 | 392 |
387 // We want to leave the node inside the list. The entry must me marked as | 393 // We want to leave the node inside the list. The entry must me marked as |
388 // dirty, and will be removed later. Otherwise, we'll get assertions when | 394 // dirty, and will be removed later. Otherwise, we'll get assertions when |
389 // attempting to remove the dirty entry. | 395 // attempting to remove the dirty entry. |
390 if (INSERT == header_->user[kOperationIndex]) { | 396 if (INSERT == control_data_->operation) { |
391 Trace("FinishInsert h:0x%x t:0x%x", head_.value(), tail_.value()); | 397 Trace("FinishInsert h:0x%x t:0x%x", head_.value(), tail_.value()); |
392 FinishInsert(&node); | 398 FinishInsert(&node); |
393 } else if (REMOVE == header_->user[kOperationIndex]) { | 399 } else if (REMOVE == control_data_->operation) { |
394 Trace("RevertRemove h:0x%x t:0x%x", head_.value(), tail_.value()); | 400 Trace("RevertRemove h:0x%x t:0x%x", head_.value(), tail_.value()); |
395 RevertRemove(&node); | 401 RevertRemove(&node); |
396 } else { | 402 } else { |
397 NOTREACHED(); | 403 NOTREACHED(); |
398 LOG(ERROR) << "Invalid operation to recover."; | 404 LOG(ERROR) << "Invalid operation to recover."; |
399 } | 405 } |
400 } | 406 } |
401 | 407 |
402 void Rankings::FinishInsert(CacheRankingsBlock* node) { | 408 void Rankings::FinishInsert(CacheRankingsBlock* node) { |
403 header_->user[kTransactionIndex] = 0; | 409 control_data_->transaction = 0; |
404 header_->user[kOperationIndex] = 0; | 410 control_data_->operation = 0; |
405 if (head_.value() != node->address().value()) { | 411 if (head_.value() != node->address().value()) { |
406 if (tail_.value() == node->address().value()) { | 412 if (tail_.value() == node->address().value()) { |
407 // This part will be skipped by the logic of Insert. | 413 // This part will be skipped by the logic of Insert. |
408 node->Data()->next = tail_.value(); | 414 node->Data()->next = tail_.value(); |
409 } | 415 } |
410 | 416 |
411 Insert(node, true); | 417 Insert(node, true); |
412 } | 418 } |
413 | 419 |
414 // Tell the backend about this entry. | 420 // Tell the backend about this entry. |
415 backend_->RecoveredEntry(node); | 421 backend_->RecoveredEntry(node); |
416 } | 422 } |
417 | 423 |
418 void Rankings::RevertRemove(CacheRankingsBlock* node) { | 424 void Rankings::RevertRemove(CacheRankingsBlock* node) { |
419 Addr next_addr(node->Data()->next); | 425 Addr next_addr(node->Data()->next); |
420 Addr prev_addr(node->Data()->prev); | 426 Addr prev_addr(node->Data()->prev); |
421 if (!next_addr.is_initialized() || !prev_addr.is_initialized()) { | 427 if (!next_addr.is_initialized() || !prev_addr.is_initialized()) { |
422 // The operation actually finished. Nothing to do. | 428 // The operation actually finished. Nothing to do. |
423 header_->user[kTransactionIndex] = 0; | 429 control_data_->transaction = 0; |
424 return; | 430 return; |
425 } | 431 } |
426 if (next_addr.is_separate_file() || prev_addr.is_separate_file()) { | 432 if (next_addr.is_separate_file() || prev_addr.is_separate_file()) { |
427 NOTREACHED(); | 433 NOTREACHED(); |
428 LOG(WARNING) << "Invalid rankings info."; | 434 LOG(WARNING) << "Invalid rankings info."; |
429 header_->user[kTransactionIndex] = 0; | 435 control_data_->transaction = 0; |
430 return; | 436 return; |
431 } | 437 } |
432 | 438 |
433 CacheRankingsBlock next(backend_->File(next_addr), next_addr); | 439 CacheRankingsBlock next(backend_->File(next_addr), next_addr); |
434 CacheRankingsBlock prev(backend_->File(prev_addr), prev_addr); | 440 CacheRankingsBlock prev(backend_->File(prev_addr), prev_addr); |
435 if (!next.Load() || !prev.Load()) | 441 if (!next.Load() || !prev.Load()) |
436 return; | 442 return; |
437 | 443 |
438 CacheAddr node_value = node->address().value(); | 444 CacheAddr node_value = node->address().value(); |
439 DCHECK(prev.Data()->next == node_value || | 445 DCHECK(prev.Data()->next == node_value || |
(...skipping 18 matching lines...) Expand all Loading... |
458 prev.Data()->next = next.address().value(); | 464 prev.Data()->next = next.address().value(); |
459 WriteHead(); | 465 WriteHead(); |
460 } else if (tail_.value() == prev.address().value()) { | 466 } else if (tail_.value() == prev.address().value()) { |
461 tail_.set_value(node_value); | 467 tail_.set_value(node_value); |
462 next.Data()->prev = prev.address().value(); | 468 next.Data()->prev = prev.address().value(); |
463 WriteTail(); | 469 WriteTail(); |
464 } | 470 } |
465 | 471 |
466 next.Store(); | 472 next.Store(); |
467 prev.Store(); | 473 prev.Store(); |
468 header_->user[kTransactionIndex] = 0; | 474 control_data_->transaction = 0; |
469 header_->user[kOperationIndex] = 0; | 475 control_data_->operation = 0; |
470 } | 476 } |
471 | 477 |
472 CacheRankingsBlock* Rankings::GetNext(CacheRankingsBlock* node) { | 478 CacheRankingsBlock* Rankings::GetNext(CacheRankingsBlock* node) { |
473 ScopedRankingsBlock next(this); | 479 ScopedRankingsBlock next(this); |
474 if (!node) { | 480 if (!node) { |
475 if (!head_.is_initialized()) | 481 if (!head_.is_initialized()) |
476 return NULL; | 482 return NULL; |
477 next.reset(new CacheRankingsBlock(backend_->File(head_), head_)); | 483 next.reset(new CacheRankingsBlock(backend_->File(head_), head_)); |
478 } else { | 484 } else { |
479 if (!tail_.is_initialized()) | 485 if (!tail_.is_initialized()) |
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
581 if ((node->address().value() == data->prev) && (head_.value() != data->prev)) | 587 if ((node->address().value() == data->prev) && (head_.value() != data->prev)) |
582 return false; | 588 return false; |
583 | 589 |
584 if ((node->address().value() == data->next) && (tail_.value() != data->next)) | 590 if ((node->address().value() == data->next) && (tail_.value() != data->next)) |
585 return false; | 591 return false; |
586 | 592 |
587 return true; | 593 return true; |
588 } | 594 } |
589 | 595 |
590 Addr Rankings::ReadHead() { | 596 Addr Rankings::ReadHead() { |
591 CacheAddr head = static_cast<CacheAddr>(header_->user[kHeadIndex]); | 597 return Addr(control_data_->heads[NO_USE]); |
592 return Addr(head); | |
593 } | 598 } |
594 | 599 |
595 Addr Rankings::ReadTail() { | 600 Addr Rankings::ReadTail() { |
596 CacheAddr tail = static_cast<CacheAddr>(header_->user[kTailIndex]); | 601 return Addr(control_data_->tails[NO_USE]); |
597 return Addr(tail); | |
598 } | 602 } |
599 | 603 |
600 void Rankings::WriteHead() { | 604 void Rankings::WriteHead() { |
601 header_->user[kHeadIndex] = static_cast<int32>(head_.value()); | 605 control_data_->heads[NO_USE] = head_.value(); |
602 } | 606 } |
603 | 607 |
604 void Rankings::WriteTail() { | 608 void Rankings::WriteTail() { |
605 header_->user[kTailIndex] = static_cast<int32>(tail_.value()); | 609 control_data_->tails[NO_USE] = tail_.value(); |
606 } | 610 } |
607 | 611 |
608 bool Rankings::CheckEntry(CacheRankingsBlock* rankings) { | 612 bool Rankings::CheckEntry(CacheRankingsBlock* rankings) { |
609 if (!rankings->Data()->pointer) | 613 if (!rankings->Data()->pointer) |
610 return true; | 614 return true; |
611 | 615 |
612 // If this entry is not dirty, it is a serious problem. | 616 // If this entry is not dirty, it is a serious problem. |
613 return backend_->GetCurrentEntryId() != rankings->Data()->dirty; | 617 return backend_->GetCurrentEntryId() != rankings->Data()->dirty; |
614 } | 618 } |
615 | 619 |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
671 ++it) { | 675 ++it) { |
672 if (it->first == address) { | 676 if (it->first == address) { |
673 CacheRankingsBlock* other = it->second; | 677 CacheRankingsBlock* other = it->second; |
674 other->Data()->next = node->Data()->next; | 678 other->Data()->next = node->Data()->next; |
675 other->Data()->prev = node->Data()->prev; | 679 other->Data()->prev = node->Data()->prev; |
676 } | 680 } |
677 } | 681 } |
678 } | 682 } |
679 | 683 |
680 } // namespace disk_cache | 684 } // namespace disk_cache |
OLD | NEW |