Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(33)

Side by Side Diff: net/disk_cache/v3/block_bitmaps.cc

Issue 14991008: Disk cache: Add base files for implementation of file format version 3. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Rebase attempt 2 Created 7 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 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/atomicops.h" 7 #include "base/atomicops.h"
8 #include "base/files/file_path.h" 8 #include "base/file_util.h"
9 #include "base/metrics/histogram.h" 9 #include "base/metrics/histogram.h"
10 #include "base/string_util.h" 10 #include "base/string_util.h"
11 #include "base/stringprintf.h" 11 #include "base/stringprintf.h"
12 #include "base/threading/thread_checker.h" 12 #include "base/threading/thread_checker.h"
13 #include "base/time.h" 13 #include "base/time.h"
14 #include "net/disk_cache/cache_util.h" 14 #include "net/disk_cache/cache_util.h"
15 #include "net/disk_cache/file_lock.h" 15 #include "net/disk_cache/file_lock.h"
16 #include "net/disk_cache/trace.h" 16 #include "net/disk_cache/trace.h"
17 17
18 using base::TimeTicks; 18 using base::TimeTicks;
19 19
20 namespace {
21
22 const char* kBlockName = "data_";
23
24 // This array is used to perform a fast lookup of the nibble bit pattern to the
25 // type of entry that can be stored there (number of consecutive blocks).
26 const char s_types[16] = {4, 3, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0};
27
28 // Returns the type of block (number of consecutive blocks that can be stored)
29 // for a given nibble of the bitmap.
30 inline int GetMapBlockType(uint8 value) {
31 value &= 0xf;
32 return s_types[value];
33 }
34
35 void FixAllocationCounters(disk_cache::BlockFileHeader* header);
36
37 // Creates a new entry on the allocation map, updating the apropriate counters.
38 // target is the type of block to use (number of empty blocks), and size is the
39 // actual number of blocks to use.
40 bool CreateMapBlock(int target, int size, disk_cache::BlockFileHeader* header,
41 int* index) {
42 if (target <= 0 || target > disk_cache::kMaxNumBlocks ||
43 size <= 0 || size > disk_cache::kMaxNumBlocks) {
44 NOTREACHED();
45 return false;
46 }
47
48 TimeTicks start = TimeTicks::Now();
49 // We are going to process the map on 32-block chunks (32 bits), and on every
50 // chunk, iterate through the 8 nibbles where the new block can be located.
51 int current = header->hints[target - 1];
52 for (int i = 0; i < header->max_entries / 32; i++, current++) {
53 if (current == header->max_entries / 32)
54 current = 0;
55 uint32 map_block = header->allocation_map[current];
56
57 for (int j = 0; j < 8; j++, map_block >>= 4) {
58 if (GetMapBlockType(map_block) != target)
59 continue;
60
61 disk_cache::FileLock lock(header);
62 int index_offset = j * 4 + 4 - target;
63 *index = current * 32 + index_offset;
64 DCHECK_EQ(*index / 4, (*index + size - 1) / 4);
65 uint32 to_add = ((1 << size) - 1) << index_offset;
66 header->num_entries++;
67
68 // Note that there is no race in the normal sense here, but if we enforce
69 // the order of memory accesses between num_entries and allocation_map, we
70 // can assert that even if we crash here, num_entries will never be less
71 // than the actual number of used blocks.
72 base::subtle::MemoryBarrier();
73 header->allocation_map[current] |= to_add;
74
75 header->hints[target - 1] = current;
76 header->empty[target - 1]--;
77 DCHECK_GE(header->empty[target - 1], 0);
78 if (target != size) {
79 header->empty[target - size - 1]++;
80 }
81 HISTOGRAM_TIMES("DiskCache.CreateBlock", TimeTicks::Now() - start);
82 return true;
83 }
84 }
85
86 // It is possible to have an undetected corruption (for example when the OS
87 // crashes), fix it here.
88 LOG(ERROR) << "Failing CreateMapBlock";
89 FixAllocationCounters(header);
90 return false;
91 }
92
93 // Deletes the block pointed by index from allocation_map, and updates the
94 // relevant counters on the header.
95 void DeleteMapBlock(int index, int size, disk_cache::BlockFileHeader* header) {
96 if (size < 0 || size > disk_cache::kMaxNumBlocks) {
97 NOTREACHED();
98 return;
99 }
100 TimeTicks start = TimeTicks::Now();
101 int byte_index = index / 8;
102 uint8* byte_map = reinterpret_cast<uint8*>(header->allocation_map);
103 uint8 map_block = byte_map[byte_index];
104
105 if (index % 8 >= 4)
106 map_block >>= 4;
107
108 // See what type of block will be availabe after we delete this one.
109 int bits_at_end = 4 - size - index % 4;
110 uint8 end_mask = (0xf << (4 - bits_at_end)) & 0xf;
111 bool update_counters = (map_block & end_mask) == 0;
112 uint8 new_value = map_block & ~(((1 << size) - 1) << (index % 4));
113 int new_type = GetMapBlockType(new_value);
114
115 disk_cache::FileLock lock(header);
116 DCHECK((((1 << size) - 1) << (index % 8)) < 0x100);
117 uint8 to_clear = ((1 << size) - 1) << (index % 8);
118 DCHECK((byte_map[byte_index] & to_clear) == to_clear);
119 byte_map[byte_index] &= ~to_clear;
120
121 if (update_counters) {
122 if (bits_at_end)
123 header->empty[bits_at_end - 1]--;
124 header->empty[new_type - 1]++;
125 DCHECK_GE(header->empty[bits_at_end - 1], 0);
126 }
127 base::subtle::MemoryBarrier();
128 header->num_entries--;
129 DCHECK_GE(header->num_entries, 0);
130 HISTOGRAM_TIMES("DiskCache.DeleteBlock", TimeTicks::Now() - start);
131 }
132
133 #ifndef NDEBUG
134 // Returns true if the specified block is used. Note that this is a simplified
135 // version of DeleteMapBlock().
136 bool UsedMapBlock(int index, int size, disk_cache::BlockFileHeader* header) {
137 if (size < 0 || size > disk_cache::kMaxNumBlocks) {
138 NOTREACHED();
139 return false;
140 }
141 int byte_index = index / 8;
142 uint8* byte_map = reinterpret_cast<uint8*>(header->allocation_map);
143 uint8 map_block = byte_map[byte_index];
144
145 if (index % 8 >= 4)
146 map_block >>= 4;
147
148 DCHECK((((1 << size) - 1) << (index % 8)) < 0x100);
149 uint8 to_clear = ((1 << size) - 1) << (index % 8);
150 return ((byte_map[byte_index] & to_clear) == to_clear);
151 }
152 #endif // NDEBUG
153
154 // Restores the "empty counters" and allocation hints.
155 void FixAllocationCounters(disk_cache::BlockFileHeader* header) {
156 for (int i = 0; i < disk_cache::kMaxNumBlocks; i++) {
157 header->hints[i] = 0;
158 header->empty[i] = 0;
159 }
160
161 for (int i = 0; i < header->max_entries / 32; i++) {
162 uint32 map_block = header->allocation_map[i];
163
164 for (int j = 0; j < 8; j++, map_block >>= 4) {
165 int type = GetMapBlockType(map_block);
166 if (type)
167 header->empty[type -1]++;
168 }
169 }
170 }
171
172 // Returns true if the current block file should not be used as-is to store more
173 // records. |block_count| is the number of blocks to allocate.
174 bool NeedToGrowBlockFile(const disk_cache::BlockFileHeader* header,
175 int block_count) {
176 bool have_space = false;
177 int empty_blocks = 0;
178 for (int i = 0; i < disk_cache::kMaxNumBlocks; i++) {
179 empty_blocks += header->empty[i] * (i + 1);
180 if (i >= block_count - 1 && header->empty[i])
181 have_space = true;
182 }
183
184 if (header->next_file && (empty_blocks < disk_cache::kMaxBlocks / 10)) {
185 // This file is almost full but we already created another one, don't use
186 // this file yet so that it is easier to find empty blocks when we start
187 // using this file again.
188 return true;
189 }
190 return !have_space;
191 }
192
193 // Returns the number of empty blocks for this file.
194 int EmptyBlocks(const disk_cache::BlockFileHeader* header) {
195 int empty_blocks = 0;
196 for (int i = 0; i < disk_cache::kMaxNumBlocks; i++) {
197 empty_blocks += header->empty[i] * (i + 1);
198 if (header->empty[i] < 0)
199 return false;
200 }
201 return empty_blocks;
202 }
203
204 // Returns true if the counters look OK.
205 bool ValidateCounters(const disk_cache::BlockFileHeader* header) {
206 if (header->max_entries < 0 || header->max_entries > disk_cache::kMaxBlocks ||
207 header->num_entries < 0)
208 return false;
209
210 int empty_blocks = EmptyBlocks(header);
211 if (empty_blocks + header->num_entries > header->max_entries)
212 return false;
213
214 return true;
215 }
216
217 } // namespace
218
219 namespace disk_cache { 20 namespace disk_cache {
220 21
221 BlockFiles::BlockFiles(const base::FilePath& path) 22 BlockFiles::BlockFiles(const base::FilePath& path)
222 : init_(false), zero_buffer_(NULL), path_(path) { 23 : init_(false), zero_buffer_(NULL), path_(path) {
223 } 24 }
224 25
225 BlockFiles::~BlockFiles() { 26 BlockFiles::~BlockFiles() {
226 if (zero_buffer_) 27 if (zero_buffer_)
227 delete[] zero_buffer_; 28 delete[] zero_buffer_;
228 CloseFiles(); 29 CloseFiles();
(...skipping 166 matching lines...) Expand 10 before | Expand all | Expand 10 after
395 size_t offset = address.start_block() * address.BlockSize() + 196 size_t offset = address.start_block() * address.BlockSize() +
396 kBlockHeaderSize; 197 kBlockHeaderSize;
397 bool ok = file->Read(buffer.get(), size, offset); 198 bool ok = file->Read(buffer.get(), size, offset);
398 DCHECK(ok); 199 DCHECK(ok);
399 } 200 }
400 201
401 return rv; 202 return rv;
402 #endif 203 #endif
403 } 204 }
404 205
405 bool BlockFiles::CreateBlockFile(int index, FileType file_type, bool force) {
406 base::FilePath name = Name(index);
407 int flags =
408 force ? base::PLATFORM_FILE_CREATE_ALWAYS : base::PLATFORM_FILE_CREATE;
409 flags |= base::PLATFORM_FILE_WRITE | base::PLATFORM_FILE_EXCLUSIVE_WRITE;
410
411 scoped_refptr<File> file(new File(
412 base::CreatePlatformFile(name, flags, NULL, NULL)));
413 if (!file->IsValid())
414 return false;
415
416 BlockFileHeader header;
417 header.entry_size = Addr::BlockSizeForFileType(file_type);
418 header.this_file = static_cast<int16>(index);
419 DCHECK(index <= kint16max && index >= 0);
420
421 return file->Write(&header, sizeof(header), 0);
422 }
423
424 bool BlockFiles::OpenBlockFile(int index) {
425 if (block_files_.size() - 1 < static_cast<unsigned int>(index)) {
426 DCHECK(index > 0);
427 int to_add = index - static_cast<int>(block_files_.size()) + 1;
428 block_files_.resize(block_files_.size() + to_add);
429 }
430
431 base::FilePath name = Name(index);
432 scoped_refptr<MappedFile> file(new MappedFile());
433
434 if (!file->Init(name, kBlockHeaderSize)) {
435 LOG(ERROR) << "Failed to open " << name.value();
436 return false;
437 }
438
439 size_t file_len = file->GetLength();
440 if (file_len < static_cast<size_t>(kBlockHeaderSize)) {
441 LOG(ERROR) << "File too small " << name.value();
442 return false;
443 }
444
445 BlockFileHeader* header = reinterpret_cast<BlockFileHeader*>(file->buffer());
446 if (kBlockMagic != header->magic || kCurrentVersion != header->version) {
447 LOG(ERROR) << "Invalid file version or magic " << name.value();
448 return false;
449 }
450
451 if (header->updating || !ValidateCounters(header)) {
452 // Last instance was not properly shutdown, or counters are out of sync.
453 if (!FixBlockFileHeader(file)) {
454 LOG(ERROR) << "Unable to fix block file " << name.value();
455 return false;
456 }
457 }
458
459 if (static_cast<int>(file_len) <
460 header->max_entries * header->entry_size + kBlockHeaderSize) {
461 LOG(ERROR) << "File too small " << name.value();
462 return false;
463 }
464
465 if (index == 0) {
466 // Load the links file into memory with a single read.
467 scoped_ptr<char[]> buf(new char[file_len]);
468 if (!file->Read(buf.get(), file_len, 0))
469 return false;
470 }
471
472 ScopedFlush flush(file);
473 DCHECK(!block_files_[index]);
474 file.swap(&block_files_[index]);
475 return true;
476 }
477
478 bool BlockFiles::GrowBlockFile(MappedFile* file, BlockFileHeader* header) { 206 bool BlockFiles::GrowBlockFile(MappedFile* file, BlockFileHeader* header) {
479 if (kMaxBlocks == header->max_entries) 207 if (kMaxBlocks == header->max_entries)
480 return false; 208 return false;
481 209
482 ScopedFlush flush(file); 210 ScopedFlush flush(file);
483 DCHECK(!header->empty[3]); 211 DCHECK(!header->empty[3]);
484 int new_size = header->max_entries + 1024; 212 int new_size = header->max_entries + 1024;
485 if (new_size > kMaxBlocks) 213 if (new_size > kMaxBlocks)
486 new_size = kMaxBlocks; 214 new_size = kMaxBlocks;
487 215
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
521 } 249 }
522 250
523 if (!GrowBlockFile(file, header)) 251 if (!GrowBlockFile(file, header))
524 return NULL; 252 return NULL;
525 break; 253 break;
526 } 254 }
527 HISTOGRAM_TIMES("DiskCache.GetFileForNewBlock", TimeTicks::Now() - start); 255 HISTOGRAM_TIMES("DiskCache.GetFileForNewBlock", TimeTicks::Now() - start);
528 return file; 256 return file;
529 } 257 }
530 258
531 MappedFile* BlockFiles::NextFile(MappedFile* file) {
532 ScopedFlush flush(file);
533 BlockFileHeader* header = reinterpret_cast<BlockFileHeader*>(file->buffer());
534 int new_file = header->next_file;
535 if (!new_file) {
536 // RANKINGS is not reported as a type for small entries, but we may be
537 // extending the rankings block file.
538 FileType type = Addr::RequiredFileType(header->entry_size);
539 if (header->entry_size == Addr::BlockSizeForFileType(RANKINGS))
540 type = RANKINGS;
541
542 new_file = CreateNextBlockFile(type);
543 if (!new_file)
544 return NULL;
545
546 FileLock lock(header);
547 header->next_file = new_file;
548 }
549
550 // Only the block_file argument is relevant for what we want.
551 Addr address(BLOCK_256, 1, new_file, 0);
552 return GetFile(address);
553 }
554
555 int BlockFiles::CreateNextBlockFile(FileType block_type) {
556 for (int i = kFirstAdditionalBlockFile; i <= kMaxBlockFile; i++) {
557 if (CreateBlockFile(i, block_type, false))
558 return i;
559 }
560 return 0;
561 }
562
563 // We walk the list of files for this particular block type, deleting the ones
564 // that are empty.
565 bool BlockFiles::RemoveEmptyFile(FileType block_type) {
566 MappedFile* file = block_files_[block_type - 1];
567 BlockFileHeader* header = reinterpret_cast<BlockFileHeader*>(file->buffer());
568
569 while (header->next_file) {
570 // Only the block_file argument is relevant for what we want.
571 Addr address(BLOCK_256, 1, header->next_file, 0);
572 MappedFile* next_file = GetFile(address);
573 if (!next_file)
574 return false;
575
576 BlockFileHeader* next_header =
577 reinterpret_cast<BlockFileHeader*>(next_file->buffer());
578 if (!next_header->num_entries) {
579 DCHECK_EQ(next_header->entry_size, header->entry_size);
580 // Delete next_file and remove it from the chain.
581 int file_index = header->next_file;
582 header->next_file = next_header->next_file;
583 DCHECK(block_files_.size() >= static_cast<unsigned int>(file_index));
584 file->Flush();
585
586 // We get a new handle to the file and release the old one so that the
587 // file gets unmmaped... so we can delete it.
588 base::FilePath name = Name(file_index);
589 scoped_refptr<File> this_file(new File(false));
590 this_file->Init(name);
591 block_files_[file_index]->Release();
592 block_files_[file_index] = NULL;
593
594 int failure = DeleteCacheFile(name) ? 0 : 1;
595 UMA_HISTOGRAM_COUNTS("DiskCache.DeleteFailed2", failure);
596 if (failure)
597 LOG(ERROR) << "Failed to delete " << name.value() << " from the cache.";
598 continue;
599 }
600
601 header = next_header;
602 file = next_file;
603 }
604 return true;
605 }
606
607 // Note that we expect to be called outside of a FileLock... however, we cannot 259 // Note that we expect to be called outside of a FileLock... however, we cannot
608 // DCHECK on header->updating because we may be fixing a crash. 260 // DCHECK on header->updating because we may be fixing a crash.
609 bool BlockFiles::FixBlockFileHeader(MappedFile* file) { 261 bool BlockFiles::FixBlockFileHeader(MappedFile* file) {
610 ScopedFlush flush(file); 262 ScopedFlush flush(file);
611 BlockFileHeader* header = reinterpret_cast<BlockFileHeader*>(file->buffer()); 263 BlockFileHeader* header = reinterpret_cast<BlockFileHeader*>(file->buffer());
612 int file_size = static_cast<int>(file->GetLength()); 264 int file_size = static_cast<int>(file->GetLength());
613 if (file_size < static_cast<int>(sizeof(*header))) 265 if (file_size < static_cast<int>(sizeof(*header)))
614 return false; // file_size > 2GB is also an error. 266 return false; // file_size > 2GB is also an error.
615 267
616 const int kMinBlockSize = 36; 268 const int kMinBlockSize = 36;
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
670 *used_count += used; 322 *used_count += used;
671 323
672 if (!header->next_file) 324 if (!header->next_file)
673 break; 325 break;
674 index = header->next_file; 326 index = header->next_file;
675 } 327 }
676 if (max_blocks) 328 if (max_blocks)
677 *load = *used_count * 100 / max_blocks; 329 *load = *used_count * 100 / max_blocks;
678 } 330 }
679 331
680 base::FilePath BlockFiles::Name(int index) {
681 // The file format allows for 256 files.
682 DCHECK(index < 256 || index >= 0);
683 std::string tmp = base::StringPrintf("%s%d", kBlockName, index);
684 return path_.AppendASCII(tmp);
685 }
686
687 } // namespace disk_cache 332 } // namespace disk_cache
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698