summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrvargas <rvargas@chromium.org>2015-09-22 14:28:02 -0700
committerCommit bot <commit-bot@chromium.org>2015-09-22 21:36:55 +0000
commit67df23f076705daa68a80db863a3c26986dc32ca (patch)
treea145ca5ab5b56b6732b4f4f233d1fc63c575fe89
parent74be507bf420d21eed1a354dc15b4784a6aba4ff (diff)
downloadchromium_src-67df23f076705daa68a80db863a3c26986dc32ca.zip
chromium_src-67df23f076705daa68a80db863a3c26986dc32ca.tar.gz
chromium_src-67df23f076705daa68a80db863a3c26986dc32ca.tar.bz2
Disk cache: switch stress_cache to perform fully async operations to
get a more realistic environmenrt. BUG=none Review URL: https://codereview.chromium.org/1351223002 Cr-Commit-Position: refs/heads/master@{#350237}
-rw-r--r--net/tools/stress_cache/stress_cache.cc261
1 files changed, 205 insertions, 56 deletions
diff --git a/net/tools/stress_cache/stress_cache.cc b/net/tools/stress_cache/stress_cache.cc
index 0706d4c..29ddff2 100644
--- a/net/tools/stress_cache/stress_cache.cc
+++ b/net/tools/stress_cache/stress_cache.cc
@@ -19,6 +19,7 @@
#include "base/at_exit.h"
#include "base/bind.h"
+#include "base/bind_helpers.h"
#include "base/command_line.h"
#include "base/debug/debugger.h"
#include "base/files/file_path.h"
@@ -28,6 +29,7 @@
#include "base/path_service.h"
#include "base/process/launch.h"
#include "base/process/process.h"
+#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
@@ -98,6 +100,197 @@ std::string GenerateStressKey() {
return std::string(key);
}
+// kNumKeys is meant to be enough to have about 3x or 4x iterations before
+// the process crashes.
+#ifdef NDEBUG
+const int kNumKeys = 4000;
+#else
+const int kNumKeys = 1200;
+#endif
+const int kNumEntries = 30;
+const int kBufferSize = 2000;
+const int kReadSize = 20;
+
+// Things that an entry can be doing.
+enum Operation { NONE, OPEN, CREATE, READ, WRITE, DOOM };
+
+// This class encapsulates a cache entry and the operations performed on that
+// entry. An entry is opened or created as needed, the current content is then
+// verified and then something is written to the entry. At that point, the
+// |state_| becomes NONE again, waiting for another write, unless the entry is
+// closed or deleted.
+class EntryWrapper {
+ public:
+ EntryWrapper() : entry_(nullptr), state_(NONE) {
+ buffer_ = new net::IOBuffer(kBufferSize);
+ memset(buffer_->data(), 'k', kBufferSize);
+ }
+
+ Operation state() const { return state_; }
+
+ void DoOpen(int key);
+
+ private:
+ void OnOpenDone(int key, int result);
+ void DoRead();
+ void OnReadDone(int result);
+ void DoWrite();
+ void OnWriteDone(int size, int result);
+ void DoDelete(const std::string& key);
+ void OnDeleteDone(int result);
+ void DoIdle();
+
+ disk_cache::Entry* entry_;
+ Operation state_;
+ scoped_refptr<net::IOBuffer> buffer_;
+};
+
+// The data that the main thread is working on.
+struct Data {
+ Data() : pendig_operations(0), writes(0), iteration(0), cache(nullptr) {}
+
+ int pendig_operations; // Counter of simultaneous operations.
+ int writes; // How many writes since this iteration started.
+ int iteration; // The iteration (number of crashes).
+ disk_cache::BackendImpl* cache;
+ std::string keys[kNumKeys];
+ EntryWrapper entries[kNumEntries];
+};
+
+Data* g_data = nullptr;
+
+void EntryWrapper::DoOpen(int key) {
+ DCHECK_EQ(state_, NONE);
+ if (entry_)
+ return DoRead();
+
+ state_ = OPEN;
+ int rv = g_data->cache->OpenEntry(
+ g_data->keys[key], &entry_,
+ base::Bind(&EntryWrapper::OnOpenDone, base::Unretained(this), key));
+ if (rv != net::ERR_IO_PENDING)
+ OnOpenDone(key, rv);
+}
+
+void EntryWrapper::OnOpenDone(int key, int result) {
+ if (result == net::OK)
+ return DoRead();
+
+ CHECK_EQ(state_, OPEN);
+ state_ = CREATE;
+ result = g_data->cache->CreateEntry(
+ g_data->keys[key], &entry_,
+ base::Bind(&EntryWrapper::OnOpenDone, base::Unretained(this), key));
+ if (result != net::ERR_IO_PENDING)
+ OnOpenDone(key, result);
+}
+
+void EntryWrapper::DoRead() {
+ int current_size = entry_->GetDataSize(0);
+ if (!current_size)
+ return DoWrite();
+
+ state_ = READ;
+ memset(buffer_->data(), 'k', kReadSize);
+ int rv = entry_->ReadData(
+ 0, 0, buffer_.get(), kReadSize,
+ base::Bind(&EntryWrapper::OnReadDone, base::Unretained(this)));
+ if (rv != net::ERR_IO_PENDING)
+ OnReadDone(rv);
+}
+
+void EntryWrapper::OnReadDone(int result) {
+ DCHECK_EQ(state_, READ);
+ CHECK_EQ(result, kReadSize);
+ CHECK_EQ(0, memcmp(buffer_->data(), "Write: ", 7));
+ DoWrite();
+}
+
+void EntryWrapper::DoWrite() {
+ bool truncate = (rand() % 2 == 0);
+ int size = kBufferSize - (rand() % 20) * kBufferSize / 20;
+ state_ = WRITE;
+ base::snprintf(buffer_->data(), kBufferSize,
+ "Write: %d iter: %d, size: %d, truncate: %d ",
+ g_data->writes, g_data->iteration, size, truncate ? 1 : 0);
+ int rv = entry_->WriteData(
+ 0, 0, buffer_.get(), size,
+ base::Bind(&EntryWrapper::OnWriteDone, base::Unretained(this), size),
+ truncate);
+ if (rv != net::ERR_IO_PENDING)
+ OnWriteDone(size, rv);
+}
+
+void EntryWrapper::OnWriteDone(int size, int result) {
+ DCHECK_EQ(state_, WRITE);
+ CHECK_EQ(size, result);
+ if (!(g_data->writes++ % 100))
+ printf("Entries: %d \r", g_data->writes);
+
+ int random = rand() % 100;
+ std::string key = entry_->GetKey();
+ if (random > 90)
+ return DoDelete(key); // 10% delete then close.
+
+ if (random > 60) { // 20% close.
+ entry_->Close();
+ entry_ = nullptr;
+ }
+
+ if (random > 80)
+ return DoDelete(key); // 10% close then delete.
+
+ DoIdle(); // 60% do another write later.
+}
+
+void EntryWrapper::DoDelete(const std::string& key) {
+ state_ = DOOM;
+ int rv = g_data->cache->DoomEntry(
+ key, base::Bind(&EntryWrapper::OnDeleteDone, base::Unretained(this)));
+ if (rv != net::ERR_IO_PENDING)
+ OnDeleteDone(rv);
+}
+
+void EntryWrapper::OnDeleteDone(int result) {
+ DCHECK_EQ(state_, DOOM);
+ if (entry_) {
+ entry_->Close();
+ entry_ = nullptr;
+ }
+ DoIdle();
+}
+
+void LoopTask();
+
+void EntryWrapper::DoIdle() {
+ state_ = NONE;
+ g_data->pendig_operations--;
+ DCHECK(g_data->pendig_operations);
+ base::MessageLoop::current()->task_runner()->PostTask(FROM_HERE,
+ base::Bind(&LoopTask));
+}
+
+// The task that keeps the main thread busy. Whenever an entry becomes idle this
+// task is executed again.
+void LoopTask() {
+ if (g_data->pendig_operations >= kNumEntries)
+ return;
+
+ int slot = rand() % kNumEntries;
+ if (g_data->entries[slot].state() == NONE) {
+ // Each slot will have some keys assigned to it so that the same entry will
+ // not be open by two slots, which means that the state is well known at
+ // all times.
+ int keys_per_entry = kNumKeys / kNumEntries;
+ int key = rand() % keys_per_entry + keys_per_entry * slot;
+ g_data->pendig_operations++;
+ g_data->entries[slot].DoOpen(key);
+ }
+
+ base::MessageLoop::current()->task_runner()->PostTask(FROM_HERE,
+ base::Bind(&LoopTask));
+}
+
// This thread will loop forever, adding and removing entries from the cache.
// iteration is the current crash cycle, so the entries on the cache are marked
// to know which instance of the application wrote them.
@@ -114,76 +307,32 @@ void StressTheCache(int iteration) {
base::Thread::Options(base::MessageLoop::TYPE_IO, 0)))
return;
- disk_cache::BackendImpl* cache = new disk_cache::BackendImpl(
+ g_data = new Data();
+ g_data->iteration = iteration;
+ g_data->cache = new disk_cache::BackendImpl(
path, mask, cache_thread.task_runner().get(), NULL);
- cache->SetMaxSize(cache_size);
- cache->SetFlags(disk_cache::kNoLoadProtection);
+ g_data->cache->SetMaxSize(cache_size);
+ g_data->cache->SetFlags(disk_cache::kNoLoadProtection);
net::TestCompletionCallback cb;
- int rv = cache->Init(cb.callback());
+ int rv = g_data->cache->Init(cb.callback());
if (cb.GetResult(rv) != net::OK) {
printf("Unable to initialize cache.\n");
return;
}
printf("Iteration %d, initial entries: %d\n", iteration,
- cache->GetEntryCount());
+ g_data->cache->GetEntryCount());
int seed = static_cast<int>(Time::Now().ToInternalValue());
srand(seed);
- // kNumKeys is meant to be enough to have about 3x or 4x iterations before
- // the process crashes.
-#ifdef NDEBUG
- const int kNumKeys = 4000;
-#else
- const int kNumKeys = 1200;
-#endif
- const int kNumEntries = 30;
- std::string keys[kNumKeys];
- disk_cache::Entry* entries[kNumEntries] = {0};
+ for (int i = 0; i < kNumKeys; i++)
+ g_data->keys[i] = GenerateStressKey();
- for (int i = 0; i < kNumKeys; i++) {
- keys[i] = GenerateStressKey();
- }
-
- const int kSize = 20000;
- scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kSize));
- memset(buffer->data(), 'k', kSize);
-
- for (int i = 0;; i++) {
- int slot = rand() % kNumEntries;
- int key = rand() % kNumKeys;
- bool truncate = (rand() % 2 == 0);
- int size = kSize - (rand() % 20) * kSize / 20;
-
- if (entries[slot])
- entries[slot]->Close();
-
- net::TestCompletionCallback cb;
- rv = cache->OpenEntry(keys[key], &entries[slot], cb.callback());
- if (cb.GetResult(rv) != net::OK) {
- rv = cache->CreateEntry(keys[key], &entries[slot], cb.callback());
- CHECK_EQ(net::OK, cb.GetResult(rv));
- }
-
- base::snprintf(buffer->data(), kSize,
- "i: %d iter: %d, size: %d, truncate: %d ", i, iteration,
- size, truncate ? 1 : 0);
- rv = entries[slot]->WriteData(0, 0, buffer.get(), size, cb.callback(),
- truncate);
- CHECK_EQ(size, cb.GetResult(rv));
-
- if (rand() % 100 > 80) {
- key = rand() % kNumKeys;
- net::TestCompletionCallback cb2;
- rv = cache->DoomEntry(keys[key], cb2.callback());
- cb2.GetResult(rv);
- }
-
- if (!(i % 100))
- printf("Entries: %d \r", i);
- }
+ base::MessageLoop::current()->task_runner()->PostTask(FROM_HERE,
+ base::Bind(&LoopTask));
+ base::RunLoop().Run();
}
// We want to prevent the timer thread from killing the process while we are