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 enum Lists { | |
20 NO_USE = 0, // List of entries that have not been reused. | |
21 LOW_USE, // List of entries with low reuse. | |
22 HIGH_USE, // List of entries with high reuse. | |
23 DELETED, // List of recently deleted or doomed entries. | |
24 LAST_ELEMENT | |
25 }; | |
26 | |
27 enum Operation { | 19 enum Operation { |
28 INSERT = 1, | 20 INSERT = 1, |
29 REMOVE | 21 REMOVE |
30 }; | 22 }; |
31 | 23 |
32 // This class provides a simple lock for the LRU list of rankings. Whenever an | 24 // This class provides a simple lock for the LRU list of rankings. Whenever an |
33 // entry is to be inserted or removed from the list, a transaction object should | 25 // entry is to be inserted or removed from the list, a transaction object should |
34 // be created to keep track of the operation. If the process crashes before | 26 // be created to keep track of the operation. If the process crashes before |
35 // finishing the operation, the transaction record (stored as part of the user | 27 // finishing the operation, the transaction record (stored as part of the user |
36 // data on the file header) can be used to finish the operation. | 28 // data on the file header) can be used to finish the operation. |
(...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
163 | 155 |
164 bool Rankings::Init(BackendImpl* backend) { | 156 bool Rankings::Init(BackendImpl* backend) { |
165 DCHECK(!init_); | 157 DCHECK(!init_); |
166 if (init_) | 158 if (init_) |
167 return false; | 159 return false; |
168 | 160 |
169 backend_ = backend; | 161 backend_ = backend; |
170 | 162 |
171 control_data_ = backend_->GetLruData(); | 163 control_data_ = backend_->GetLruData(); |
172 | 164 |
173 head_ = ReadHead(); | 165 ReadHeads(); |
174 tail_ = ReadTail(); | 166 ReadTails(); |
175 | 167 |
176 if (control_data_->transaction) | 168 if (control_data_->transaction) |
177 CompleteTransaction(); | 169 CompleteTransaction(); |
178 | 170 |
179 init_ = true; | 171 init_ = true; |
180 return true; | 172 return true; |
181 } | 173 } |
182 | 174 |
183 void Rankings::Reset() { | 175 void Rankings::Reset() { |
184 init_ = false; | 176 init_ = false; |
185 head_.set_value(0); | 177 for (int i = 0; i < LAST_ELEMENT; i++) { |
186 tail_.set_value(0); | 178 heads_[i].set_value(0); |
| 179 tails_[i].set_value(0); |
| 180 } |
187 control_data_ = NULL; | 181 control_data_ = NULL; |
188 } | 182 } |
189 | 183 |
190 bool Rankings::GetRanking(CacheRankingsBlock* rankings) { | 184 bool Rankings::GetRanking(CacheRankingsBlock* rankings) { |
191 Time start = Time::Now(); | 185 Time start = Time::Now(); |
192 if (!rankings->address().is_initialized()) | 186 if (!rankings->address().is_initialized()) |
193 return false; | 187 return false; |
194 | 188 |
195 if (!rankings->Load()) | 189 if (!rankings->Load()) |
196 return false; | 190 return false; |
(...skipping 19 matching lines...) Expand all Loading... |
216 return true; | 210 return true; |
217 } | 211 } |
218 | 212 |
219 EntryImpl* cache_entry = | 213 EntryImpl* cache_entry = |
220 reinterpret_cast<EntryImpl*>(rankings->Data()->pointer); | 214 reinterpret_cast<EntryImpl*>(rankings->Data()->pointer); |
221 rankings->SetData(cache_entry->rankings()->Data()); | 215 rankings->SetData(cache_entry->rankings()->Data()); |
222 UMA_HISTOGRAM_TIMES(L"DiskCache.GetRankings", Time::Now() - start); | 216 UMA_HISTOGRAM_TIMES(L"DiskCache.GetRankings", Time::Now() - start); |
223 return true; | 217 return true; |
224 } | 218 } |
225 | 219 |
226 void Rankings::Insert(CacheRankingsBlock* node, bool modified) { | 220 void Rankings::Insert(CacheRankingsBlock* node, bool modified, List list) { |
227 Trace("Insert 0x%x", node->address().value()); | 221 Trace("Insert 0x%x", node->address().value()); |
228 DCHECK(node->HasData()); | 222 DCHECK(node->HasData()); |
229 Transaction lock(control_data_, node->address(), INSERT, NO_USE); | 223 Addr& my_head = heads_[list]; |
230 CacheRankingsBlock head(backend_->File(head_), head_); | 224 Addr& my_tail = tails_[list]; |
231 if (head_.is_initialized()) { | 225 Transaction lock(control_data_, node->address(), INSERT, list); |
| 226 CacheRankingsBlock head(backend_->File(my_head), my_head); |
| 227 if (my_head.is_initialized()) { |
232 if (!GetRanking(&head)) | 228 if (!GetRanking(&head)) |
233 return; | 229 return; |
234 | 230 |
235 if (head.Data()->prev != head_.value() && // Normal path. | 231 if (head.Data()->prev != my_head.value() && // Normal path. |
236 head.Data()->prev != node->address().value()) { // FinishInsert(). | 232 head.Data()->prev != node->address().value()) { // FinishInsert(). |
237 backend_->CriticalError(ERR_INVALID_LINKS); | 233 backend_->CriticalError(ERR_INVALID_LINKS); |
238 return; | 234 return; |
239 } | 235 } |
240 | 236 |
241 head.Data()->prev = node->address().value(); | 237 head.Data()->prev = node->address().value(); |
242 head.Store(); | 238 head.Store(); |
243 GenerateCrash(ON_INSERT_1); | 239 GenerateCrash(ON_INSERT_1); |
244 UpdateIterators(&head); | 240 UpdateIterators(&head); |
245 } | 241 } |
246 | 242 |
247 node->Data()->next = head_.value(); | 243 node->Data()->next = my_head.value(); |
248 node->Data()->prev = node->address().value(); | 244 node->Data()->prev = node->address().value(); |
249 head_.set_value(node->address().value()); | 245 my_head.set_value(node->address().value()); |
250 | 246 |
251 if (!tail_.is_initialized() || tail_.value() == node->address().value()) { | 247 if (!my_tail.is_initialized() || my_tail.value() == node->address().value()) { |
252 tail_.set_value(node->address().value()); | 248 my_tail.set_value(node->address().value()); |
253 node->Data()->next = tail_.value(); | 249 node->Data()->next = my_tail.value(); |
254 WriteTail(); | 250 WriteTail(list); |
255 GenerateCrash(ON_INSERT_2); | 251 GenerateCrash(ON_INSERT_2); |
256 } | 252 } |
257 | 253 |
258 Time now = Time::Now(); | 254 Time now = Time::Now(); |
259 node->Data()->last_used = now.ToInternalValue(); | 255 node->Data()->last_used = now.ToInternalValue(); |
260 if (modified) | 256 if (modified) |
261 node->Data()->last_modified = now.ToInternalValue(); | 257 node->Data()->last_modified = now.ToInternalValue(); |
262 node->Store(); | 258 node->Store(); |
263 GenerateCrash(ON_INSERT_3); | 259 GenerateCrash(ON_INSERT_3); |
264 | 260 |
265 // The last thing to do is move our head to point to a node already stored. | 261 // The last thing to do is move our head to point to a node already stored. |
266 WriteHead(); | 262 WriteHead(list); |
267 GenerateCrash(ON_INSERT_4); | 263 GenerateCrash(ON_INSERT_4); |
268 } | 264 } |
269 | 265 |
270 // If a, b and r are elements on the list, and we want to remove r, the possible | 266 // If a, b and r are elements on the list, and we want to remove r, the possible |
271 // states for the objects if a crash happens are (where y(x, z) means for object | 267 // states for the objects if a crash happens are (where y(x, z) means for object |
272 // y, prev is x and next is z): | 268 // y, prev is x and next is z): |
273 // A. One element: | 269 // A. One element: |
274 // 1. r(r, r), head(r), tail(r) initial state | 270 // 1. r(r, r), head(r), tail(r) initial state |
275 // 2. r(r, r), head(0), tail(r) WriteHead() | 271 // 2. r(r, r), head(0), tail(r) WriteHead() |
276 // 3. r(r, r), head(0), tail(0) WriteTail() | 272 // 3. r(r, r), head(0), tail(0) WriteTail() |
277 // 4. r(0, 0), head(0), tail(0) next.Store() | 273 // 4. r(0, 0), head(0), tail(0) next.Store() |
278 // | 274 // |
279 // B. Remove a random element: | 275 // B. Remove a random element: |
280 // 1. a(x, r), r(a, b), b(r, y), head(x), tail(y) initial state | 276 // 1. a(x, r), r(a, b), b(r, y), head(x), tail(y) initial state |
281 // 2. a(x, r), r(a, b), b(a, y), head(x), tail(y) next.Store() | 277 // 2. a(x, r), r(a, b), b(a, y), head(x), tail(y) next.Store() |
282 // 3. a(x, b), r(a, b), b(a, y), head(x), tail(y) prev.Store() | 278 // 3. a(x, b), r(a, b), b(a, y), head(x), tail(y) prev.Store() |
283 // 4. a(x, b), r(0, 0), b(a, y), head(x), tail(y) node.Store() | 279 // 4. a(x, b), r(0, 0), b(a, y), head(x), tail(y) node.Store() |
284 // | 280 // |
285 // C. Remove head: | 281 // C. Remove head: |
286 // 1. r(r, b), b(r, y), head(r), tail(y) initial state | 282 // 1. r(r, b), b(r, y), head(r), tail(y) initial state |
287 // 2. r(r, b), b(r, y), head(b), tail(y) WriteHead() | 283 // 2. r(r, b), b(r, y), head(b), tail(y) WriteHead() |
288 // 3. r(r, b), b(b, y), head(b), tail(y) next.Store() | 284 // 3. r(r, b), b(b, y), head(b), tail(y) next.Store() |
289 // 4. r(0, 0), b(b, y), head(b), tail(y) prev.Store() | 285 // 4. r(0, 0), b(b, y), head(b), tail(y) prev.Store() |
290 // | 286 // |
291 // D. Remove tail: | 287 // D. Remove tail: |
292 // 1. a(x, r), r(a, r), head(x), tail(r) initial state | 288 // 1. a(x, r), r(a, r), head(x), tail(r) initial state |
293 // 2. a(x, r), r(a, r), head(x), tail(a) WriteTail() | 289 // 2. a(x, r), r(a, r), head(x), tail(a) WriteTail() |
294 // 3. a(x, a), r(a, r), head(x), tail(a) prev.Store() | 290 // 3. a(x, a), r(a, r), head(x), tail(a) prev.Store() |
295 // 4. a(x, a), r(0, 0), head(x), tail(a) next.Store() | 291 // 4. a(x, a), r(0, 0), head(x), tail(a) next.Store() |
296 void Rankings::Remove(CacheRankingsBlock* node) { | 292 void Rankings::Remove(CacheRankingsBlock* node, List list) { |
297 Trace("Remove 0x%x (0x%x 0x%x)", node->address().value(), node->Data()->next, | 293 Trace("Remove 0x%x (0x%x 0x%x)", node->address().value(), node->Data()->next, |
298 node->Data()->prev); | 294 node->Data()->prev); |
299 DCHECK(node->HasData()); | 295 DCHECK(node->HasData()); |
300 Addr next_addr(node->Data()->next); | 296 Addr next_addr(node->Data()->next); |
301 Addr prev_addr(node->Data()->prev); | 297 Addr prev_addr(node->Data()->prev); |
302 if (!next_addr.is_initialized() || next_addr.is_separate_file() || | 298 if (!next_addr.is_initialized() || next_addr.is_separate_file() || |
303 !prev_addr.is_initialized() || prev_addr.is_separate_file()) { | 299 !prev_addr.is_initialized() || prev_addr.is_separate_file()) { |
304 LOG(WARNING) << "Invalid rankings info."; | 300 LOG(WARNING) << "Invalid rankings info."; |
305 return; | 301 return; |
306 } | 302 } |
307 | 303 |
308 CacheRankingsBlock next(backend_->File(next_addr), next_addr); | 304 CacheRankingsBlock next(backend_->File(next_addr), next_addr); |
309 CacheRankingsBlock prev(backend_->File(prev_addr), prev_addr); | 305 CacheRankingsBlock prev(backend_->File(prev_addr), prev_addr); |
310 if (!GetRanking(&next) || !GetRanking(&prev)) | 306 if (!GetRanking(&next) || !GetRanking(&prev)) |
311 return; | 307 return; |
312 | 308 |
313 if (!CheckLinks(node, &prev, &next)) | 309 if (!CheckLinks(node, &prev, &next, list)) |
314 return; | 310 return; |
315 | 311 |
316 Transaction lock(control_data_, node->address(), REMOVE, NO_USE); | 312 Transaction lock(control_data_, node->address(), REMOVE, list); |
317 prev.Data()->next = next.address().value(); | 313 prev.Data()->next = next.address().value(); |
318 next.Data()->prev = prev.address().value(); | 314 next.Data()->prev = prev.address().value(); |
319 GenerateCrash(ON_REMOVE_1); | 315 GenerateCrash(ON_REMOVE_1); |
320 | 316 |
321 CacheAddr node_value = node->address().value(); | 317 CacheAddr node_value = node->address().value(); |
322 if (node_value == head_.value() || node_value == tail_.value()) { | 318 Addr& my_head = heads_[list]; |
323 if (head_.value() == tail_.value()) { | 319 Addr& my_tail = tails_[list]; |
324 head_.set_value(0); | 320 if (node_value == my_head.value() || node_value == my_tail.value()) { |
325 tail_.set_value(0); | 321 if (my_head.value() == my_tail.value()) { |
| 322 my_head.set_value(0); |
| 323 my_tail.set_value(0); |
326 | 324 |
327 WriteHead(); | 325 WriteHead(list); |
328 GenerateCrash(ON_REMOVE_2); | 326 GenerateCrash(ON_REMOVE_2); |
329 WriteTail(); | 327 WriteTail(list); |
330 GenerateCrash(ON_REMOVE_3); | 328 GenerateCrash(ON_REMOVE_3); |
331 } else if (node_value == head_.value()) { | 329 } else if (node_value == my_head.value()) { |
332 head_.set_value(next.address().value()); | 330 my_head.set_value(next.address().value()); |
333 next.Data()->prev = next.address().value(); | 331 next.Data()->prev = next.address().value(); |
334 | 332 |
335 WriteHead(); | 333 WriteHead(list); |
336 GenerateCrash(ON_REMOVE_4); | 334 GenerateCrash(ON_REMOVE_4); |
337 } else if (node_value == tail_.value()) { | 335 } else if (node_value == my_tail.value()) { |
338 tail_.set_value(prev.address().value()); | 336 my_tail.set_value(prev.address().value()); |
339 prev.Data()->next = prev.address().value(); | 337 prev.Data()->next = prev.address().value(); |
340 | 338 |
341 WriteTail(); | 339 WriteTail(list); |
342 GenerateCrash(ON_REMOVE_5); | 340 GenerateCrash(ON_REMOVE_5); |
343 | 341 |
344 // Store the new tail to make sure we can undo the operation if we crash. | 342 // Store the new tail to make sure we can undo the operation if we crash. |
345 prev.Store(); | 343 prev.Store(); |
346 GenerateCrash(ON_REMOVE_6); | 344 GenerateCrash(ON_REMOVE_6); |
347 } | 345 } |
348 } | 346 } |
349 | 347 |
350 // Nodes out of the list can be identified by invalid pointers. | 348 // Nodes out of the list can be identified by invalid pointers. |
351 node->Data()->next = 0; | 349 node->Data()->next = 0; |
352 node->Data()->prev = 0; | 350 node->Data()->prev = 0; |
353 | 351 |
354 // The last thing to get to disk is the node itself, so before that there is | 352 // The last thing to get to disk is the node itself, so before that there is |
355 // enough info to recover. | 353 // enough info to recover. |
356 next.Store(); | 354 next.Store(); |
357 GenerateCrash(ON_REMOVE_7); | 355 GenerateCrash(ON_REMOVE_7); |
358 prev.Store(); | 356 prev.Store(); |
359 GenerateCrash(ON_REMOVE_8); | 357 GenerateCrash(ON_REMOVE_8); |
360 node->Store(); | 358 node->Store(); |
361 UpdateIterators(&next); | 359 UpdateIterators(&next); |
362 UpdateIterators(&prev); | 360 UpdateIterators(&prev); |
363 } | 361 } |
364 | 362 |
365 // A crash in between Remove and Insert will lead to a dirty entry not on the | 363 // A crash in between Remove and Insert will lead to a dirty entry not on the |
366 // list. We want to avoid that case as much as we can (as while waiting for IO), | 364 // list. We want to avoid that case as much as we can (as while waiting for IO), |
367 // but the net effect is just an assert on debug when attempting to remove the | 365 // but the net effect is just an assert on debug when attempting to remove the |
368 // entry. Otherwise we'll need reentrant transactions, which is an overkill. | 366 // entry. Otherwise we'll need reentrant transactions, which is an overkill. |
369 void Rankings::UpdateRank(CacheRankingsBlock* node, bool modified) { | 367 void Rankings::UpdateRank(CacheRankingsBlock* node, bool modified, List list) { |
370 Time start = Time::Now(); | 368 Time start = Time::Now(); |
371 Remove(node); | 369 Remove(node, list); |
372 Insert(node, modified); | 370 Insert(node, modified, list); |
373 UMA_HISTOGRAM_TIMES(L"DiskCache.UpdateRank", Time::Now() - start); | 371 UMA_HISTOGRAM_TIMES(L"DiskCache.UpdateRank", Time::Now() - start); |
374 } | 372 } |
375 | 373 |
376 void Rankings::CompleteTransaction() { | 374 void Rankings::CompleteTransaction() { |
377 Addr node_addr(static_cast<CacheAddr>(control_data_->transaction)); | 375 Addr node_addr(static_cast<CacheAddr>(control_data_->transaction)); |
378 if (!node_addr.is_initialized() || node_addr.is_separate_file()) { | 376 if (!node_addr.is_initialized() || node_addr.is_separate_file()) { |
379 NOTREACHED(); | 377 NOTREACHED(); |
380 LOG(ERROR) << "Invalid rankings info."; | 378 LOG(ERROR) << "Invalid rankings info."; |
381 return; | 379 return; |
382 } | 380 } |
383 | 381 |
384 Trace("CompleteTransaction 0x%x", node_addr.value()); | 382 Trace("CompleteTransaction 0x%x", node_addr.value()); |
385 | 383 |
386 CacheRankingsBlock node(backend_->File(node_addr), node_addr); | 384 CacheRankingsBlock node(backend_->File(node_addr), node_addr); |
387 if (!node.Load()) | 385 if (!node.Load()) |
388 return; | 386 return; |
389 | 387 |
390 node.Data()->pointer = NULL; | 388 node.Data()->pointer = NULL; |
391 node.Store(); | 389 node.Store(); |
392 | 390 |
| 391 Addr& my_head = heads_[control_data_->operation_list]; |
| 392 Addr& my_tail = tails_[control_data_->operation_list]; |
| 393 |
393 // We want to leave the node inside the list. The entry must me marked as | 394 // We want to leave the node inside the list. The entry must me marked as |
394 // dirty, and will be removed later. Otherwise, we'll get assertions when | 395 // dirty, and will be removed later. Otherwise, we'll get assertions when |
395 // attempting to remove the dirty entry. | 396 // attempting to remove the dirty entry. |
396 if (INSERT == control_data_->operation) { | 397 if (INSERT == control_data_->operation) { |
397 Trace("FinishInsert h:0x%x t:0x%x", head_.value(), tail_.value()); | 398 Trace("FinishInsert h:0x%x t:0x%x", my_head.value(), my_tail.value()); |
398 FinishInsert(&node); | 399 FinishInsert(&node); |
399 } else if (REMOVE == control_data_->operation) { | 400 } else if (REMOVE == control_data_->operation) { |
400 Trace("RevertRemove h:0x%x t:0x%x", head_.value(), tail_.value()); | 401 Trace("RevertRemove h:0x%x t:0x%x", my_head.value(), my_tail.value()); |
401 RevertRemove(&node); | 402 RevertRemove(&node); |
402 } else { | 403 } else { |
403 NOTREACHED(); | 404 NOTREACHED(); |
404 LOG(ERROR) << "Invalid operation to recover."; | 405 LOG(ERROR) << "Invalid operation to recover."; |
405 } | 406 } |
406 } | 407 } |
407 | 408 |
408 void Rankings::FinishInsert(CacheRankingsBlock* node) { | 409 void Rankings::FinishInsert(CacheRankingsBlock* node) { |
409 control_data_->transaction = 0; | 410 control_data_->transaction = 0; |
410 control_data_->operation = 0; | 411 control_data_->operation = 0; |
411 if (head_.value() != node->address().value()) { | 412 Addr& my_head = heads_[control_data_->operation_list]; |
412 if (tail_.value() == node->address().value()) { | 413 Addr& my_tail = tails_[control_data_->operation_list]; |
| 414 if (my_head.value() != node->address().value()) { |
| 415 if (my_tail.value() == node->address().value()) { |
413 // This part will be skipped by the logic of Insert. | 416 // This part will be skipped by the logic of Insert. |
414 node->Data()->next = tail_.value(); | 417 node->Data()->next = my_tail.value(); |
415 } | 418 } |
416 | 419 |
417 Insert(node, true); | 420 Insert(node, true, static_cast<List>(control_data_->operation_list)); |
418 } | 421 } |
419 | 422 |
420 // Tell the backend about this entry. | 423 // Tell the backend about this entry. |
421 backend_->RecoveredEntry(node); | 424 backend_->RecoveredEntry(node); |
422 } | 425 } |
423 | 426 |
424 void Rankings::RevertRemove(CacheRankingsBlock* node) { | 427 void Rankings::RevertRemove(CacheRankingsBlock* node) { |
425 Addr next_addr(node->Data()->next); | 428 Addr next_addr(node->Data()->next); |
426 Addr prev_addr(node->Data()->prev); | 429 Addr prev_addr(node->Data()->prev); |
427 if (!next_addr.is_initialized() || !prev_addr.is_initialized()) { | 430 if (!next_addr.is_initialized() || !prev_addr.is_initialized()) { |
(...skipping 19 matching lines...) Expand all Loading... |
447 prev.Data()->next == next.address().value()); | 450 prev.Data()->next == next.address().value()); |
448 DCHECK(next.Data()->prev == node_value || | 451 DCHECK(next.Data()->prev == node_value || |
449 next.Data()->prev == next_addr.value() || | 452 next.Data()->prev == next_addr.value() || |
450 next.Data()->prev == prev.address().value()); | 453 next.Data()->prev == prev.address().value()); |
451 | 454 |
452 if (node_value != prev_addr.value()) | 455 if (node_value != prev_addr.value()) |
453 prev.Data()->next = node_value; | 456 prev.Data()->next = node_value; |
454 if (node_value != next_addr.value()) | 457 if (node_value != next_addr.value()) |
455 next.Data()->prev = node_value; | 458 next.Data()->prev = node_value; |
456 | 459 |
457 if (!head_.is_initialized() || !tail_.is_initialized()) { | 460 List my_list = static_cast<List>(control_data_->operation_list); |
458 head_.set_value(node_value); | 461 Addr& my_head = heads_[my_list]; |
459 tail_.set_value(node_value); | 462 Addr& my_tail = tails_[my_list]; |
460 WriteHead(); | 463 if (!my_head.is_initialized() || !my_tail.is_initialized()) { |
461 WriteTail(); | 464 my_head.set_value(node_value); |
462 } else if (head_.value() == next.address().value()) { | 465 my_tail.set_value(node_value); |
463 head_.set_value(node_value); | 466 WriteHead(my_list); |
| 467 WriteTail(my_list); |
| 468 } else if (my_head.value() == next.address().value()) { |
| 469 my_head.set_value(node_value); |
464 prev.Data()->next = next.address().value(); | 470 prev.Data()->next = next.address().value(); |
465 WriteHead(); | 471 WriteHead(my_list); |
466 } else if (tail_.value() == prev.address().value()) { | 472 } else if (my_tail.value() == prev.address().value()) { |
467 tail_.set_value(node_value); | 473 my_tail.set_value(node_value); |
468 next.Data()->prev = prev.address().value(); | 474 next.Data()->prev = prev.address().value(); |
469 WriteTail(); | 475 WriteTail(my_list); |
470 } | 476 } |
471 | 477 |
472 next.Store(); | 478 next.Store(); |
473 prev.Store(); | 479 prev.Store(); |
474 control_data_->transaction = 0; | 480 control_data_->transaction = 0; |
475 control_data_->operation = 0; | 481 control_data_->operation = 0; |
476 } | 482 } |
477 | 483 |
478 CacheRankingsBlock* Rankings::GetNext(CacheRankingsBlock* node) { | 484 CacheRankingsBlock* Rankings::GetNext(CacheRankingsBlock* node, List list) { |
479 ScopedRankingsBlock next(this); | 485 ScopedRankingsBlock next(this); |
480 if (!node) { | 486 if (!node) { |
481 if (!head_.is_initialized()) | 487 Addr& my_head = heads_[list]; |
| 488 if (!my_head.is_initialized()) |
482 return NULL; | 489 return NULL; |
483 next.reset(new CacheRankingsBlock(backend_->File(head_), head_)); | 490 next.reset(new CacheRankingsBlock(backend_->File(my_head), my_head)); |
484 } else { | 491 } else { |
485 if (!tail_.is_initialized()) | 492 Addr& my_tail = tails_[list]; |
| 493 if (!my_tail.is_initialized()) |
486 return NULL; | 494 return NULL; |
487 if (tail_.value() == node->address().value()) | 495 if (my_tail.value() == node->address().value()) |
488 return NULL; | 496 return NULL; |
489 Addr address(node->Data()->next); | 497 Addr address(node->Data()->next); |
490 next.reset(new CacheRankingsBlock(backend_->File(address), address)); | 498 next.reset(new CacheRankingsBlock(backend_->File(address), address)); |
491 } | 499 } |
492 | 500 |
493 TrackRankingsBlock(next.get(), true); | 501 TrackRankingsBlock(next.get(), true); |
494 | 502 |
495 if (!GetRanking(next.get())) | 503 if (!GetRanking(next.get())) |
496 return NULL; | 504 return NULL; |
497 | 505 |
498 if (node && !CheckSingleLink(node, next.get())) | 506 if (node && !CheckSingleLink(node, next.get())) |
499 return NULL; | 507 return NULL; |
500 | 508 |
501 return next.release(); | 509 return next.release(); |
502 } | 510 } |
503 | 511 |
504 CacheRankingsBlock* Rankings::GetPrev(CacheRankingsBlock* node) { | 512 CacheRankingsBlock* Rankings::GetPrev(CacheRankingsBlock* node, List list) { |
505 ScopedRankingsBlock prev(this); | 513 ScopedRankingsBlock prev(this); |
506 if (!node) { | 514 if (!node) { |
507 if (!tail_.is_initialized()) | 515 Addr& my_tail = tails_[list]; |
| 516 if (!my_tail.is_initialized()) |
508 return NULL; | 517 return NULL; |
509 prev.reset(new CacheRankingsBlock(backend_->File(tail_), tail_)); | 518 prev.reset(new CacheRankingsBlock(backend_->File(my_tail), my_tail)); |
510 } else { | 519 } else { |
511 if (!head_.is_initialized()) | 520 Addr& my_head = heads_[list]; |
| 521 if (!my_head.is_initialized()) |
512 return NULL; | 522 return NULL; |
513 if (head_.value() == node->address().value()) | 523 if (my_head.value() == node->address().value()) |
514 return NULL; | 524 return NULL; |
515 Addr address(node->Data()->prev); | 525 Addr address(node->Data()->prev); |
516 prev.reset(new CacheRankingsBlock(backend_->File(address), address)); | 526 prev.reset(new CacheRankingsBlock(backend_->File(address), address)); |
517 } | 527 } |
518 | 528 |
519 TrackRankingsBlock(prev.get(), true); | 529 TrackRankingsBlock(prev.get(), true); |
520 | 530 |
521 if (!GetRanking(prev.get())) | 531 if (!GetRanking(prev.get())) |
522 return NULL; | 532 return NULL; |
523 | 533 |
524 if (node && !CheckSingleLink(prev.get(), node)) | 534 if (node && !CheckSingleLink(prev.get(), node)) |
525 return NULL; | 535 return NULL; |
526 | 536 |
527 return prev.release(); | 537 return prev.release(); |
528 } | 538 } |
529 | 539 |
530 void Rankings::FreeRankingsBlock(CacheRankingsBlock* node) { | 540 void Rankings::FreeRankingsBlock(CacheRankingsBlock* node) { |
531 TrackRankingsBlock(node, false); | 541 TrackRankingsBlock(node, false); |
532 } | 542 } |
533 | 543 |
534 int Rankings::SelfCheck() { | 544 int Rankings::SelfCheck() { |
535 if (!head_.is_initialized()) { | 545 int total = 0; |
536 if (!tail_.is_initialized()) | 546 for (int i = 0; i < LAST_ELEMENT; i++) { |
537 return 0; | 547 int partial = CheckList(static_cast<List>(i)); |
538 return ERR_INVALID_TAIL; | 548 if (partial < 0) |
| 549 return partial; |
| 550 total += partial; |
539 } | 551 } |
540 if (!tail_.is_initialized()) | 552 return total; |
541 return ERR_INVALID_HEAD; | |
542 | |
543 if (tail_.is_separate_file()) | |
544 return ERR_INVALID_TAIL; | |
545 | |
546 if (head_.is_separate_file()) | |
547 return ERR_INVALID_HEAD; | |
548 | |
549 int num_items = 0; | |
550 Addr address(head_.value()); | |
551 Addr prev(head_.value()); | |
552 scoped_ptr<CacheRankingsBlock> node; | |
553 do { | |
554 node.reset(new CacheRankingsBlock(backend_->File(address), address)); | |
555 node->Load(); | |
556 if (node->Data()->prev != prev.value()) | |
557 return ERR_INVALID_PREV; | |
558 if (!CheckEntry(node.get())) | |
559 return ERR_INVALID_ENTRY; | |
560 | |
561 prev.set_value(address.value()); | |
562 address.set_value(node->Data()->next); | |
563 if (!address.is_initialized() || address.is_separate_file()) | |
564 return ERR_INVALID_NEXT; | |
565 | |
566 num_items++; | |
567 } while (node->address().value() != address.value()); | |
568 return num_items; | |
569 } | 553 } |
570 | 554 |
571 bool Rankings::SanityCheck(CacheRankingsBlock* node, bool from_list) { | 555 bool Rankings::SanityCheck(CacheRankingsBlock* node, bool from_list) { |
572 const RankingsNode* data = node->Data(); | 556 const RankingsNode* data = node->Data(); |
573 if (!data->contents) | 557 if (!data->contents) |
574 return false; | 558 return false; |
575 | 559 |
576 // It may have never been inserted. | 560 // It may have never been inserted. |
577 if (from_list && (!data->last_used || !data->last_modified)) | 561 if (from_list && (!data->last_used || !data->last_modified)) |
578 return false; | 562 return false; |
579 | 563 |
580 if ((!data->next && data->prev) || (data->next && !data->prev)) | 564 if ((!data->next && data->prev) || (data->next && !data->prev)) |
581 return false; | 565 return false; |
582 | 566 |
583 // Both pointers on zero is a node out of the list. | 567 // Both pointers on zero is a node out of the list. |
584 if (!data->next && !data->prev && from_list) | 568 if (!data->next && !data->prev && from_list) |
585 return false; | 569 return false; |
586 | 570 |
587 if ((node->address().value() == data->prev) && (head_.value() != data->prev)) | 571 if ((node->address().value() == data->prev) && !IsHead(data->prev)) |
588 return false; | 572 return false; |
589 | 573 |
590 if ((node->address().value() == data->next) && (tail_.value() != data->next)) | 574 if ((node->address().value() == data->next) && !IsTail(data->next)) |
591 return false; | 575 return false; |
592 | 576 |
593 return true; | 577 return true; |
594 } | 578 } |
595 | 579 |
596 Addr Rankings::ReadHead() { | 580 void Rankings::ReadHeads() { |
597 return Addr(control_data_->heads[NO_USE]); | 581 for (int i = 0; i < LAST_ELEMENT; i++) |
| 582 heads_[i] = Addr(control_data_->heads[i]); |
598 } | 583 } |
599 | 584 |
600 Addr Rankings::ReadTail() { | 585 void Rankings::ReadTails() { |
601 return Addr(control_data_->tails[NO_USE]); | 586 for (int i = 0; i < LAST_ELEMENT; i++) |
| 587 tails_[i] = Addr(control_data_->tails[i]); |
602 } | 588 } |
603 | 589 |
604 void Rankings::WriteHead() { | 590 void Rankings::WriteHead(List list) { |
605 control_data_->heads[NO_USE] = head_.value(); | 591 control_data_->heads[list] = heads_[list].value(); |
606 } | 592 } |
607 | 593 |
608 void Rankings::WriteTail() { | 594 void Rankings::WriteTail(List list) { |
609 control_data_->tails[NO_USE] = tail_.value(); | 595 control_data_->tails[list] = tails_[list].value(); |
610 } | 596 } |
611 | 597 |
612 bool Rankings::CheckEntry(CacheRankingsBlock* rankings) { | 598 bool Rankings::CheckEntry(CacheRankingsBlock* rankings) { |
613 if (!rankings->Data()->pointer) | 599 if (!rankings->Data()->pointer) |
614 return true; | 600 return true; |
615 | 601 |
616 // If this entry is not dirty, it is a serious problem. | 602 // If this entry is not dirty, it is a serious problem. |
617 return backend_->GetCurrentEntryId() != rankings->Data()->dirty; | 603 return backend_->GetCurrentEntryId() != rankings->Data()->dirty; |
618 } | 604 } |
619 | 605 |
620 bool Rankings::CheckLinks(CacheRankingsBlock* node, CacheRankingsBlock* prev, | 606 bool Rankings::CheckLinks(CacheRankingsBlock* node, CacheRankingsBlock* prev, |
621 CacheRankingsBlock* next) { | 607 CacheRankingsBlock* next, List list) { |
622 if ((prev->Data()->next != node->address().value() && | 608 if ((prev->Data()->next != node->address().value() && |
623 head_.value() != node->address().value()) || | 609 heads_[list].value() != node->address().value()) || |
624 (next->Data()->prev != node->address().value() && | 610 (next->Data()->prev != node->address().value() && |
625 tail_.value() != node->address().value())) { | 611 tails_[list].value() != node->address().value())) { |
626 LOG(ERROR) << "Inconsistent LRU."; | 612 LOG(ERROR) << "Inconsistent LRU."; |
627 | 613 |
628 if (prev->Data()->next == next->address().value() && | 614 if (prev->Data()->next == next->address().value() && |
629 next->Data()->prev == prev->address().value()) { | 615 next->Data()->prev == prev->address().value()) { |
630 // The list is actually ok, node is wrong. | 616 // The list is actually ok, node is wrong. |
631 node->Data()->next = 0; | 617 node->Data()->next = 0; |
632 node->Data()->prev = 0; | 618 node->Data()->prev = 0; |
633 node->Store(); | 619 node->Store(); |
634 return false; | 620 return false; |
635 } | 621 } |
(...skipping 10 matching lines...) Expand all Loading... |
646 next->Data()->prev != prev->address().value()) { | 632 next->Data()->prev != prev->address().value()) { |
647 LOG(ERROR) << "Inconsistent LRU."; | 633 LOG(ERROR) << "Inconsistent LRU."; |
648 | 634 |
649 backend_->CriticalError(ERR_INVALID_LINKS); | 635 backend_->CriticalError(ERR_INVALID_LINKS); |
650 return false; | 636 return false; |
651 } | 637 } |
652 | 638 |
653 return true; | 639 return true; |
654 } | 640 } |
655 | 641 |
| 642 int Rankings::CheckList(List list) { |
| 643 Addr& my_head = heads_[list]; |
| 644 Addr& my_tail = tails_[list]; |
| 645 if (!my_head.is_initialized()) { |
| 646 if (!my_tail.is_initialized()) |
| 647 return 0; |
| 648 // If there is no head, having a tail is an error. |
| 649 return ERR_INVALID_TAIL; |
| 650 } |
| 651 // If there is no tail, having a head is an error. |
| 652 if (!my_tail.is_initialized()) |
| 653 return ERR_INVALID_HEAD; |
| 654 |
| 655 if (my_tail.is_separate_file()) |
| 656 return ERR_INVALID_TAIL; |
| 657 |
| 658 if (my_head.is_separate_file()) |
| 659 return ERR_INVALID_HEAD; |
| 660 |
| 661 int num_items = 0; |
| 662 Addr address(my_head.value()); |
| 663 Addr prev(my_head.value()); |
| 664 scoped_ptr<CacheRankingsBlock> node; |
| 665 do { |
| 666 node.reset(new CacheRankingsBlock(backend_->File(address), address)); |
| 667 node->Load(); |
| 668 if (node->Data()->prev != prev.value()) |
| 669 return ERR_INVALID_PREV; |
| 670 if (!CheckEntry(node.get())) |
| 671 return ERR_INVALID_ENTRY; |
| 672 |
| 673 prev.set_value(address.value()); |
| 674 address.set_value(node->Data()->next); |
| 675 if (!address.is_initialized() || address.is_separate_file()) |
| 676 return ERR_INVALID_NEXT; |
| 677 |
| 678 num_items++; |
| 679 } while (node->address().value() != address.value()); |
| 680 return num_items; |
| 681 } |
| 682 |
| 683 bool Rankings::IsHead(CacheAddr addr) { |
| 684 for (int i = 0; i < LAST_ELEMENT; i++) |
| 685 if (addr == heads_[i].value()) |
| 686 return true; |
| 687 return false; |
| 688 } |
| 689 |
| 690 bool Rankings::IsTail(CacheAddr addr) { |
| 691 for (int i = 0; i < LAST_ELEMENT; i++) |
| 692 if (addr == tails_[i].value()) |
| 693 return true; |
| 694 return false; |
| 695 } |
| 696 |
656 void Rankings::TrackRankingsBlock(CacheRankingsBlock* node, | 697 void Rankings::TrackRankingsBlock(CacheRankingsBlock* node, |
657 bool start_tracking) { | 698 bool start_tracking) { |
658 if (!node) | 699 if (!node) |
659 return; | 700 return; |
660 | 701 |
661 IteratorPair current(node->address().value(), node); | 702 IteratorPair current(node->address().value(), node); |
662 | 703 |
663 if (start_tracking) | 704 if (start_tracking) |
664 iterators_.push_back(current); | 705 iterators_.push_back(current); |
665 else | 706 else |
666 iterators_.remove(current); | 707 iterators_.remove(current); |
667 } | 708 } |
668 | 709 |
669 // We expect to have just a few iterators at any given time, maybe two or three, | 710 // We expect to have just a few iterators at any given time, maybe two or three, |
670 // But we could have more than one pointing at the same mode. We walk the list | 711 // But we could have more than one pointing at the same mode. We walk the list |
671 // of cache iterators and update all that are pointing to the given node. | 712 // of cache iterators and update all that are pointing to the given node. |
672 void Rankings::UpdateIterators(CacheRankingsBlock* node) { | 713 void Rankings::UpdateIterators(CacheRankingsBlock* node) { |
673 CacheAddr address = node->address().value(); | 714 CacheAddr address = node->address().value(); |
674 for (IteratorList::iterator it = iterators_.begin(); it != iterators_.end(); | 715 for (IteratorList::iterator it = iterators_.begin(); it != iterators_.end(); |
675 ++it) { | 716 ++it) { |
676 if (it->first == address) { | 717 if (it->first == address) { |
677 CacheRankingsBlock* other = it->second; | 718 CacheRankingsBlock* other = it->second; |
678 other->Data()->next = node->Data()->next; | 719 other->Data()->next = node->Data()->next; |
679 other->Data()->prev = node->Data()->prev; | 720 other->Data()->prev = node->Data()->prev; |
680 } | 721 } |
681 } | 722 } |
682 } | 723 } |
683 | 724 |
684 } // namespace disk_cache | 725 } // namespace disk_cache |
OLD | NEW |