| 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 |