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

Side by Side Diff: net/disk_cache/blockfile/backend_worker_v3.cc

Issue 1910023002: Remove partial blockfile v3 disk_cache implementation. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 8 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
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "net/disk_cache/blockfile/backend_worker_v3.h"
6
7 #include <stdint.h>
8
9 #include <limits>
10
11 #include "base/bind.h"
12 #include "base/bind_helpers.h"
13 #include "base/files/file_path.h"
14 #include "base/files/file_util.h"
15 #include "base/message_loop/message_loop.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/time/time.h"
19 #include "base/timer/timer.h"
20 #include "net/base/net_errors.h"
21 #include "net/disk_cache/blockfile/errors.h"
22 #include "net/disk_cache/blockfile/experiments.h"
23 #include "net/disk_cache/blockfile/file.h"
24
25 using base::Time;
26 using base::TimeDelta;
27 using base::TimeTicks;
28
29 namespace {
30
31 #if defined(V3_NOT_JUST_YET_READY)
32
33 const char kIndexName[] = "index";
34
35 // Seems like ~240 MB correspond to less than 50k entries for 99% of the people.
36 // Note that the actual target is to keep the index table load factor under 55%
37 // for most users.
38 const int k64kEntriesStore = 240 * 1000 * 1000;
39 const int kBaseTableLen = 64 * 1024;
40 const int kDefaultCacheSize = 80 * 1024 * 1024;
41
42 // Avoid trimming the cache for the first 5 minutes (10 timer ticks).
43 const int kTrimDelay = 10;
44
45 int DesiredIndexTableLen(int32_t storage_size) {
46 if (storage_size <= k64kEntriesStore)
47 return kBaseTableLen;
48 if (storage_size <= k64kEntriesStore * 2)
49 return kBaseTableLen * 2;
50 if (storage_size <= k64kEntriesStore * 4)
51 return kBaseTableLen * 4;
52 if (storage_size <= k64kEntriesStore * 8)
53 return kBaseTableLen * 8;
54
55 // The biggest storage_size for int32_t requires a 4 MB table.
56 return kBaseTableLen * 16;
57 }
58
59 int MaxStorageSizeForTable(int table_len) {
60 return table_len * (k64kEntriesStore / kBaseTableLen);
61 }
62
63 size_t GetIndexSize(int table_len) {
64 size_t table_size = sizeof(disk_cache::CacheAddr) * table_len;
65 return sizeof(disk_cache::IndexHeader) + table_size;
66 }
67
68 // ------------------------------------------------------------------------
69
70 // Sets group for the current experiment. Returns false if the files should be
71 // discarded.
72 bool InitExperiment(disk_cache::IndexHeader* header, bool cache_created) {
73 if (header->experiment == disk_cache::EXPERIMENT_OLD_FILE1 ||
74 header->experiment == disk_cache::EXPERIMENT_OLD_FILE2) {
75 // Discard current cache.
76 return false;
77 }
78
79 if (base::FieldTrialList::FindFullName("SimpleCacheTrial") ==
80 "ExperimentControl") {
81 if (cache_created) {
82 header->experiment = disk_cache::EXPERIMENT_SIMPLE_CONTROL;
83 return true;
84 } else if (header->experiment != disk_cache::EXPERIMENT_SIMPLE_CONTROL) {
85 return false;
86 }
87 }
88
89 header->experiment = disk_cache::NO_EXPERIMENT;
90 return true;
91 }
92 #endif // defined(V3_NOT_JUST_YET_READY).
93
94 } // namespace
95
96 // ------------------------------------------------------------------------
97
98 namespace disk_cache {
99
100 BackendImplV3::Worker::Worker(
101 const base::FilePath& path,
102 const scoped_refptr<base::SingleThreadTaskRunner>& main_thread)
103 : path_(path), block_files_(path), init_(false) {
104 }
105
106 #if defined(V3_NOT_JUST_YET_READY)
107
108 int BackendImpl::SyncInit() {
109 #if defined(NET_BUILD_STRESS_CACHE)
110 // Start evictions right away.
111 up_ticks_ = kTrimDelay * 2;
112 #endif
113 DCHECK(!init_);
114 if (init_)
115 return net::ERR_FAILED;
116
117 bool create_files = false;
118 if (!InitBackingStore(&create_files)) {
119 ReportError(ERR_STORAGE_ERROR);
120 return net::ERR_FAILED;
121 }
122
123 num_refs_ = num_pending_io_ = max_refs_ = 0;
124 entry_count_ = byte_count_ = 0;
125
126 if (!restarted_) {
127 buffer_bytes_ = 0;
128 trace_object_ = TraceObject::GetTraceObject();
129 // Create a recurrent timer of 30 secs.
130 int timer_delay = unit_test_ ? 1000 : 30000;
131 timer_.reset(new base::RepeatingTimer());
132 timer_->Start(FROM_HERE, TimeDelta::FromMilliseconds(timer_delay), this,
133 &BackendImpl::OnStatsTimer);
134 }
135
136 init_ = true;
137 Trace("Init");
138
139 if (data_->header.experiment != NO_EXPERIMENT &&
140 cache_type_ != net::DISK_CACHE) {
141 // No experiment for other caches.
142 return net::ERR_FAILED;
143 }
144
145 if (!(user_flags_ & kNoRandom)) {
146 // The unit test controls directly what to test.
147 new_eviction_ = (cache_type_ == net::DISK_CACHE);
148 }
149
150 if (!CheckIndex()) {
151 ReportError(ERR_INIT_FAILED);
152 return net::ERR_FAILED;
153 }
154
155 if (!restarted_ && (create_files || !data_->header.num_entries))
156 ReportError(ERR_CACHE_CREATED);
157
158 if (!(user_flags_ & kNoRandom) && cache_type_ == net::DISK_CACHE &&
159 !InitExperiment(&data_->header, create_files)) {
160 return net::ERR_FAILED;
161 }
162
163 // We don't care if the value overflows. The only thing we care about is that
164 // the id cannot be zero, because that value is used as "not dirty".
165 // Increasing the value once per second gives us many years before we start
166 // having collisions.
167 data_->header.this_id++;
168 if (!data_->header.this_id)
169 data_->header.this_id++;
170
171 bool previous_crash = (data_->header.crash != 0);
172 data_->header.crash = 1;
173
174 if (!block_files_.Init(create_files))
175 return net::ERR_FAILED;
176
177 // We want to minimize the changes to cache for an AppCache.
178 if (cache_type() == net::APP_CACHE) {
179 DCHECK(!new_eviction_);
180 read_only_ = true;
181 } else if (cache_type() == net::SHADER_CACHE) {
182 DCHECK(!new_eviction_);
183 }
184
185 eviction_.Init(this);
186
187 // stats_ and rankings_ may end up calling back to us so we better be enabled.
188 disabled_ = false;
189 if (!InitStats())
190 return net::ERR_FAILED;
191
192 disabled_ = !rankings_.Init(this, new_eviction_);
193
194 #if defined(STRESS_CACHE_EXTENDED_VALIDATION)
195 trace_object_->EnableTracing(false);
196 int sc = SelfCheck();
197 if (sc < 0 && sc != ERR_NUM_ENTRIES_MISMATCH)
198 NOTREACHED();
199 trace_object_->EnableTracing(true);
200 #endif
201
202 if (previous_crash) {
203 ReportError(ERR_PREVIOUS_CRASH);
204 } else if (!restarted_) {
205 ReportError(ERR_NO_ERROR);
206 }
207
208 FlushIndex();
209
210 return disabled_ ? net::ERR_FAILED : net::OK;
211 }
212
213 void BackendImpl::PrepareForRestart() {
214 // Reset the mask_ if it was not given by the user.
215 if (!(user_flags_ & kMask))
216 mask_ = 0;
217
218 if (!(user_flags_ & kNewEviction))
219 new_eviction_ = false;
220
221 disabled_ = true;
222 data_->header.crash = 0;
223 index_->Flush();
224 index_ = NULL;
225 data_ = NULL;
226 block_files_.CloseFiles();
227 rankings_.Reset();
228 init_ = false;
229 restarted_ = true;
230 }
231
232 BackendImpl::~BackendImpl() {
233 if (user_flags_ & kNoRandom) {
234 // This is a unit test, so we want to be strict about not leaking entries
235 // and completing all the work.
236 background_queue_.WaitForPendingIO();
237 } else {
238 // This is most likely not a test, so we want to do as little work as
239 // possible at this time, at the price of leaving dirty entries behind.
240 background_queue_.DropPendingIO();
241 }
242
243 if (background_queue_.BackgroundIsCurrentThread()) {
244 // Unit tests may use the same thread for everything.
245 CleanupCache();
246 } else {
247 background_queue_.background_thread()->PostTask(
248 FROM_HERE, base::Bind(&FinalCleanupCallback, base::Unretained(this)));
249 // http://crbug.com/74623
250 base::ThreadRestrictions::ScopedAllowWait allow_wait;
251 done_.Wait();
252 }
253 }
254
255 void BackendImpl::CleanupCache() {
256 Trace("Backend Cleanup");
257 eviction_.Stop();
258 timer_.reset();
259
260 if (init_) {
261 StoreStats();
262 if (data_)
263 data_->header.crash = 0;
264
265 if (user_flags_ & kNoRandom) {
266 // This is a net_unittest, verify that we are not 'leaking' entries.
267 File::WaitForPendingIO(&num_pending_io_);
268 DCHECK(!num_refs_);
269 } else {
270 File::DropPendingIO();
271 }
272 }
273 block_files_.CloseFiles();
274 FlushIndex();
275 index_ = NULL;
276 ptr_factory_.InvalidateWeakPtrs();
277 done_.Signal();
278 }
279
280 base::FilePath BackendImpl::GetFileName(Addr address) const {
281 if (!address.is_separate_file() || !address.is_initialized()) {
282 NOTREACHED();
283 return base::FilePath();
284 }
285
286 std::string tmp = base::StringPrintf("f_%06x", address.FileNumber());
287 return path_.AppendASCII(tmp);
288 }
289
290 // We just created a new file so we're going to write the header and set the
291 // file length to include the hash table (zero filled).
292 bool BackendImpl::CreateBackingStore(disk_cache::File* file) {
293 AdjustMaxCacheSize(0);
294
295 IndexHeader header;
296 header.table_len = DesiredIndexTableLen(max_size_);
297
298 // We need file version 2.1 for the new eviction algorithm.
299 if (new_eviction_)
300 header.version = 0x20001;
301
302 header.create_time = Time::Now().ToInternalValue();
303
304 if (!file->Write(&header, sizeof(header), 0))
305 return false;
306
307 return file->SetLength(GetIndexSize(header.table_len));
308 }
309
310 bool BackendImpl::InitBackingStore(bool* file_created) {
311 if (!base::CreateDirectory(path_))
312 return false;
313
314 base::FilePath index_name = path_.AppendASCII(kIndexName);
315
316 int flags = base::PLATFORM_FILE_READ |
317 base::PLATFORM_FILE_WRITE |
318 base::PLATFORM_FILE_OPEN_ALWAYS |
319 base::PLATFORM_FILE_EXCLUSIVE_WRITE;
320 scoped_refptr<disk_cache::File> file(new disk_cache::File(
321 base::CreatePlatformFile(index_name, flags, file_created, NULL)));
322
323 if (!file->IsValid())
324 return false;
325
326 bool ret = true;
327 if (*file_created)
328 ret = CreateBackingStore(file.get());
329
330 file = NULL;
331 if (!ret)
332 return false;
333
334 index_ = new MappedFile();
335 data_ = reinterpret_cast<Index*>(index_->Init(index_name, 0));
336 if (!data_) {
337 LOG(ERROR) << "Unable to map Index file";
338 return false;
339 }
340
341 if (index_->GetLength() < sizeof(Index)) {
342 // We verify this again on CheckIndex() but it's easier to make sure now
343 // that the header is there.
344 LOG(ERROR) << "Corrupt Index file";
345 return false;
346 }
347
348 return true;
349 }
350
351 void BackendImpl::ReportError(int error) {
352 STRESS_DCHECK(!error || error == ERR_PREVIOUS_CRASH ||
353 error == ERR_CACHE_CREATED);
354
355 // We transmit positive numbers, instead of direct error codes.
356 DCHECK_LE(error, 0);
357 CACHE_UMA(CACHE_ERROR, "Error", 0, error * -1);
358 }
359
360
361 bool BackendImpl::CheckIndex() {
362 DCHECK(data_);
363
364 size_t current_size = index_->GetLength();
365 if (current_size < sizeof(Index)) {
366 LOG(ERROR) << "Corrupt Index file";
367 return false;
368 }
369
370 if (new_eviction_) {
371 // We support versions 2.0 and 2.1, upgrading 2.0 to 2.1.
372 if (kIndexMagic != data_->header.magic ||
373 kCurrentVersion >> 16 != data_->header.version >> 16) {
374 LOG(ERROR) << "Invalid file version or magic";
375 return false;
376 }
377 if (kCurrentVersion == data_->header.version) {
378 // We need file version 2.1 for the new eviction algorithm.
379 UpgradeTo2_1();
380 }
381 } else {
382 if (kIndexMagic != data_->header.magic ||
383 kCurrentVersion != data_->header.version) {
384 LOG(ERROR) << "Invalid file version or magic";
385 return false;
386 }
387 }
388
389 if (!data_->header.table_len) {
390 LOG(ERROR) << "Invalid table size";
391 return false;
392 }
393
394 if (current_size < GetIndexSize(data_->header.table_len) ||
395 data_->header.table_len & (kBaseTableLen - 1)) {
396 LOG(ERROR) << "Corrupt Index file";
397 return false;
398 }
399
400 AdjustMaxCacheSize(data_->header.table_len);
401
402 #if !defined(NET_BUILD_STRESS_CACHE)
403 if (data_->header.num_bytes < 0 ||
404 (max_size_ < std::numeric_limits<int32_t>::max() - kDefaultCacheSize &&
405 data_->header.num_bytes > max_size_ + kDefaultCacheSize)) {
406 LOG(ERROR) << "Invalid cache (current) size";
407 return false;
408 }
409 #endif
410
411 if (data_->header.num_entries < 0) {
412 LOG(ERROR) << "Invalid number of entries";
413 return false;
414 }
415
416 if (!mask_)
417 mask_ = data_->header.table_len - 1;
418
419 // Load the table into memory with a single read.
420 std::unique_ptr<char[]> buf(new char[current_size]);
421 return index_->Read(buf.get(), current_size, 0);
422 }
423
424 bool BackendImpl::InitStats() {
425 Addr address(data_->header.stats);
426 int size = stats_.StorageSize();
427
428 if (!address.is_initialized()) {
429 FileType file_type = Addr::RequiredFileType(size);
430 DCHECK_NE(file_type, EXTERNAL);
431 int num_blocks = Addr::RequiredBlocks(size, file_type);
432
433 if (!CreateBlock(file_type, num_blocks, &address))
434 return false;
435 return stats_.Init(NULL, 0, address);
436 }
437
438 if (!address.is_block_file()) {
439 NOTREACHED();
440 return false;
441 }
442
443 // Load the required data.
444 size = address.num_blocks() * address.BlockSize();
445 MappedFile* file = File(address);
446 if (!file)
447 return false;
448
449 std::unique_ptr<char[]> data(new char[size]);
450 size_t offset = address.start_block() * address.BlockSize() +
451 kBlockHeaderSize;
452 if (!file->Read(data.get(), size, offset))
453 return false;
454
455 if (!stats_.Init(data.get(), size, address))
456 return false;
457 if (cache_type_ == net::DISK_CACHE && ShouldReportAgain())
458 stats_.InitSizeHistogram();
459 return true;
460 }
461
462 #endif // defined(V3_NOT_JUST_YET_READY).
463
464 int BackendImplV3::Worker::Init(const CompletionCallback& callback) {
465 return net::ERR_FAILED;
466 }
467
468 BackendImplV3::Worker::~Worker() {
469 }
470
471 } // namespace disk_cache
OLDNEW
« no previous file with comments | « net/disk_cache/blockfile/backend_worker_v3.h ('k') | net/disk_cache/blockfile/block_bitmaps_v3.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698