• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "net/disk_cache/rankings.h"
6 
7 #include "base/metrics/histogram.h"
8 #include "net/disk_cache/backend_impl.h"
9 #include "net/disk_cache/entry_impl.h"
10 #include "net/disk_cache/errors.h"
11 #include "net/disk_cache/histogram_macros.h"
12 
13 using base::Time;
14 using base::TimeTicks;
15 
16 // This is used by crash_cache.exe to generate unit test files.
17 disk_cache::RankCrashes g_rankings_crash = disk_cache::NO_CRASH;
18 
19 namespace {
20 
21 enum Operation {
22   INSERT = 1,
23   REMOVE
24 };
25 
26 // This class provides a simple lock for the LRU list of rankings. Whenever an
27 // entry is to be inserted or removed from the list, a transaction object should
28 // be created to keep track of the operation. If the process crashes before
29 // finishing the operation, the transaction record (stored as part of the user
30 // data on the file header) can be used to finish the operation.
31 class Transaction {
32  public:
33   // addr is the cache addres of the node being inserted or removed. We want to
34   // avoid having the compiler doing optimizations on when to read or write
35   // from user_data because it is the basis of the crash detection. Maybe
36   // volatile is not enough for that, but it should be a good hint.
37   Transaction(volatile disk_cache::LruData* data, disk_cache::Addr addr,
38               Operation op, int list);
39   ~Transaction();
40  private:
41   volatile disk_cache::LruData* data_;
42   DISALLOW_COPY_AND_ASSIGN(Transaction);
43 };
44 
Transaction(volatile disk_cache::LruData * data,disk_cache::Addr addr,Operation op,int list)45 Transaction::Transaction(volatile disk_cache::LruData* data,
46                          disk_cache::Addr addr, Operation op, int list)
47     : data_(data) {
48   DCHECK(!data_->transaction);
49   DCHECK(addr.is_initialized());
50   data_->operation = op;
51   data_->operation_list = list;
52   data_->transaction = addr.value();
53 }
54 
~Transaction()55 Transaction::~Transaction() {
56   DCHECK(data_->transaction);
57   data_->transaction = 0;
58   data_->operation = 0;
59   data_->operation_list = 0;
60 }
61 
62 // Code locations that can generate crashes.
63 enum CrashLocation {
64   ON_INSERT_1, ON_INSERT_2, ON_INSERT_3, ON_INSERT_4, ON_REMOVE_1, ON_REMOVE_2,
65   ON_REMOVE_3, ON_REMOVE_4, ON_REMOVE_5, ON_REMOVE_6, ON_REMOVE_7, ON_REMOVE_8
66 };
67 
68 #ifndef NDEBUG
TerminateSelf()69 void TerminateSelf() {
70 #if defined(OS_WIN)
71   // Windows does more work on _exit() than we would like, so we force exit.
72   TerminateProcess(GetCurrentProcess(), 0);
73 #elif defined(OS_POSIX)
74 #if defined(ANDROID)
75   abort();
76 #else
77   // On POSIX, _exit() will terminate the process with minimal cleanup,
78   // and it is cleaner than killing.
79   _exit(0);
80 #endif
81 #endif
82 }
83 #endif  // NDEBUG
84 
85 // Generates a crash on debug builds, acording to the value of g_rankings_crash.
86 // This used by crash_cache.exe to generate unit-test files.
GenerateCrash(CrashLocation location)87 void GenerateCrash(CrashLocation location) {
88 #ifndef NDEBUG
89   if (disk_cache::NO_CRASH == g_rankings_crash)
90     return;
91   switch (location) {
92     case ON_INSERT_1:
93       switch (g_rankings_crash) {
94         case disk_cache::INSERT_ONE_1:
95         case disk_cache::INSERT_LOAD_1:
96           TerminateSelf();
97         default:
98           break;
99       }
100       break;
101     case ON_INSERT_2:
102       if (disk_cache::INSERT_EMPTY_1 == g_rankings_crash)
103         TerminateSelf();
104       break;
105     case ON_INSERT_3:
106       switch (g_rankings_crash) {
107         case disk_cache::INSERT_EMPTY_2:
108         case disk_cache::INSERT_ONE_2:
109         case disk_cache::INSERT_LOAD_2:
110           TerminateSelf();
111         default:
112           break;
113       }
114       break;
115     case ON_INSERT_4:
116       switch (g_rankings_crash) {
117         case disk_cache::INSERT_EMPTY_3:
118         case disk_cache::INSERT_ONE_3:
119           TerminateSelf();
120         default:
121           break;
122       }
123       break;
124     case ON_REMOVE_1:
125       switch (g_rankings_crash) {
126         case disk_cache::REMOVE_ONE_1:
127         case disk_cache::REMOVE_HEAD_1:
128         case disk_cache::REMOVE_TAIL_1:
129         case disk_cache::REMOVE_LOAD_1:
130           TerminateSelf();
131         default:
132           break;
133       }
134       break;
135     case ON_REMOVE_2:
136       if (disk_cache::REMOVE_ONE_2 == g_rankings_crash)
137         TerminateSelf();
138       break;
139     case ON_REMOVE_3:
140       if (disk_cache::REMOVE_ONE_3 == g_rankings_crash)
141         TerminateSelf();
142       break;
143     case ON_REMOVE_4:
144       if (disk_cache::REMOVE_HEAD_2 == g_rankings_crash)
145         TerminateSelf();
146       break;
147     case ON_REMOVE_5:
148       if (disk_cache::REMOVE_TAIL_2 == g_rankings_crash)
149         TerminateSelf();
150       break;
151     case ON_REMOVE_6:
152       if (disk_cache::REMOVE_TAIL_3 == g_rankings_crash)
153         TerminateSelf();
154       break;
155     case ON_REMOVE_7:
156       switch (g_rankings_crash) {
157         case disk_cache::REMOVE_ONE_4:
158         case disk_cache::REMOVE_LOAD_2:
159         case disk_cache::REMOVE_HEAD_3:
160           TerminateSelf();
161         default:
162           break;
163       }
164       break;
165     case ON_REMOVE_8:
166       switch (g_rankings_crash) {
167         case disk_cache::REMOVE_HEAD_4:
168         case disk_cache::REMOVE_LOAD_3:
169           TerminateSelf();
170         default:
171           break;
172       }
173       break;
174     default:
175       NOTREACHED();
176       return;
177   }
178 #endif  // NDEBUG
179 }
180 
181 // Update the timestamp fields of |node|.
UpdateTimes(disk_cache::CacheRankingsBlock * node,bool modified)182 void UpdateTimes(disk_cache::CacheRankingsBlock* node, bool modified) {
183   base::Time now = base::Time::Now();
184   node->Data()->last_used = now.ToInternalValue();
185   if (modified)
186     node->Data()->last_modified = now.ToInternalValue();
187 }
188 
189 }  // namespace
190 
191 namespace disk_cache {
192 
Iterator(Rankings * rankings)193 Rankings::Iterator::Iterator(Rankings* rankings) {
194   memset(this, 0, sizeof(Iterator));
195   my_rankings = rankings;
196 }
197 
~Iterator()198 Rankings::Iterator::~Iterator() {
199   for (int i = 0; i < 3; i++)
200     ScopedRankingsBlock(my_rankings, nodes[i]);
201 }
202 
Rankings()203 Rankings::Rankings() : init_(false) {}
204 
~Rankings()205 Rankings::~Rankings() {}
206 
Init(BackendImpl * backend,bool count_lists)207 bool Rankings::Init(BackendImpl* backend, bool count_lists) {
208   DCHECK(!init_);
209   if (init_)
210     return false;
211 
212   backend_ = backend;
213   control_data_ = backend_->GetLruData();
214   count_lists_ = count_lists;
215 
216   ReadHeads();
217   ReadTails();
218 
219   if (control_data_->transaction)
220     CompleteTransaction();
221 
222   init_ = true;
223   return true;
224 }
225 
Reset()226 void Rankings::Reset() {
227   init_ = false;
228   for (int i = 0; i < LAST_ELEMENT; i++) {
229     heads_[i].set_value(0);
230     tails_[i].set_value(0);
231   }
232   control_data_ = NULL;
233 }
234 
Insert(CacheRankingsBlock * node,bool modified,List list)235 void Rankings::Insert(CacheRankingsBlock* node, bool modified, List list) {
236   Trace("Insert 0x%x l %d", node->address().value(), list);
237   DCHECK(node->HasData());
238   Addr& my_head = heads_[list];
239   Addr& my_tail = tails_[list];
240   Transaction lock(control_data_, node->address(), INSERT, list);
241   CacheRankingsBlock head(backend_->File(my_head), my_head);
242   if (my_head.is_initialized()) {
243     if (!GetRanking(&head))
244       return;
245 
246     if (head.Data()->prev != my_head.value() &&  // Normal path.
247         head.Data()->prev != node->address().value()) {  // FinishInsert().
248       backend_->CriticalError(ERR_INVALID_LINKS);
249       return;
250     }
251 
252     head.Data()->prev = node->address().value();
253     head.Store();
254     GenerateCrash(ON_INSERT_1);
255     UpdateIterators(&head);
256   }
257 
258   node->Data()->next = my_head.value();
259   node->Data()->prev = node->address().value();
260   my_head.set_value(node->address().value());
261 
262   if (!my_tail.is_initialized() || my_tail.value() == node->address().value()) {
263     my_tail.set_value(node->address().value());
264     node->Data()->next = my_tail.value();
265     WriteTail(list);
266     GenerateCrash(ON_INSERT_2);
267   }
268 
269   UpdateTimes(node, modified);
270   node->Store();
271   GenerateCrash(ON_INSERT_3);
272 
273   // The last thing to do is move our head to point to a node already stored.
274   WriteHead(list);
275   IncrementCounter(list);
276   GenerateCrash(ON_INSERT_4);
277 }
278 
279 // If a, b and r are elements on the list, and we want to remove r, the possible
280 // states for the objects if a crash happens are (where y(x, z) means for object
281 // y, prev is x and next is z):
282 // A. One element:
283 //    1. r(r, r), head(r), tail(r)                    initial state
284 //    2. r(r, r), head(0), tail(r)                    WriteHead()
285 //    3. r(r, r), head(0), tail(0)                    WriteTail()
286 //    4. r(0, 0), head(0), tail(0)                    next.Store()
287 //
288 // B. Remove a random element:
289 //    1. a(x, r), r(a, b), b(r, y), head(x), tail(y)  initial state
290 //    2. a(x, r), r(a, b), b(a, y), head(x), tail(y)  next.Store()
291 //    3. a(x, b), r(a, b), b(a, y), head(x), tail(y)  prev.Store()
292 //    4. a(x, b), r(0, 0), b(a, y), head(x), tail(y)  node.Store()
293 //
294 // C. Remove head:
295 //    1. r(r, b), b(r, y), head(r), tail(y)           initial state
296 //    2. r(r, b), b(r, y), head(b), tail(y)           WriteHead()
297 //    3. r(r, b), b(b, y), head(b), tail(y)           next.Store()
298 //    4. r(0, 0), b(b, y), head(b), tail(y)           prev.Store()
299 //
300 // D. Remove tail:
301 //    1. a(x, r), r(a, r), head(x), tail(r)           initial state
302 //    2. a(x, r), r(a, r), head(x), tail(a)           WriteTail()
303 //    3. a(x, a), r(a, r), head(x), tail(a)           prev.Store()
304 //    4. a(x, a), r(0, 0), head(x), tail(a)           next.Store()
Remove(CacheRankingsBlock * node,List list,bool strict)305 void Rankings::Remove(CacheRankingsBlock* node, List list, bool strict) {
306   Trace("Remove 0x%x (0x%x 0x%x) l %d", node->address().value(),
307         node->Data()->next, node->Data()->prev, list);
308   DCHECK(node->HasData());
309   if (strict)
310   InvalidateIterators(node);
311 
312   Addr next_addr(node->Data()->next);
313   Addr prev_addr(node->Data()->prev);
314   if (!next_addr.is_initialized() || next_addr.is_separate_file() ||
315       !prev_addr.is_initialized() || prev_addr.is_separate_file()) {
316     if (next_addr.is_initialized() || prev_addr.is_initialized()) {
317       LOG(ERROR) << "Invalid rankings info.";
318     }
319     return;
320   }
321 
322   CacheRankingsBlock next(backend_->File(next_addr), next_addr);
323   CacheRankingsBlock prev(backend_->File(prev_addr), prev_addr);
324   if (!GetRanking(&next) || !GetRanking(&prev))
325     return;
326 
327   if (!CheckLinks(node, &prev, &next, &list))
328     return;
329 
330   Transaction lock(control_data_, node->address(), REMOVE, list);
331   prev.Data()->next = next.address().value();
332   next.Data()->prev = prev.address().value();
333   GenerateCrash(ON_REMOVE_1);
334 
335   CacheAddr node_value = node->address().value();
336   Addr& my_head = heads_[list];
337   Addr& my_tail = tails_[list];
338   if (node_value == my_head.value() || node_value == my_tail.value()) {
339     if (my_head.value() == my_tail.value()) {
340       my_head.set_value(0);
341       my_tail.set_value(0);
342 
343       WriteHead(list);
344       GenerateCrash(ON_REMOVE_2);
345       WriteTail(list);
346       GenerateCrash(ON_REMOVE_3);
347     } else if (node_value == my_head.value()) {
348       my_head.set_value(next.address().value());
349       next.Data()->prev = next.address().value();
350 
351       WriteHead(list);
352       GenerateCrash(ON_REMOVE_4);
353     } else if (node_value == my_tail.value()) {
354       my_tail.set_value(prev.address().value());
355       prev.Data()->next = prev.address().value();
356 
357       WriteTail(list);
358       GenerateCrash(ON_REMOVE_5);
359 
360       // Store the new tail to make sure we can undo the operation if we crash.
361       prev.Store();
362       GenerateCrash(ON_REMOVE_6);
363     }
364   }
365 
366   // Nodes out of the list can be identified by invalid pointers.
367   node->Data()->next = 0;
368   node->Data()->prev = 0;
369 
370   // The last thing to get to disk is the node itself, so before that there is
371   // enough info to recover.
372   next.Store();
373   GenerateCrash(ON_REMOVE_7);
374   prev.Store();
375   GenerateCrash(ON_REMOVE_8);
376   node->Store();
377   DecrementCounter(list);
378   UpdateIterators(&next);
379   UpdateIterators(&prev);
380 }
381 
382 // A crash in between Remove and Insert will lead to a dirty entry not on the
383 // list. We want to avoid that case as much as we can (as while waiting for IO),
384 // but the net effect is just an assert on debug when attempting to remove the
385 // entry. Otherwise we'll need reentrant transactions, which is an overkill.
UpdateRank(CacheRankingsBlock * node,bool modified,List list)386 void Rankings::UpdateRank(CacheRankingsBlock* node, bool modified, List list) {
387   Addr& my_head = heads_[list];
388   if (my_head.value() == node->address().value()) {
389     UpdateTimes(node, modified);
390     node->set_modified();
391     return;
392   }
393 
394   TimeTicks start = TimeTicks::Now();
395   Remove(node, list, true);
396   Insert(node, modified, list);
397   CACHE_UMA(AGE_MS, "UpdateRank", 0, start);
398 }
399 
GetNext(CacheRankingsBlock * node,List list)400 CacheRankingsBlock* Rankings::GetNext(CacheRankingsBlock* node, List list) {
401   ScopedRankingsBlock next(this);
402   if (!node) {
403     Addr& my_head = heads_[list];
404     if (!my_head.is_initialized())
405       return NULL;
406     next.reset(new CacheRankingsBlock(backend_->File(my_head), my_head));
407   } else {
408     if (!node->HasData())
409       node->Load();
410     Addr& my_tail = tails_[list];
411     if (!my_tail.is_initialized())
412       return NULL;
413     if (my_tail.value() == node->address().value())
414       return NULL;
415     Addr address(node->Data()->next);
416     if (address.value() == node->address().value())
417       return NULL;  // Another tail? fail it.
418     next.reset(new CacheRankingsBlock(backend_->File(address), address));
419   }
420 
421   TrackRankingsBlock(next.get(), true);
422 
423   if (!GetRanking(next.get()))
424     return NULL;
425 
426   ConvertToLongLived(next.get());
427   if (node && !CheckSingleLink(node, next.get()))
428     return NULL;
429 
430   return next.release();
431 }
432 
GetPrev(CacheRankingsBlock * node,List list)433 CacheRankingsBlock* Rankings::GetPrev(CacheRankingsBlock* node, List list) {
434   ScopedRankingsBlock prev(this);
435   if (!node) {
436     Addr& my_tail = tails_[list];
437     if (!my_tail.is_initialized())
438       return NULL;
439     prev.reset(new CacheRankingsBlock(backend_->File(my_tail), my_tail));
440   } else {
441     if (!node->HasData())
442       node->Load();
443     Addr& my_head = heads_[list];
444     if (!my_head.is_initialized())
445       return NULL;
446     if (my_head.value() == node->address().value())
447       return NULL;
448     Addr address(node->Data()->prev);
449     if (address.value() == node->address().value())
450       return NULL;  // Another head? fail it.
451     prev.reset(new CacheRankingsBlock(backend_->File(address), address));
452   }
453 
454   TrackRankingsBlock(prev.get(), true);
455 
456   if (!GetRanking(prev.get()))
457     return NULL;
458 
459   ConvertToLongLived(prev.get());
460   if (node && !CheckSingleLink(prev.get(), node))
461     return NULL;
462 
463   return prev.release();
464 }
465 
FreeRankingsBlock(CacheRankingsBlock * node)466 void Rankings::FreeRankingsBlock(CacheRankingsBlock* node) {
467   TrackRankingsBlock(node, false);
468 }
469 
TrackRankingsBlock(CacheRankingsBlock * node,bool start_tracking)470 void Rankings::TrackRankingsBlock(CacheRankingsBlock* node,
471                                   bool start_tracking) {
472   if (!node)
473     return;
474 
475   IteratorPair current(node->address().value(), node);
476 
477   if (start_tracking)
478     iterators_.push_back(current);
479   else
480     iterators_.remove(current);
481 }
482 
SelfCheck()483 int Rankings::SelfCheck() {
484   int total = 0;
485   for (int i = 0; i < LAST_ELEMENT; i++) {
486     int partial = CheckList(static_cast<List>(i));
487     if (partial < 0)
488       return partial;
489     total += partial;
490   }
491   return total;
492 }
493 
SanityCheck(CacheRankingsBlock * node,bool from_list) const494 bool Rankings::SanityCheck(CacheRankingsBlock* node, bool from_list) const {
495   const RankingsNode* data = node->Data();
496 
497   if ((!data->next && data->prev) || (data->next && !data->prev))
498     return false;
499 
500   // Both pointers on zero is a node out of the list.
501   if (!data->next && !data->prev && from_list)
502     return false;
503 
504   List list = NO_USE;  // Initialize it to something.
505   if ((node->address().value() == data->prev) && !IsHead(data->prev, &list))
506     return false;
507 
508   if ((node->address().value() == data->next) && !IsTail(data->next, &list))
509     return false;
510 
511   if (!data->next && !data->prev)
512     return true;
513 
514   Addr next_addr(data->next);
515   Addr prev_addr(data->prev);
516   if (!next_addr.SanityCheck() || next_addr.file_type() != RANKINGS ||
517       !prev_addr.SanityCheck() || prev_addr.file_type() != RANKINGS)
518     return false;
519 
520   return true;
521 }
522 
DataSanityCheck(CacheRankingsBlock * node,bool from_list) const523 bool Rankings::DataSanityCheck(CacheRankingsBlock* node, bool from_list) const {
524   const RankingsNode* data = node->Data();
525   if (!data->contents)
526     return false;
527 
528   // It may have never been inserted.
529   if (from_list && (!data->last_used || !data->last_modified))
530     return false;
531 
532   return true;
533 }
534 
SetContents(CacheRankingsBlock * node,CacheAddr address)535 void Rankings::SetContents(CacheRankingsBlock* node, CacheAddr address) {
536   node->Data()->contents = address;
537   node->Store();
538 }
539 
ReadHeads()540 void Rankings::ReadHeads() {
541   for (int i = 0; i < LAST_ELEMENT; i++)
542     heads_[i] = Addr(control_data_->heads[i]);
543 }
544 
ReadTails()545 void Rankings::ReadTails() {
546   for (int i = 0; i < LAST_ELEMENT; i++)
547     tails_[i] = Addr(control_data_->tails[i]);
548 }
549 
WriteHead(List list)550 void Rankings::WriteHead(List list) {
551   control_data_->heads[list] = heads_[list].value();
552 }
553 
WriteTail(List list)554 void Rankings::WriteTail(List list) {
555   control_data_->tails[list] = tails_[list].value();
556 }
557 
GetRanking(CacheRankingsBlock * rankings)558 bool Rankings::GetRanking(CacheRankingsBlock* rankings) {
559   if (!rankings->address().is_initialized())
560     return false;
561 
562   TimeTicks start = TimeTicks::Now();
563   if (!rankings->Load())
564     return false;
565 
566   if (!SanityCheck(rankings, true)) {
567     backend_->CriticalError(ERR_INVALID_LINKS);
568     return false;
569   }
570 
571   backend_->OnEvent(Stats::OPEN_RANKINGS);
572 
573   // "dummy" is the old "pointer" value, so it has to be 0.
574   if (!rankings->Data()->dirty && !rankings->Data()->dummy)
575     return true;
576 
577   EntryImpl* entry = backend_->GetOpenEntry(rankings);
578   if (!entry) {
579     // We cannot trust this entry, but we cannot initiate a cleanup from this
580     // point (we may be in the middle of a cleanup already). Just get rid of
581     // the invalid pointer and continue; the entry will be deleted when detected
582     // from a regular open/create path.
583     rankings->Data()->dummy = 0;
584     rankings->Data()->dirty = backend_->GetCurrentEntryId() - 1;
585     if (!rankings->Data()->dirty)
586       rankings->Data()->dirty--;
587     return true;
588   }
589 
590   // Note that we should not leave this module without deleting rankings first.
591   rankings->SetData(entry->rankings()->Data());
592 
593   CACHE_UMA(AGE_MS, "GetRankings", 0, start);
594   return true;
595 }
596 
ConvertToLongLived(CacheRankingsBlock * rankings)597 void Rankings::ConvertToLongLived(CacheRankingsBlock* rankings) {
598   if (rankings->own_data())
599     return;
600 
601   // We cannot return a shared node because we are not keeping a reference
602   // to the entry that owns the buffer. Make this node a copy of the one that
603   // we have, and let the iterator logic update it when the entry changes.
604   CacheRankingsBlock temp(NULL, Addr(0));
605   *temp.Data() = *rankings->Data();
606   rankings->StopSharingData();
607   *rankings->Data() = *temp.Data();
608 }
609 
CompleteTransaction()610 void Rankings::CompleteTransaction() {
611   Addr node_addr(static_cast<CacheAddr>(control_data_->transaction));
612   if (!node_addr.is_initialized() || node_addr.is_separate_file()) {
613     NOTREACHED();
614     LOG(ERROR) << "Invalid rankings info.";
615     return;
616   }
617 
618   Trace("CompleteTransaction 0x%x", node_addr.value());
619 
620   CacheRankingsBlock node(backend_->File(node_addr), node_addr);
621   if (!node.Load())
622     return;
623 
624   node.Data()->dummy = 0;
625   node.Store();
626 
627   Addr& my_head = heads_[control_data_->operation_list];
628   Addr& my_tail = tails_[control_data_->operation_list];
629 
630   // We want to leave the node inside the list. The entry must me marked as
631   // dirty, and will be removed later. Otherwise, we'll get assertions when
632   // attempting to remove the dirty entry.
633   if (INSERT == control_data_->operation) {
634     Trace("FinishInsert h:0x%x t:0x%x", my_head.value(), my_tail.value());
635     FinishInsert(&node);
636   } else if (REMOVE == control_data_->operation) {
637     Trace("RevertRemove h:0x%x t:0x%x", my_head.value(), my_tail.value());
638     RevertRemove(&node);
639   } else {
640     NOTREACHED();
641     LOG(ERROR) << "Invalid operation to recover.";
642   }
643 }
644 
FinishInsert(CacheRankingsBlock * node)645 void Rankings::FinishInsert(CacheRankingsBlock* node) {
646   control_data_->transaction = 0;
647   control_data_->operation = 0;
648   Addr& my_head = heads_[control_data_->operation_list];
649   Addr& my_tail = tails_[control_data_->operation_list];
650   if (my_head.value() != node->address().value()) {
651     if (my_tail.value() == node->address().value()) {
652       // This part will be skipped by the logic of Insert.
653       node->Data()->next = my_tail.value();
654     }
655 
656     Insert(node, true, static_cast<List>(control_data_->operation_list));
657   }
658 
659   // Tell the backend about this entry.
660   backend_->RecoveredEntry(node);
661 }
662 
RevertRemove(CacheRankingsBlock * node)663 void Rankings::RevertRemove(CacheRankingsBlock* node) {
664   Addr next_addr(node->Data()->next);
665   Addr prev_addr(node->Data()->prev);
666   if (!next_addr.is_initialized() || !prev_addr.is_initialized()) {
667     // The operation actually finished. Nothing to do.
668     control_data_->transaction = 0;
669     return;
670   }
671   if (next_addr.is_separate_file() || prev_addr.is_separate_file()) {
672     NOTREACHED();
673     LOG(WARNING) << "Invalid rankings info.";
674     control_data_->transaction = 0;
675     return;
676   }
677 
678   CacheRankingsBlock next(backend_->File(next_addr), next_addr);
679   CacheRankingsBlock prev(backend_->File(prev_addr), prev_addr);
680   if (!next.Load() || !prev.Load())
681     return;
682 
683   CacheAddr node_value = node->address().value();
684   DCHECK(prev.Data()->next == node_value ||
685          prev.Data()->next == prev_addr.value() ||
686          prev.Data()->next == next.address().value());
687   DCHECK(next.Data()->prev == node_value ||
688          next.Data()->prev == next_addr.value() ||
689          next.Data()->prev == prev.address().value());
690 
691   if (node_value != prev_addr.value())
692     prev.Data()->next = node_value;
693   if (node_value != next_addr.value())
694     next.Data()->prev = node_value;
695 
696   List my_list = static_cast<List>(control_data_->operation_list);
697   Addr& my_head = heads_[my_list];
698   Addr& my_tail = tails_[my_list];
699   if (!my_head.is_initialized() || !my_tail.is_initialized()) {
700     my_head.set_value(node_value);
701     my_tail.set_value(node_value);
702     WriteHead(my_list);
703     WriteTail(my_list);
704   } else if (my_head.value() == next.address().value()) {
705     my_head.set_value(node_value);
706     prev.Data()->next = next.address().value();
707     WriteHead(my_list);
708   } else if (my_tail.value() == prev.address().value()) {
709     my_tail.set_value(node_value);
710     next.Data()->prev = prev.address().value();
711     WriteTail(my_list);
712   }
713 
714   next.Store();
715   prev.Store();
716   control_data_->transaction = 0;
717   control_data_->operation = 0;
718 }
719 
CheckEntry(CacheRankingsBlock * rankings)720 bool Rankings::CheckEntry(CacheRankingsBlock* rankings) {
721   if (!rankings->Data()->dummy)
722     return true;
723 
724   // If this entry is not dirty, it is a serious problem.
725   return backend_->GetCurrentEntryId() != rankings->Data()->dirty;
726 }
727 
CheckLinks(CacheRankingsBlock * node,CacheRankingsBlock * prev,CacheRankingsBlock * next,List * list)728 bool Rankings::CheckLinks(CacheRankingsBlock* node, CacheRankingsBlock* prev,
729                           CacheRankingsBlock* next, List* list) {
730   CacheAddr node_addr = node->address().value();
731   if (prev->Data()->next == node_addr &&
732       next->Data()->prev == node_addr) {
733     // A regular linked node.
734     return true;
735   }
736 
737   Trace("CheckLinks 0x%x (0x%x 0x%x)", node_addr,
738         prev->Data()->next, next->Data()->prev);
739 
740   if (node_addr != prev->address().value() &&
741       node_addr != next->address().value() &&
742       prev->Data()->next == next->address().value() &&
743       next->Data()->prev == prev->address().value()) {
744     // The list is actually ok, node is wrong.
745     Trace("node 0x%x out of list %d", node_addr, list);
746     node->Data()->next = 0;
747     node->Data()->prev = 0;
748     node->Store();
749     return false;
750   }
751 
752   if (prev->Data()->next == node_addr ||
753       next->Data()->prev == node_addr) {
754     // Only one link is weird, lets double check.
755     if (prev->Data()->next != node_addr && IsHead(node_addr, list))
756       return true;
757 
758     if (next->Data()->prev != node_addr && IsTail(node_addr, list))
759       return true;
760   }
761 
762   LOG(ERROR) << "Inconsistent LRU.";
763   backend_->CriticalError(ERR_INVALID_LINKS);
764   return false;
765 }
766 
CheckSingleLink(CacheRankingsBlock * prev,CacheRankingsBlock * next)767 bool Rankings::CheckSingleLink(CacheRankingsBlock* prev,
768                                CacheRankingsBlock* next) {
769   if (prev->Data()->next != next->address().value() ||
770       next->Data()->prev != prev->address().value()) {
771     LOG(ERROR) << "Inconsistent LRU.";
772 
773     backend_->CriticalError(ERR_INVALID_LINKS);
774     return false;
775   }
776 
777   return true;
778 }
779 
CheckList(List list)780 int Rankings::CheckList(List list) {
781   Addr& my_head = heads_[list];
782   Addr& my_tail = tails_[list];
783   if (!my_head.is_initialized()) {
784     if (!my_tail.is_initialized())
785       return 0;
786     // If there is no head, having a tail is an error.
787     return ERR_INVALID_TAIL;
788   }
789   // If there is no tail, having a head is an error.
790   if (!my_tail.is_initialized())
791     return ERR_INVALID_HEAD;
792 
793   if (my_tail.is_separate_file())
794     return ERR_INVALID_TAIL;
795 
796   if (my_head.is_separate_file())
797     return ERR_INVALID_HEAD;
798 
799   int num_items = 0;
800   Addr address(my_head.value());
801   Addr prev(my_head.value());
802   scoped_ptr<CacheRankingsBlock> node;
803   do {
804     node.reset(new CacheRankingsBlock(backend_->File(address), address));
805     node->Load();
806     if (node->Data()->prev != prev.value())
807       return ERR_INVALID_PREV;
808     if (!CheckEntry(node.get()))
809       return ERR_INVALID_ENTRY;
810 
811     prev.set_value(address.value());
812     address.set_value(node->Data()->next);
813     if (!address.is_initialized() || address.is_separate_file())
814       return ERR_INVALID_NEXT;
815 
816     num_items++;
817   } while (node->address().value() != address.value());
818   return num_items;
819 }
820 
IsHead(CacheAddr addr,List * list) const821 bool Rankings::IsHead(CacheAddr addr, List* list) const {
822   for (int i = 0; i < LAST_ELEMENT; i++) {
823     if (addr == heads_[i].value()) {
824       if (*list != i)
825         Trace("Changing list %d to %d", *list, i);
826       *list = static_cast<List>(i);
827       return true;
828     }
829   }
830   return false;
831 }
832 
IsTail(CacheAddr addr,List * list) const833 bool Rankings::IsTail(CacheAddr addr, List* list) const {
834   for (int i = 0; i < LAST_ELEMENT; i++) {
835     if (addr == tails_[i].value()) {
836       if (*list != i)
837         Trace("Changing list %d to %d", *list, i);
838       *list = static_cast<List>(i);
839       return true;
840     }
841   }
842   return false;
843 }
844 
845 // We expect to have just a few iterators at any given time, maybe two or three,
846 // But we could have more than one pointing at the same mode. We walk the list
847 // of cache iterators and update all that are pointing to the given node.
UpdateIterators(CacheRankingsBlock * node)848 void Rankings::UpdateIterators(CacheRankingsBlock* node) {
849   CacheAddr address = node->address().value();
850   for (IteratorList::iterator it = iterators_.begin(); it != iterators_.end();
851        ++it) {
852     if (it->first == address && it->second->HasData()) {
853       CacheRankingsBlock* other = it->second;
854       *other->Data() = *node->Data();
855     }
856   }
857 }
858 
InvalidateIterators(CacheRankingsBlock * node)859 void Rankings::InvalidateIterators(CacheRankingsBlock* node) {
860   CacheAddr address = node->address().value();
861   for (IteratorList::iterator it = iterators_.begin(); it != iterators_.end();
862        ++it) {
863     if (it->first == address) {
864 #ifndef ANDROID
865 // Confirmed with chromium developers that this is normal, and removing from
866 // Android to close bug 3239659
867       LOG(WARNING) << "Invalidating iterator at 0x" << std::hex << address;
868 #endif
869       it->second->Discard();
870     }
871   }
872 }
873 
IncrementCounter(List list)874 void Rankings::IncrementCounter(List list) {
875   if (!count_lists_)
876     return;
877 
878   DCHECK(control_data_->sizes[list] < kint32max);
879   if (control_data_->sizes[list] < kint32max)
880     control_data_->sizes[list]++;
881 }
882 
DecrementCounter(List list)883 void Rankings::DecrementCounter(List list) {
884   if (!count_lists_)
885     return;
886 
887   DCHECK(control_data_->sizes[list] > 0);
888   if (control_data_->sizes[list] > 0)
889     control_data_->sizes[list]--;
890 }
891 
892 }  // namespace disk_cache
893