diff options
author | ttuttle@chromium.org <ttuttle@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-09-23 05:33:06 +0000 |
---|---|---|
committer | ttuttle@chromium.org <ttuttle@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-09-23 05:33:06 +0000 |
commit | 66b858a8d6a1f2a16eb226ec1e3b00901570ca87 (patch) | |
tree | 5c2cd8664de82c973e4ef46449bce2fa2380bdd2 /net | |
parent | c2ac1dda33d812edcacdfd49c314bca13c5dd267 (diff) | |
download | chromium_src-66b858a8d6a1f2a16eb226ec1e3b00901570ca87.zip chromium_src-66b858a8d6a1f2a16eb226ec1e3b00901570ca87.tar.gz chromium_src-66b858a8d6a1f2a16eb226ec1e3b00901570ca87.tar.bz2 |
Simple Cache: Make stream 2 optional
Make the third stream optional, since it's usually empty.
BUG=280592
Review URL: https://chromiumcodereview.appspot.com/22511002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@224674 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r-- | net/disk_cache/entry_unittest.cc | 172 | ||||
-rw-r--r-- | net/disk_cache/simple/simple_entry_impl.cc | 20 | ||||
-rw-r--r-- | net/disk_cache/simple/simple_synchronous_entry.cc | 469 | ||||
-rw-r--r-- | net/disk_cache/simple/simple_synchronous_entry.h | 54 |
4 files changed, 539 insertions, 176 deletions
diff --git a/net/disk_cache/entry_unittest.cc b/net/disk_cache/entry_unittest.cc index 99c31a6..b8c9e66 100644 --- a/net/disk_cache/entry_unittest.cc +++ b/net/disk_cache/entry_unittest.cc @@ -64,6 +64,8 @@ class DiskCacheEntryTest : public DiskCacheTestWithCache { void DoomSparseEntry(); void PartialSparseEntry(); bool SimpleCacheMakeBadChecksumEntry(const std::string& key, int* data_size); + bool SimpleCacheThirdStreamFileExists(const char* key); + void SyncDoomEntry(const char* key); }; // This part of the test runs on the background thread. @@ -3606,4 +3608,174 @@ TEST_F(DiskCacheEntryTest, SimpleCacheCRCRewrite) { } } +bool DiskCacheEntryTest::SimpleCacheThirdStreamFileExists(const char* key) { + int third_stream_file_index = + disk_cache::simple_util::GetFileIndexFromStreamIndex(2); + base::FilePath third_stream_file_path = cache_path_.AppendASCII( + disk_cache::simple_util::GetFilenameFromKeyAndFileIndex( + key, third_stream_file_index)); + return PathExists(third_stream_file_path); +} + +void DiskCacheEntryTest::SyncDoomEntry(const char* key) { + net::TestCompletionCallback callback; + cache_->DoomEntry(key, callback.callback()); + callback.WaitForResult(); +} + +// Check that a newly-created entry with no third-stream writes omits the +// third stream file. +TEST_F(DiskCacheEntryTest, SimpleCacheOmittedThirdStream1) { + SetSimpleCacheMode(); + InitCache(); + + const int kHalfSize = 8; + const int kSize = kHalfSize * 2; + const char key[] = "key"; + scoped_refptr<net::IOBuffer> buffer1(new net::IOBuffer(kSize)); + scoped_refptr<net::IOBuffer> buffer2(new net::IOBuffer(kSize)); + CacheTestFillBuffer(buffer1->data(), kHalfSize, false); + + disk_cache::Entry* entry; + + // Create entry and close without writing: third stream file should be + // omitted, since the stream is empty. + ASSERT_EQ(net::OK, CreateEntry(key, &entry)); + entry->Close(); + EXPECT_FALSE(SimpleCacheThirdStreamFileExists(key)); + + SyncDoomEntry(key); + EXPECT_FALSE(SimpleCacheThirdStreamFileExists(key)); +} + +// Check that a newly-created entry with only a single zero-offset, zero-length +// write omits the third stream file. +TEST_F(DiskCacheEntryTest, SimpleCacheOmittedThirdStream2) { + SetSimpleCacheMode(); + InitCache(); + + const int kHalfSize = 8; + const int kSize = kHalfSize * 2; + const char key[] = "key"; + scoped_refptr<net::IOBuffer> buffer1(new net::IOBuffer(kSize)); + scoped_refptr<net::IOBuffer> buffer2(new net::IOBuffer(kSize)); + CacheTestFillBuffer(buffer1->data(), kHalfSize, false); + + disk_cache::Entry* entry; + int buf_len; + + // Create entry, write empty buffer to third stream, and close: third stream + // should still be omitted, since the entry ignores writes that don't modify + // data or change the length. + ASSERT_EQ(net::OK, CreateEntry(key, &entry)); + buf_len = WriteData(entry, 2, 0, buffer1, 0, true); + ASSERT_EQ(0, buf_len); + entry->Close(); + EXPECT_FALSE(SimpleCacheThirdStreamFileExists(key)); + + SyncDoomEntry(key); + EXPECT_FALSE(SimpleCacheThirdStreamFileExists(key)); +} + +// Check that we can read back data written to the third stream. +TEST_F(DiskCacheEntryTest, SimpleCacheOmittedThirdStream3) { + SetSimpleCacheMode(); + InitCache(); + + const int kHalfSize = 8; + const int kSize = kHalfSize * 2; + const char key[] = "key"; + scoped_refptr<net::IOBuffer> buffer1(new net::IOBuffer(kSize)); + scoped_refptr<net::IOBuffer> buffer2(new net::IOBuffer(kSize)); + CacheTestFillBuffer(buffer1->data(), kHalfSize, false); + + disk_cache::Entry* entry; + int buf_len; + + // Create entry, write data to third stream, and close: third stream should + // not be omitted, since it contains data. Re-open entry and ensure there + // are that many bytes in the third stream. + ASSERT_EQ(net::OK, CreateEntry(key, &entry)); + buf_len = WriteData(entry, 2, 0, buffer1, kHalfSize, true); + ASSERT_EQ(kHalfSize, buf_len); + entry->Close(); + EXPECT_TRUE(SimpleCacheThirdStreamFileExists(key)); + + ASSERT_EQ(net::OK, OpenEntry(key, &entry)); + buf_len = ReadData(entry, 2, 0, buffer2, kSize); + ASSERT_EQ(buf_len, kHalfSize); + // TODO: Compare data? + entry->Close(); + EXPECT_TRUE(SimpleCacheThirdStreamFileExists(key)); + + SyncDoomEntry(key); + EXPECT_FALSE(SimpleCacheThirdStreamFileExists(key)); +} + +// Check that we remove the third stream file upon opening an entry and finding +// the third stream empty. (This is the upgrade path for entries written +// before the third stream was optional.) +TEST_F(DiskCacheEntryTest, SimpleCacheOmittedThirdStream4) { + SetSimpleCacheMode(); + InitCache(); + + const int kHalfSize = 8; + const int kSize = kHalfSize * 2; + const char key[] = "key"; + scoped_refptr<net::IOBuffer> buffer1(new net::IOBuffer(kSize)); + scoped_refptr<net::IOBuffer> buffer2(new net::IOBuffer(kSize)); + CacheTestFillBuffer(buffer1->data(), kHalfSize, false); + + disk_cache::Entry* entry; + int buf_len; + + // Create entry, write data to third stream, truncate third stream back to + // empty, and close: third stream will not initially be omitted, since entry + // creates the file when the first significant write comes in, and only + // removes it on open if it is empty. Reopen, ensure that the file is + // deleted, and that there's no data in the third stream. + ASSERT_EQ(net::OK, CreateEntry(key, &entry)); + buf_len = WriteData(entry, 2, 0, buffer1, kHalfSize, true); + ASSERT_EQ(kHalfSize, buf_len); + buf_len = WriteData(entry, 2, 0, buffer1, 0, true); + ASSERT_EQ(0, buf_len); + entry->Close(); + EXPECT_TRUE(SimpleCacheThirdStreamFileExists(key)); + + ASSERT_EQ(net::OK, OpenEntry(key, &entry)); + EXPECT_FALSE(SimpleCacheThirdStreamFileExists(key)); + buf_len = ReadData(entry, 2, 0, buffer2, kSize); + ASSERT_EQ(0, buf_len); + entry->Close(); + EXPECT_FALSE(SimpleCacheThirdStreamFileExists(key)); + + SyncDoomEntry(key); + EXPECT_FALSE(SimpleCacheThirdStreamFileExists(key)); +} + +// Check that we don't accidentally create the third stream file once the entry +// has been doomed. +TEST_F(DiskCacheEntryTest, SimpleCacheOmittedThirdStream5) { + SetSimpleCacheMode(); + InitCache(); + + const int kHalfSize = 8; + const int kSize = kHalfSize * 2; + const char key[] = "key"; + scoped_refptr<net::IOBuffer> buffer1(new net::IOBuffer(kSize)); + scoped_refptr<net::IOBuffer> buffer2(new net::IOBuffer(kSize)); + CacheTestFillBuffer(buffer1->data(), kHalfSize, false); + + disk_cache::Entry* entry; + + // Create entry, doom entry, write data to third stream, and close: third + // stream should not exist. (Note: We don't care if the write fails, just + // that it doesn't cause the file to be created on disk.) + ASSERT_EQ(net::OK, CreateEntry(key, &entry)); + entry->Doom(); + WriteData(entry, 2, 0, buffer1, kHalfSize, true); + entry->Close(); + EXPECT_FALSE(SimpleCacheThirdStreamFileExists(key)); +} + #endif // defined(OS_POSIX) diff --git a/net/disk_cache/simple/simple_entry_impl.cc b/net/disk_cache/simple/simple_entry_impl.cc index 33084b6..3d2bc22 100644 --- a/net/disk_cache/simple/simple_entry_impl.cc +++ b/net/disk_cache/simple/simple_entry_impl.cc @@ -50,7 +50,8 @@ enum WriteResult { WRITE_RESULT_OVER_MAX_SIZE = 2, WRITE_RESULT_BAD_STATE = 3, WRITE_RESULT_SYNC_WRITE_FAILURE = 4, - WRITE_RESULT_MAX = 5, + WRITE_RESULT_FAST_EMPTY_RETURN = 5, + WRITE_RESULT_MAX = 6, }; // Used in histograms, please only add entries at the end. @@ -70,7 +71,7 @@ void RecordReadResult(net::CacheType cache_type, ReadResult result) { void RecordWriteResult(net::CacheType cache_type, WriteResult result) { SIMPLE_CACHE_UMA(ENUMERATION, - "WriteResult", cache_type, result, WRITE_RESULT_MAX); + "WriteResult2", cache_type, result, WRITE_RESULT_MAX); } // TODO(ttuttle): Consider removing this once we have a good handle on header @@ -887,6 +888,18 @@ void SimpleEntryImpl::WriteDataInternal(int stream_index, return; } + // Ignore zero-length writes that do not change the file size. + if (buf_len == 0) { + int32 data_size = data_size_[stream_index]; + if (truncate ? (offset == data_size) : (offset <= data_size)) { + RecordWriteResult(cache_type_, WRITE_RESULT_FAST_EMPTY_RETURN); + if (!callback.is_null()) { + MessageLoopProxy::current()->PostTask(FROM_HERE, base::Bind( + callback, 0)); + } + return; + } + } state_ = STATE_IO_PENDING; if (!doomed_ && backend_.get()) backend_->index()->UseIfExists(entry_hash_); @@ -917,7 +930,8 @@ void SimpleEntryImpl::WriteDataInternal(int stream_index, Closure task = base::Bind(&SimpleSynchronousEntry::WriteData, base::Unretained(synchronous_entry_), SimpleSynchronousEntry::EntryOperationData( - stream_index, offset, buf_len, truncate), + stream_index, offset, buf_len, truncate, + doomed_), make_scoped_refptr(buf), entry_stat.get(), result.get()); diff --git a/net/disk_cache/simple/simple_synchronous_entry.cc b/net/disk_cache/simple/simple_synchronous_entry.cc index 62d4214..38e8a3c 100644 --- a/net/disk_cache/simple/simple_synchronous_entry.cc +++ b/net/disk_cache/simple/simple_synchronous_entry.cc @@ -31,6 +31,7 @@ using base::PlatformFileError; using base::PlatformFileInfo; using base::PLATFORM_FILE_CREATE; using base::PLATFORM_FILE_ERROR_EXISTS; +using base::PLATFORM_FILE_ERROR_NOT_FOUND; using base::PLATFORM_FILE_OK; using base::PLATFORM_FILE_OPEN; using base::PLATFORM_FILE_READ; @@ -56,20 +57,14 @@ enum OpenEntryResult { }; // Used in histograms, please only add entries at the end. -enum CreateEntryResult { - CREATE_ENTRY_SUCCESS = 0, - CREATE_ENTRY_PLATFORM_FILE_ERROR = 1, - CREATE_ENTRY_CANT_WRITE_HEADER = 2, - CREATE_ENTRY_CANT_WRITE_KEY = 3, - CREATE_ENTRY_MAX = 4, -}; - -// Used in histograms, please only add entries at the end. enum WriteResult { WRITE_RESULT_SUCCESS = 0, WRITE_RESULT_PRETRUNCATE_FAILURE, WRITE_RESULT_WRITE_FAILURE, WRITE_RESULT_TRUNCATE_FAILURE, + WRITE_RESULT_LAZY_STREAM_ENTRY_DOOMED, + WRITE_RESULT_LAZY_CREATE_FAILURE, + WRITE_RESULT_LAZY_INITIALIZE_FAILURE, WRITE_RESULT_MAX, }; @@ -105,23 +100,6 @@ void RecordSyncOpenResult(net::CacheType cache_type, } } -void RecordSyncCreateResult(net::CacheType cache_type, - CreateEntryResult result, - bool had_index) { - DCHECK_GT(CREATE_ENTRY_MAX, result); - SIMPLE_CACHE_UMA(ENUMERATION, - "SyncCreateResult", cache_type, result, CREATE_ENTRY_MAX); - if (had_index) { - SIMPLE_CACHE_UMA(ENUMERATION, - "SyncCreateResult_WithIndex", cache_type, - result, CREATE_ENTRY_MAX); - } else { - SIMPLE_CACHE_UMA(ENUMERATION, - "SyncCreateResult_WithoutIndex", cache_type, - result, CREATE_ENTRY_MAX); - } -} - void RecordWriteResult(net::CacheType cache_type, WriteResult result) { SIMPLE_CACHE_UMA(ENUMERATION, "SyncWriteResult", cache_type, result, WRITE_RESULT_MAX); @@ -138,6 +116,12 @@ void RecordCloseResult(net::CacheType cache_type, CloseResult result) { "SyncCloseResult", cache_type, result, WRITE_RESULT_MAX); } +bool CanOmitEmptyFile(int file_index) { + DCHECK_LE(0, file_index); + DCHECK_GT(disk_cache::kSimpleEntryFileCount, file_index); + return file_index == disk_cache::simple_util::GetFileIndexFromStreamIndex(2); +} + } // namespace namespace disk_cache { @@ -219,11 +203,13 @@ SimpleSynchronousEntry::EntryOperationData::EntryOperationData(int index_p, SimpleSynchronousEntry::EntryOperationData::EntryOperationData(int index_p, int offset_p, int buf_len_p, - bool truncate_p) + bool truncate_p, + bool doomed_p) : index(index_p), offset(offset_p), buf_len(buf_len_p), - truncate(truncate_p) {} + truncate(truncate_p), + doomed(doomed_p) {} // static void SimpleSynchronousEntry::OpenEntry( @@ -272,23 +258,6 @@ void SimpleSynchronousEntry::CreateEntry( out_results->sync_entry = sync_entry; } -// TODO(gavinp): Move this function to its correct location in this .cc file. -// static -bool SimpleSynchronousEntry::DeleteFilesForEntryHash( - const FilePath& path, - const uint64 entry_hash) { - bool result = true; - for (int i = 0; i < kSimpleEntryFileCount; ++i) { - FilePath to_delete = - path.AppendASCII(GetFilenameFromEntryHashAndFileIndex(entry_hash, i)); - if (!base::DeleteFile(to_delete, false)) { - result = false; - DLOG(ERROR) << "Could not delete " << to_delete.MaybeAsASCII(); - } - } - return result; -} - // static int SimpleSynchronousEntry::DoomEntry( const FilePath& path, @@ -317,6 +286,10 @@ void SimpleSynchronousEntry::ReadData(const EntryOperationData& in_entry_op, const int64 file_offset = entry_stat->GetOffsetInFile(key_, in_entry_op.offset, in_entry_op.index); int file_index = GetFileIndexFromStreamIndex(in_entry_op.index); + // Zero-length reads and reads to the empty streams of omitted files should + // be handled in the SimpleEntryImpl. + DCHECK_LT(0, in_entry_op.buf_len); + DCHECK(!empty_file_omitted_[file_index]); int bytes_read = ReadPlatformFile( files_[file_index], file_offset, out_buf->data(), in_entry_op.buf_len); if (bytes_read > 0) { @@ -336,17 +309,46 @@ void SimpleSynchronousEntry::ReadData(const EntryOperationData& in_entry_op, void SimpleSynchronousEntry::WriteData(const EntryOperationData& in_entry_op, net::IOBuffer* in_buf, SimpleEntryStat* out_entry_stat, - int* out_result) const { + int* out_result) { DCHECK(initialized_); DCHECK_NE(0, in_entry_op.index); int index = in_entry_op.index; int file_index = GetFileIndexFromStreamIndex(index); int offset = in_entry_op.offset; int buf_len = in_entry_op.buf_len; - int truncate = in_entry_op.truncate; + bool truncate = in_entry_op.truncate; + bool doomed = in_entry_op.doomed; const int64 file_offset = out_entry_stat->GetOffsetInFile( key_, in_entry_op.offset, in_entry_op.index); bool extending_by_write = offset + buf_len > out_entry_stat->data_size(index); + + if (empty_file_omitted_[file_index]) { + // Don't create a new file if the entry has been doomed, to avoid it being + // mixed up with a newly-created entry with the same key. + if (doomed) { + DLOG(WARNING) << "Rejecting write to lazily omitted stream " + << in_entry_op.index << " of doomed cache entry."; + RecordWriteResult(cache_type_, WRITE_RESULT_LAZY_STREAM_ENTRY_DOOMED); + *out_result = net::ERR_CACHE_WRITE_FAILURE; + return; + } + PlatformFileError error; + if (!MaybeCreateFile(file_index, FILE_REQUIRED, &error)) { + RecordWriteResult(cache_type_, WRITE_RESULT_LAZY_CREATE_FAILURE); + Doom(); + *out_result = net::ERR_CACHE_WRITE_FAILURE; + return; + } + CreateEntryResult result; + if (!InitializeCreatedFile(file_index, &result)) { + RecordWriteResult(cache_type_, WRITE_RESULT_LAZY_INITIALIZE_FAILURE); + Doom(); + *out_result = net::ERR_CACHE_WRITE_FAILURE; + return; + } + } + DCHECK(!empty_file_omitted_[file_index]); + if (extending_by_write) { // The EOF record and the eventual stream afterward need to be zeroed out. const int64 file_eof_offset = @@ -431,20 +433,24 @@ void SimpleSynchronousEntry::Close( for (std::vector<CRCRecord>::const_iterator it = crc32s_to_write->begin(); it != crc32s_to_write->end(); ++it) { + const int stream_index = it->index; + const int file_index = GetFileIndexFromStreamIndex(stream_index); + if (empty_file_omitted_[file_index]) + continue; + SimpleFileEOF eof_record; - int index = it->index; - eof_record.stream_size = entry_stat.data_size(index); + eof_record.stream_size = entry_stat.data_size(stream_index); eof_record.final_magic_number = kSimpleFinalMagicNumber; eof_record.flags = 0; if (it->has_crc32) eof_record.flags |= SimpleFileEOF::FLAG_HAS_CRC32; eof_record.data_crc32 = it->data_crc32; - int file_index = GetFileIndexFromStreamIndex(index); - int eof_offset = entry_stat.GetEOFOffsetInFile(key_, index); + int eof_offset = entry_stat.GetEOFOffsetInFile(key_, stream_index); // If stream 0 changed size, the file needs to be resized, otherwise the // next open will yield wrong stream sizes. On stream 1 and stream 2 proper // resizing of the file is handled in SimpleSynchronousEntry::WriteData(). - if (index == 0 && !TruncatePlatformFile(files_[file_index], eof_offset)) { + if (stream_index == 0 && + !TruncatePlatformFile(files_[file_index], eof_offset)) { RecordCloseResult(cache_type_, CLOSE_RESULT_WRITE_FAILURE); DLOG(INFO) << "Could not truncate stream 0 file."; Doom(); @@ -461,6 +467,9 @@ void SimpleSynchronousEntry::Close( } } for (int i = 0; i < kSimpleEntryFileCount; ++i) { + if (empty_file_omitted_[i]) + continue; + bool did_close_file = ClosePlatformFile(files_[i]); DCHECK(did_close_file); const int64 file_size = entry_stat.GetFileSize(key_, i); @@ -472,6 +481,7 @@ void SimpleSynchronousEntry::Close( "LastClusterLossPercent", cache_type_, cluster_loss * 100 / (cluster_loss + file_size)); } + RecordCloseResult(cache_type_, CLOSE_RESULT_SUCCESS); have_open_files_ = false; delete this; @@ -489,6 +499,7 @@ SimpleSynchronousEntry::SimpleSynchronousEntry(net::CacheType cache_type, initialized_(false) { for (int i = 0; i < kSimpleEntryFileCount; ++i) { files_[i] = kInvalidPlatformFileValue; + empty_file_omitted_[i] = false; } } @@ -498,133 +509,191 @@ SimpleSynchronousEntry::~SimpleSynchronousEntry() { CloseFiles(); } -bool SimpleSynchronousEntry::OpenOrCreateFiles( - bool create, +bool SimpleSynchronousEntry::MaybeOpenFile( + int file_index, + PlatformFileError* out_error) { + DCHECK(out_error); + + FilePath filename = GetFilenameFromFileIndex(file_index); + int flags = PLATFORM_FILE_OPEN | PLATFORM_FILE_READ | PLATFORM_FILE_WRITE; + files_[file_index] = CreatePlatformFile(filename, flags, NULL, out_error); + + if (CanOmitEmptyFile(file_index) && + *out_error == PLATFORM_FILE_ERROR_NOT_FOUND) { + empty_file_omitted_[file_index] = true; + return true; + } + + return *out_error == PLATFORM_FILE_OK; +} + +bool SimpleSynchronousEntry::MaybeCreateFile( + int file_index, + FileRequired file_required, + PlatformFileError* out_error) { + DCHECK(out_error); + + if (CanOmitEmptyFile(file_index) && file_required == FILE_NOT_REQUIRED) { + empty_file_omitted_[file_index] = true; + return true; + } + + FilePath filename = GetFilenameFromFileIndex(file_index); + int flags = PLATFORM_FILE_CREATE | PLATFORM_FILE_READ | PLATFORM_FILE_WRITE; + files_[file_index] = CreatePlatformFile(filename, flags, NULL, out_error); + + empty_file_omitted_[file_index] = false; + + return *out_error == PLATFORM_FILE_OK; +} + +bool SimpleSynchronousEntry::OpenFiles( bool had_index, SimpleEntryStat* out_entry_stat) { for (int i = 0; i < kSimpleEntryFileCount; ++i) { - FilePath filename = - path_.AppendASCII(GetFilenameFromEntryHashAndFileIndex(entry_hash_, i)); - int flags = PLATFORM_FILE_READ | PLATFORM_FILE_WRITE; - if (create) - flags |= PLATFORM_FILE_CREATE; - else - flags |= PLATFORM_FILE_OPEN; PlatformFileError error; - files_[i] = CreatePlatformFile(filename, flags, NULL, &error); - if (error != PLATFORM_FILE_OK) { + if (!MaybeOpenFile(i, &error)) { // TODO(ttuttle,gavinp): Remove one each of these triplets of histograms. // We can calculate the third as the sum or difference of the other two. - if (create) { - RecordSyncCreateResult( - cache_type_, CREATE_ENTRY_PLATFORM_FILE_ERROR, had_index); + RecordSyncOpenResult( + cache_type_, OPEN_ENTRY_PLATFORM_FILE_ERROR, had_index); + SIMPLE_CACHE_UMA(ENUMERATION, + "SyncOpenPlatformFileError", cache_type_, + -error, -base::PLATFORM_FILE_ERROR_MAX); + if (had_index) { SIMPLE_CACHE_UMA(ENUMERATION, - "SyncCreatePlatformFileError", cache_type_, + "SyncOpenPlatformFileError_WithIndex", cache_type_, -error, -base::PLATFORM_FILE_ERROR_MAX); - if (had_index) { - SIMPLE_CACHE_UMA(ENUMERATION, - "SyncCreatePlatformFileError_WithIndex", cache_type_, - -error, -base::PLATFORM_FILE_ERROR_MAX); - } else { - SIMPLE_CACHE_UMA(ENUMERATION, - "SyncCreatePlatformFileError_WithoutIndex", - cache_type_, - -error, -base::PLATFORM_FILE_ERROR_MAX); - } } else { - RecordSyncOpenResult( - cache_type_, OPEN_ENTRY_PLATFORM_FILE_ERROR, had_index); SIMPLE_CACHE_UMA(ENUMERATION, - "SyncOpenPlatformFileError", cache_type_, + "SyncOpenPlatformFileError_WithoutIndex", + cache_type_, -error, -base::PLATFORM_FILE_ERROR_MAX); - if (had_index) { - SIMPLE_CACHE_UMA(ENUMERATION, - "SyncOpenPlatformFileError_WithIndex", cache_type_, - -error, -base::PLATFORM_FILE_ERROR_MAX); - } else { - SIMPLE_CACHE_UMA(ENUMERATION, - "SyncOpenPlatformFileError_WithoutIndex", - cache_type_, - -error, -base::PLATFORM_FILE_ERROR_MAX); - } - } - while (--i >= 0) { - bool ALLOW_UNUSED did_close = ClosePlatformFile(files_[i]); - DLOG_IF(INFO, !did_close) << "Could not close file " - << filename.MaybeAsASCII(); } + while (--i >= 0) + CloseFile(i); return false; } } have_open_files_ = true; - if (create) { - base::Time creation_time = Time::Now(); - out_entry_stat->set_last_modified(creation_time); - out_entry_stat->set_last_used(creation_time); - for (int i = 0; i < kSimpleEntryStreamCount; ++i) - out_entry_stat->set_data_size(i, 0); - } else { - base::TimeDelta entry_age = base::Time::Now() - base::Time::UnixEpoch(); - for (int i = 0; i < kSimpleEntryFileCount; ++i) { - PlatformFileInfo file_info; - bool success = GetPlatformFileInfo(files_[i], &file_info); - base::Time file_last_modified; - if (!success) { - DLOG(WARNING) << "Could not get platform file info."; - continue; - } - out_entry_stat->set_last_used(file_info.last_accessed); - if (simple_util::GetMTime(path_, &file_last_modified)) - out_entry_stat->set_last_modified(file_last_modified); - else - out_entry_stat->set_last_modified(file_info.last_modified); - - base::TimeDelta stream_age = - base::Time::Now() - out_entry_stat->last_modified(); - if (stream_age < entry_age) - entry_age = stream_age; - - // Two things prevent from knowing the right values for |data_size|: - // 1) The key is not known, hence its length is unknown. - // 2) Stream 0 and stream 1 are in the same file, and the exact size for - // each will only be known when reading the EOF record for stream 0. - // - // The size for file 0 and 1 is temporarily kept in - // |data_size(1)| and |data_size(2)| respectively. Reading the key in - // InitializeForOpen yields the data size for each file. In the case of - // file hash_1, this is the total size of stream 2, and is assigned to - // data_size(2). In the case of file 0, it is the combined size of stream - // 0, stream 1 and one EOF record. The exact distribution of sizes between - // stream 1 and stream 0 is only determined after reading the EOF record - // for stream 0 in ReadAndValidateStream0. - out_entry_stat->set_data_size(i + 1, file_info.size); + + base::TimeDelta entry_age = base::Time::Now() - base::Time::UnixEpoch(); + for (int i = 0; i < kSimpleEntryFileCount; ++i) { + if (empty_file_omitted_[i]) { + out_entry_stat->set_data_size(i + 1, 0); + continue; } - SIMPLE_CACHE_UMA(CUSTOM_COUNTS, - "SyncOpenEntryAge", cache_type_, - entry_age.InHours(), 1, 1000, 50); + + PlatformFileInfo file_info; + bool success = GetPlatformFileInfo(files_[i], &file_info); + base::Time file_last_modified; + if (!success) { + DLOG(WARNING) << "Could not get platform file info."; + continue; + } + out_entry_stat->set_last_used(file_info.last_accessed); + if (simple_util::GetMTime(path_, &file_last_modified)) + out_entry_stat->set_last_modified(file_last_modified); + else + out_entry_stat->set_last_modified(file_info.last_modified); + + base::TimeDelta stream_age = + base::Time::Now() - out_entry_stat->last_modified(); + if (stream_age < entry_age) + entry_age = stream_age; + + // Two things prevent from knowing the right values for |data_size|: + // 1) The key is not known, hence its length is unknown. + // 2) Stream 0 and stream 1 are in the same file, and the exact size for + // each will only be known when reading the EOF record for stream 0. + // + // The size for file 0 and 1 is temporarily kept in + // |data_size(1)| and |data_size(2)| respectively. Reading the key in + // InitializeForOpen yields the data size for each file. In the case of + // file hash_1, this is the total size of stream 2, and is assigned to + // data_size(2). In the case of file 0, it is the combined size of stream + // 0, stream 1 and one EOF record. The exact distribution of sizes between + // stream 1 and stream 0 is only determined after reading the EOF record + // for stream 0 in ReadAndValidateStream0. + out_entry_stat->set_data_size(i + 1, file_info.size); } + SIMPLE_CACHE_UMA(CUSTOM_COUNTS, + "SyncOpenEntryAge", cache_type_, + entry_age.InHours(), 1, 1000, 50); return true; } -void SimpleSynchronousEntry::CloseFiles() { +bool SimpleSynchronousEntry::CreateFiles( + bool had_index, + SimpleEntryStat* out_entry_stat) { for (int i = 0; i < kSimpleEntryFileCount; ++i) { - DCHECK_NE(kInvalidPlatformFileValue, files_[i]); - bool did_close = ClosePlatformFile(files_[i]); + PlatformFileError error; + if (!MaybeCreateFile(i, FILE_NOT_REQUIRED, &error)) { + // TODO(ttuttle,gavinp): Remove one each of these triplets of histograms. + // We can calculate the third as the sum or difference of the other two. + RecordSyncCreateResult(CREATE_ENTRY_PLATFORM_FILE_ERROR, had_index); + SIMPLE_CACHE_UMA(ENUMERATION, + "SyncCreatePlatformFileError", cache_type_, + -error, -base::PLATFORM_FILE_ERROR_MAX); + if (had_index) { + SIMPLE_CACHE_UMA(ENUMERATION, + "SyncCreatePlatformFileError_WithIndex", cache_type_, + -error, -base::PLATFORM_FILE_ERROR_MAX); + } else { + SIMPLE_CACHE_UMA(ENUMERATION, + "SyncCreatePlatformFileError_WithoutIndex", + cache_type_, + -error, -base::PLATFORM_FILE_ERROR_MAX); + } + while (--i >= 0) + CloseFile(i); + return false; + } + } + + have_open_files_ = true; + + base::Time creation_time = Time::Now(); + out_entry_stat->set_last_modified(creation_time); + out_entry_stat->set_last_used(creation_time); + for (int i = 0; i < kSimpleEntryStreamCount; ++i) + out_entry_stat->set_data_size(i, 0); + + return true; +} + +void SimpleSynchronousEntry::CloseFile(int index) { + if (empty_file_omitted_[index]) { + empty_file_omitted_[index] = false; + } else { + DCHECK_NE(kInvalidPlatformFileValue, files_[index]); + bool did_close = ClosePlatformFile(files_[index]); DCHECK(did_close); + files_[index] = kInvalidPlatformFileValue; } } +void SimpleSynchronousEntry::CloseFiles() { + for (int i = 0; i < kSimpleEntryFileCount; ++i) + CloseFile(i); +} + int SimpleSynchronousEntry::InitializeForOpen( bool had_index, SimpleEntryStat* out_entry_stat, scoped_refptr<net::GrowableIOBuffer>* stream_0_data, uint32* out_stream_0_crc32) { DCHECK(!initialized_); - if (!OpenOrCreateFiles(false, had_index, out_entry_stat)) + if (!OpenFiles(had_index, out_entry_stat)) { + DLOG(WARNING) << "Could not open platform files for entry."; return net::ERR_FAILED; + } for (int i = 0; i < kSimpleEntryFileCount; ++i) { + if (empty_file_omitted_[i]) + continue; + SimpleFileHeader header; int header_read_result = ReadPlatformFile(files_[i], 0, reinterpret_cast<char*>(&header), @@ -671,8 +740,10 @@ int SimpleSynchronousEntry::InitializeForOpen( } else { out_entry_stat->set_data_size( 2, GetDataSizeFromKeyAndFileSize(key_, out_entry_stat->data_size(2))); - if (out_entry_stat->data_size(2) < 0) + if (out_entry_stat->data_size(2) < 0) { + DLOG(WARNING) << "Stream 2 file is too small."; return net::ERR_FAILED; + } } if (base::Hash(key.get(), header.key_length) != header.key_hash) { @@ -682,46 +753,68 @@ int SimpleSynchronousEntry::InitializeForOpen( return net::ERR_FAILED; } } + + const int third_stream_file_index = GetFileIndexFromStreamIndex(2); + DCHECK(CanOmitEmptyFile(third_stream_file_index)); + if (!empty_file_omitted_[third_stream_file_index] && + out_entry_stat->data_size(2) == 0) { + DLOG(INFO) << "Removing empty third stream file."; + CloseFile(third_stream_file_index); + DeleteFileForEntryHash(path_, entry_hash_, third_stream_file_index); + empty_file_omitted_[third_stream_file_index] = true; + } + RecordSyncOpenResult(cache_type_, OPEN_ENTRY_SUCCESS, had_index); initialized_ = true; return net::OK; } +bool SimpleSynchronousEntry::InitializeCreatedFile( + int file_index, + CreateEntryResult* out_result) { + SimpleFileHeader header; + header.initial_magic_number = kSimpleInitialMagicNumber; + header.version = kSimpleEntryVersionOnDisk; + + header.key_length = key_.size(); + header.key_hash = base::Hash(key_); + + int bytes_written = WritePlatformFile( + files_[file_index], 0, reinterpret_cast<char*>(&header), sizeof(header)); + if (bytes_written != sizeof(header)) { + *out_result = CREATE_ENTRY_CANT_WRITE_HEADER; + return false; + } + + bytes_written = WritePlatformFile( + files_[file_index], sizeof(header), key_.data(), key_.size()); + if (bytes_written != implicit_cast<int>(key_.size())) { + *out_result = CREATE_ENTRY_CANT_WRITE_KEY; + return false; + } + + return true; +} + int SimpleSynchronousEntry::InitializeForCreate( bool had_index, SimpleEntryStat* out_entry_stat) { DCHECK(!initialized_); - if (!OpenOrCreateFiles(true, had_index, out_entry_stat)) { + if (!CreateFiles(had_index, out_entry_stat)) { DLOG(WARNING) << "Could not create platform files."; return net::ERR_FILE_EXISTS; } for (int i = 0; i < kSimpleEntryFileCount; ++i) { - SimpleFileHeader header; - header.initial_magic_number = kSimpleInitialMagicNumber; - header.version = kSimpleEntryVersionOnDisk; + if (empty_file_omitted_[i]) + continue; - header.key_length = key_.size(); - header.key_hash = base::Hash(key_); - - if (WritePlatformFile( - files_[i], 0, reinterpret_cast<char*>(&header), sizeof(header)) != - sizeof(header)) { - DLOG(WARNING) << "Could not write cache file header to cache entry."; - RecordSyncCreateResult( - cache_type_, CREATE_ENTRY_CANT_WRITE_HEADER, had_index); - return net::ERR_FAILED; - } - - if (WritePlatformFile( - files_[i], sizeof(SimpleFileHeader), key_.data(), key_.size()) != - implicit_cast<int>(key_.size())) { - DLOG(WARNING) << "Could not write keys to new cache entry."; - RecordSyncCreateResult( - cache_type_, CREATE_ENTRY_CANT_WRITE_KEY, had_index); + CreateEntryResult result; + if (!InitializeCreatedFile(i, &result)) { + RecordSyncCreateResult(result, had_index); return net::ERR_FAILED; } } - RecordSyncCreateResult(cache_type_, CREATE_ENTRY_SUCCESS, had_index); + RecordSyncCreateResult(CREATE_ENTRY_SUCCESS, had_index); initialized_ = true; return net::OK; } @@ -809,8 +902,48 @@ int SimpleSynchronousEntry::GetEOFRecordData(int index, } void SimpleSynchronousEntry::Doom() const { - // TODO(gavinp): Consider if we should guard against redundant Doom() calls. DeleteFilesForEntryHash(path_, entry_hash_); } +bool SimpleSynchronousEntry::DeleteFileForEntryHash( + const FilePath& path, + const uint64 entry_hash, + const int file_index) { + FilePath to_delete = path.AppendASCII( + GetFilenameFromEntryHashAndFileIndex(entry_hash, file_index)); + return base::DeleteFile(to_delete, false); +} + +bool SimpleSynchronousEntry::DeleteFilesForEntryHash( + const FilePath& path, + const uint64 entry_hash) { + bool result = true; + for (int i = 0; i < kSimpleEntryFileCount; ++i) { + if (!DeleteFileForEntryHash(path, entry_hash, i) && !CanOmitEmptyFile(i)) + result = false; + } + return result; +} + +void SimpleSynchronousEntry::RecordSyncCreateResult(CreateEntryResult result, + bool had_index) { + DCHECK_GT(CREATE_ENTRY_MAX, result); + SIMPLE_CACHE_UMA(ENUMERATION, + "SyncCreateResult", cache_type_, result, CREATE_ENTRY_MAX); + if (had_index) { + SIMPLE_CACHE_UMA(ENUMERATION, + "SyncCreateResult_WithIndex", cache_type_, + result, CREATE_ENTRY_MAX); + } else { + SIMPLE_CACHE_UMA(ENUMERATION, + "SyncCreateResult_WithoutIndex", cache_type_, + result, CREATE_ENTRY_MAX); + } +} + +FilePath SimpleSynchronousEntry::GetFilenameFromFileIndex(int file_index) { + return path_.AppendASCII( + GetFilenameFromEntryHashAndFileIndex(entry_hash_, file_index)); +} + } // namespace disk_cache diff --git a/net/disk_cache/simple/simple_synchronous_entry.h b/net/disk_cache/simple/simple_synchronous_entry.h index c406e6e..470e8e2 100644 --- a/net/disk_cache/simple/simple_synchronous_entry.h +++ b/net/disk_cache/simple/simple_synchronous_entry.h @@ -92,12 +92,14 @@ class SimpleSynchronousEntry { EntryOperationData(int index_p, int offset_p, int buf_len_p, - bool truncate_p); + bool truncate_p, + bool doomed_p); int index; int offset; int buf_len; bool truncate; + bool doomed; }; static void OpenEntry(net::CacheType cache_type, @@ -134,7 +136,7 @@ class SimpleSynchronousEntry { void WriteData(const EntryOperationData& in_entry_op, net::IOBuffer* in_buf, SimpleEntryStat* out_entry_stat, - int* out_result) const; + int* out_result); void CheckEOFRecord(int index, const SimpleEntryStat& entry_stat, uint32 expected_crc32, @@ -150,6 +152,19 @@ class SimpleSynchronousEntry { std::string key() const { return key_; } private: + enum CreateEntryResult { + CREATE_ENTRY_SUCCESS = 0, + CREATE_ENTRY_PLATFORM_FILE_ERROR = 1, + CREATE_ENTRY_CANT_WRITE_HEADER = 2, + CREATE_ENTRY_CANT_WRITE_KEY = 3, + CREATE_ENTRY_MAX = 4, + }; + + enum FileRequired { + FILE_NOT_REQUIRED, + FILE_REQUIRED + }; + SimpleSynchronousEntry( net::CacheType cache_type, const base::FilePath& path, @@ -160,9 +175,22 @@ class SimpleSynchronousEntry { // called. ~SimpleSynchronousEntry(); - bool OpenOrCreateFiles(bool create, - bool had_index, - SimpleEntryStat* out_entry_stat); + // Tries to open one of the cache entry files. Succeeds if the open succeeds + // or if the file was not found and is allowed to be omitted if the + // corresponding stream is empty. + bool MaybeOpenFile(int file_index, + base::PlatformFileError* out_error); + // Creates one of the cache entry files if necessary. If the file is allowed + // to be omitted if the corresponding stream is empty, and if |file_required| + // is FILE_NOT_REQUIRED, then the file is not created; otherwise, it is. + bool MaybeCreateFile(int file_index, + FileRequired file_required, + base::PlatformFileError* out_error); + bool OpenFiles(bool had_index, + SimpleEntryStat* out_entry_stat); + bool CreateFiles(bool had_index, + SimpleEntryStat* out_entry_stat); + void CloseFile(int index); void CloseFiles(); // Returns a net error, i.e. net::OK on success. |had_index| is passed @@ -173,6 +201,11 @@ class SimpleSynchronousEntry { scoped_refptr<net::GrowableIOBuffer>* stream_0_data, uint32* out_stream_0_crc32); + // Writes the header and key to a newly-created stream file. |index| is the + // index of the stream. Returns true on success; returns false and sets + // |*out_result| on failure. + bool InitializeCreatedFile(int index, CreateEntryResult* out_result); + // Returns a net error, including net::OK on success and net::FILE_EXISTS // when the entry already exists. |had_index| is passed from the main entry // for metrics purposes, and is true if the index was initialized when the @@ -194,9 +227,16 @@ class SimpleSynchronousEntry { int* out_data_size) const; void Doom() const; + static bool DeleteFileForEntryHash(const base::FilePath& path, + uint64 entry_hash, + int file_index); static bool DeleteFilesForEntryHash(const base::FilePath& path, uint64 entry_hash); + void RecordSyncCreateResult(CreateEntryResult result, bool had_index); + + base::FilePath GetFilenameFromFileIndex(int file_index); + const net::CacheType cache_type_; const base::FilePath path_; const uint64 entry_hash_; @@ -206,6 +246,10 @@ class SimpleSynchronousEntry { bool initialized_; base::PlatformFile files_[kSimpleEntryFileCount]; + + // True if the corresponding stream is empty and therefore no on-disk file + // was created to store it. + bool empty_file_omitted_[kSimpleEntryFileCount]; }; } // namespace disk_cache |