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/block_files.h" | 5 #include "net/disk_cache/block_files.h" |
6 | 6 |
7 #include "base/file_util.h" | 7 #include "base/file_util.h" |
8 #include "base/histogram.h" | 8 #include "base/histogram.h" |
9 #include "base/string_util.h" | 9 #include "base/string_util.h" |
10 #include "base/time.h" | 10 #include "base/time.h" |
| 11 #include "net/disk_cache/cache_util.h" |
11 #include "net/disk_cache/file_lock.h" | 12 #include "net/disk_cache/file_lock.h" |
12 | 13 |
13 using base::Time; | 14 using base::Time; |
14 | 15 |
15 namespace { | 16 namespace { |
16 | 17 |
17 const wchar_t* kBlockName = L"data_"; | 18 const wchar_t* kBlockName = L"data_"; |
18 | 19 |
19 // This array is used to perform a fast lookup of the nibble bit pattern to the | 20 // This array is used to perform a fast lookup of the nibble bit pattern to the |
20 // type of entry that can be stored there (number of consecutive blocks). | 21 // type of entry that can be stored there (number of consecutive blocks). |
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
129 | 130 |
130 for (int j = 0; j < 8; j++, map_block >>= 4) { | 131 for (int j = 0; j < 8; j++, map_block >>= 4) { |
131 int type = GetMapBlockType(map_block); | 132 int type = GetMapBlockType(map_block); |
132 if (type) | 133 if (type) |
133 header->empty[type -1]++; | 134 header->empty[type -1]++; |
134 } | 135 } |
135 } | 136 } |
136 } | 137 } |
137 | 138 |
138 // Returns true if the current block file should not be used as-is to store more | 139 // Returns true if the current block file should not be used as-is to store more |
139 // records. |block_count| is the number of blocks to allocate, and | 140 // records. |block_count| is the number of blocks to allocate. |
140 // |use_next_file| is set to true on return if we should use the next file in | |
141 // the chain, even though we could find empty space on the current file. | |
142 bool NeedToGrowBlockFile(const disk_cache::BlockFileHeader* header, | 141 bool NeedToGrowBlockFile(const disk_cache::BlockFileHeader* header, |
143 int block_count, bool* use_next_file) { | 142 int block_count) { |
144 if ((header->max_entries > disk_cache::kMaxBlocks * 9 / 10) && | 143 bool have_space = false; |
145 header->next_file) { | 144 int empty_blocks = 0; |
| 145 for (int i = 0; i < disk_cache::kMaxNumBlocks; i++) { |
| 146 empty_blocks += header->empty[i] * (i + 1); |
| 147 if (i >= block_count - 1 && header->empty[i]) |
| 148 have_space = true; |
| 149 } |
| 150 |
| 151 if (header->next_file && (empty_blocks < disk_cache::kMaxBlocks / 10)) { |
146 // This file is almost full but we already created another one, don't use | 152 // This file is almost full but we already created another one, don't use |
147 // this file yet so that it is easier to find empty blocks when we start | 153 // this file yet so that it is easier to find empty blocks when we start |
148 // using this file again. | 154 // using this file again. |
149 *use_next_file = true; | |
150 return true; | 155 return true; |
151 } | 156 } |
152 *use_next_file = false; | 157 return !have_space; |
153 for (int i = block_count; i <= disk_cache::kMaxNumBlocks; i++) { | |
154 if (header->empty[i - 1]) | |
155 return false; | |
156 } | |
157 return true; | |
158 } | 158 } |
159 | 159 |
160 } // namespace | 160 } // namespace |
161 | 161 |
162 namespace disk_cache { | 162 namespace disk_cache { |
163 | 163 |
164 BlockFiles::~BlockFiles() { | 164 BlockFiles::~BlockFiles() { |
165 if (zero_buffer_) | 165 if (zero_buffer_) |
166 delete[] zero_buffer_; | 166 delete[] zero_buffer_; |
167 CloseFiles(); | 167 CloseFiles(); |
168 } | 168 } |
169 | 169 |
170 bool BlockFiles::Init(bool create_files) { | 170 bool BlockFiles::Init(bool create_files) { |
171 DCHECK(!init_); | 171 DCHECK(!init_); |
172 if (init_) | 172 if (init_) |
173 return false; | 173 return false; |
174 | 174 |
175 block_files_.resize(kFirstAdditionlBlockFile); | 175 block_files_.resize(kFirstAdditionlBlockFile); |
176 for (int i = 0; i < kFirstAdditionlBlockFile; i++) { | 176 for (int i = 0; i < kFirstAdditionlBlockFile; i++) { |
177 if (create_files) | 177 if (create_files) |
178 if (!CreateBlockFile(i, static_cast<FileType>(i + 1), true)) | 178 if (!CreateBlockFile(i, static_cast<FileType>(i + 1), true)) |
179 return false; | 179 return false; |
180 | 180 |
181 if (!OpenBlockFile(i)) | 181 if (!OpenBlockFile(i)) |
182 return false; | 182 return false; |
| 183 |
| 184 // Walk this chain of files removing empty ones. |
| 185 RemoveEmptyFile(static_cast<FileType>(i + 1)); |
183 } | 186 } |
184 | 187 |
185 init_ = true; | 188 init_ = true; |
186 return true; | 189 return true; |
187 } | 190 } |
188 | 191 |
189 void BlockFiles::CloseFiles() { | 192 void BlockFiles::CloseFiles() { |
190 init_ = false; | 193 init_ = false; |
191 for (unsigned int i = 0; i < block_files_.size(); i++) { | 194 for (unsigned int i = 0; i < block_files_.size(); i++) { |
192 if (block_files_[i]) { | 195 if (block_files_[i]) { |
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
305 | 308 |
306 return true; | 309 return true; |
307 } | 310 } |
308 | 311 |
309 MappedFile* BlockFiles::FileForNewBlock(FileType block_type, int block_count) { | 312 MappedFile* BlockFiles::FileForNewBlock(FileType block_type, int block_count) { |
310 COMPILE_ASSERT(RANKINGS == 1, invalid_fily_type); | 313 COMPILE_ASSERT(RANKINGS == 1, invalid_fily_type); |
311 MappedFile* file = block_files_[block_type - 1]; | 314 MappedFile* file = block_files_[block_type - 1]; |
312 BlockFileHeader* header = reinterpret_cast<BlockFileHeader*>(file->buffer()); | 315 BlockFileHeader* header = reinterpret_cast<BlockFileHeader*>(file->buffer()); |
313 | 316 |
314 Time start = Time::Now(); | 317 Time start = Time::Now(); |
315 bool use_next_file; | 318 while (NeedToGrowBlockFile(header, block_count)) { |
316 while (NeedToGrowBlockFile(header, block_count, &use_next_file)) { | 319 if (kMaxBlocks == header->max_entries) { |
317 if (use_next_file || kMaxBlocks == header->max_entries) { | |
318 file = NextFile(file); | 320 file = NextFile(file); |
319 if (!file) | 321 if (!file) |
320 return NULL; | 322 return NULL; |
321 header = reinterpret_cast<BlockFileHeader*>(file->buffer()); | 323 header = reinterpret_cast<BlockFileHeader*>(file->buffer()); |
322 continue; | 324 continue; |
323 } | 325 } |
324 | 326 |
325 if (!GrowBlockFile(file, header)) | 327 if (!GrowBlockFile(file, header)) |
326 return NULL; | 328 return NULL; |
327 break; | 329 break; |
(...skipping 26 matching lines...) Expand all Loading... |
354 } | 356 } |
355 | 357 |
356 int BlockFiles::CreateNextBlockFile(FileType block_type) { | 358 int BlockFiles::CreateNextBlockFile(FileType block_type) { |
357 for (int i = kFirstAdditionlBlockFile; i <= kMaxBlockFile; i++) { | 359 for (int i = kFirstAdditionlBlockFile; i <= kMaxBlockFile; i++) { |
358 if (CreateBlockFile(i, block_type, false)) | 360 if (CreateBlockFile(i, block_type, false)) |
359 return i; | 361 return i; |
360 } | 362 } |
361 return 0; | 363 return 0; |
362 } | 364 } |
363 | 365 |
| 366 // We walk the list of files for this particular block type, deleting the ones |
| 367 // that are empty. |
| 368 void BlockFiles::RemoveEmptyFile(FileType block_type) { |
| 369 MappedFile* file = block_files_[block_type - 1]; |
| 370 BlockFileHeader* header = reinterpret_cast<BlockFileHeader*>(file->buffer()); |
| 371 |
| 372 while (header->next_file) { |
| 373 // Only the block_file argument is relevant for what we want. |
| 374 Addr address(BLOCK_256, 1, header->next_file, 0); |
| 375 MappedFile* next_file = GetFile(address); |
| 376 if (!next_file) |
| 377 return; |
| 378 |
| 379 BlockFileHeader* next_header = |
| 380 reinterpret_cast<BlockFileHeader*>(next_file->buffer()); |
| 381 if (!next_header->num_entries) { |
| 382 DCHECK_EQ(next_header->entry_size, header->entry_size); |
| 383 // Delete next_file and remove it from the chain. |
| 384 int file_index = header->next_file; |
| 385 header->next_file = next_header->next_file; |
| 386 DCHECK(block_files_.size() >= static_cast<unsigned int>(file_index)); |
| 387 block_files_[file_index]->Release(); |
| 388 block_files_[file_index] = NULL; |
| 389 |
| 390 std::wstring name = Name(file_index); |
| 391 int failure = DeleteCacheFile(name) ? 0 : 1; |
| 392 UMA_HISTOGRAM_COUNTS("DiskCache.DeleteFailed2", failure); |
| 393 if (failure) |
| 394 LOG(ERROR) << "Failed to delete " << name << " from the cache."; |
| 395 continue; |
| 396 } |
| 397 |
| 398 header = next_header; |
| 399 file = next_file; |
| 400 } |
| 401 } |
| 402 |
364 bool BlockFiles::CreateBlock(FileType block_type, int block_count, | 403 bool BlockFiles::CreateBlock(FileType block_type, int block_count, |
365 Addr* block_address) { | 404 Addr* block_address) { |
366 if (block_type < RANKINGS || block_type > BLOCK_4K || | 405 if (block_type < RANKINGS || block_type > BLOCK_4K || |
367 block_count < 1 || block_count > 4) | 406 block_count < 1 || block_count > 4) |
368 return false; | 407 return false; |
369 if (!init_) | 408 if (!init_) |
370 return false; | 409 return false; |
371 | 410 |
372 MappedFile* file = FileForNewBlock(block_type, block_count); | 411 MappedFile* file = FileForNewBlock(block_type, block_count); |
373 if (!file) | 412 if (!file) |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
406 return; | 445 return; |
407 | 446 |
408 size_t size = address.BlockSize() * address.num_blocks(); | 447 size_t size = address.BlockSize() * address.num_blocks(); |
409 size_t offset = address.start_block() * address.BlockSize() + | 448 size_t offset = address.start_block() * address.BlockSize() + |
410 kBlockHeaderSize; | 449 kBlockHeaderSize; |
411 if (deep) | 450 if (deep) |
412 file->Write(zero_buffer_, size, offset); | 451 file->Write(zero_buffer_, size, offset); |
413 | 452 |
414 BlockFileHeader* header = reinterpret_cast<BlockFileHeader*>(file->buffer()); | 453 BlockFileHeader* header = reinterpret_cast<BlockFileHeader*>(file->buffer()); |
415 DeleteMapBlock(address.start_block(), address.num_blocks(), header); | 454 DeleteMapBlock(address.start_block(), address.num_blocks(), header); |
| 455 if (!header->num_entries) { |
| 456 // This file is now empty. Let's try to delete it. |
| 457 FileType type = Addr::RequiredFileType(header->entry_size); |
| 458 if (Addr::BlockSizeForFileType(RANKINGS) == header->entry_size) |
| 459 type = RANKINGS; |
| 460 RemoveEmptyFile(type); |
| 461 } |
416 } | 462 } |
417 | 463 |
418 bool BlockFiles::FixBlockFileHeader(MappedFile* file) { | 464 bool BlockFiles::FixBlockFileHeader(MappedFile* file) { |
419 BlockFileHeader* header = reinterpret_cast<BlockFileHeader*>(file->buffer()); | 465 BlockFileHeader* header = reinterpret_cast<BlockFileHeader*>(file->buffer()); |
420 int file_size = static_cast<int>(file->GetLength()); | 466 int file_size = static_cast<int>(file->GetLength()); |
421 if (file_size < static_cast<int>(sizeof(*header))) | 467 if (file_size < static_cast<int>(sizeof(*header))) |
422 return false; // file_size > 2GB is also an error. | 468 return false; // file_size > 2GB is also an error. |
423 | 469 |
424 int expected = header->entry_size * header->max_entries + sizeof(*header); | 470 int expected = header->entry_size * header->max_entries + sizeof(*header); |
425 if (file_size != expected) { | 471 if (file_size != expected) { |
426 int max_expected = header->entry_size * kMaxBlocks + sizeof(*header); | 472 int max_expected = header->entry_size * kMaxBlocks + sizeof(*header); |
427 if (file_size < expected || header->empty[3] || file_size > max_expected) { | 473 if (file_size < expected || header->empty[3] || file_size > max_expected) { |
428 NOTREACHED(); | 474 NOTREACHED(); |
429 LOG(ERROR) << "Unexpected file size"; | 475 LOG(ERROR) << "Unexpected file size"; |
430 return false; | 476 return false; |
431 } | 477 } |
432 // We were in the middle of growing the file. | 478 // We were in the middle of growing the file. |
433 int num_entries = (file_size - sizeof(*header)) / header->entry_size; | 479 int num_entries = (file_size - sizeof(*header)) / header->entry_size; |
434 header->max_entries = num_entries; | 480 header->max_entries = num_entries; |
435 } | 481 } |
436 | 482 |
437 FixAllocationCounters(header); | 483 FixAllocationCounters(header); |
438 header->updating = 0; | 484 header->updating = 0; |
439 return true; | 485 return true; |
440 } | 486 } |
441 | 487 |
442 } // namespace disk_cache | 488 } // namespace disk_cache |
OLD | NEW |