Skip to content

Commit

Permalink
Delay loading the Extension State Store for 5 seconds on startup.
Browse files Browse the repository at this point in the history
BUG=161848


Review URL: https://chromiumcodereview.appspot.com/11519016

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@172413 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
mpcomplete@chromium.org committed Dec 11, 2012
1 parent 8bef405 commit ebaa018
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 8 deletions.
70 changes: 65 additions & 5 deletions chrome/browser/extensions/state_store.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,19 @@

#include "chrome/browser/extensions/state_store.h"

#include "base/bind.h"
#include "base/message_loop.h"
#include "chrome/common/chrome_notification_types.h"
#include "chrome/common/extensions/extension.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_types.h"

namespace {

// Delay, in seconds, before we should open the State Store database. We
// defer it to avoid slowing down startup. See http://crbug.com/161848
const int kInitDelaySeconds = 5;

std::string GetFullKey(const std::string& extension_id,
const std::string& key) {
return extension_id + "." + key;
Expand All @@ -20,20 +26,62 @@ std::string GetFullKey(const std::string& extension_id,

namespace extensions {

// Helper class to delay tasks until we're ready to start executing them.
class StateStore::DelayedTaskQueue {
public:
DelayedTaskQueue() : ready_(false) {}
~DelayedTaskQueue() {}

// Queues up a task for invoking once we're ready. Invokes immediately if
// we're already ready.
void InvokeWhenReady(base::Closure task);

// Marks us ready, and invokes all pending tasks.
void SetReady();

private:
bool ready_;
std::vector<base::Closure> pending_tasks_;
};

void StateStore::DelayedTaskQueue::InvokeWhenReady(base::Closure task) {
if (ready_) {
task.Run();
} else {
pending_tasks_.push_back(task);
}
}

void StateStore::DelayedTaskQueue::SetReady() {
ready_ = true;

for (size_t i = 0; i < pending_tasks_.size(); ++i)
pending_tasks_[i].Run();
pending_tasks_.clear();
}

StateStore::StateStore(Profile* profile, const FilePath& db_path)
: store_(db_path) {
: task_queue_(new DelayedTaskQueue()) {
registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_INSTALLED,
content::Source<Profile>(profile));
registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNINSTALLED,
content::Source<Profile>(profile));

MessageLoop::current()->PostDelayedTask(FROM_HERE,
base::Bind(&StateStore::Init, AsWeakPtr(), db_path),
base::TimeDelta::FromSeconds(kInitDelaySeconds));
}

StateStore::StateStore(Profile* profile, ValueStore* value_store)
: store_(value_store) {
: store_(value_store),
task_queue_(new DelayedTaskQueue()) {
registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_INSTALLED,
content::Source<Profile>(profile));
registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNINSTALLED,
content::Source<Profile>(profile));

// This constructor is for testing. No need to delay Init.
Init(FilePath());
}

StateStore::~StateStore() {
Expand All @@ -46,14 +94,18 @@ void StateStore::RegisterKey(const std::string& key) {
void StateStore::GetExtensionValue(const std::string& extension_id,
const std::string& key,
ReadCallback callback) {
store_.Get(GetFullKey(extension_id, key), callback);
task_queue_->InvokeWhenReady(
base::Bind(&ValueStoreFrontend::Get, base::Unretained(&store_),
GetFullKey(extension_id, key), callback));
}

void StateStore::SetExtensionValue(
const std::string& extension_id,
const std::string& key,
scoped_ptr<base::Value> value) {
store_.Set(GetFullKey(extension_id, key), value.Pass());
task_queue_->InvokeWhenReady(
base::Bind(&ValueStoreFrontend::Set, base::Unretained(&store_),
GetFullKey(extension_id, key), base::Passed(value.Pass())));
}

void StateStore::Observe(int type,
Expand All @@ -73,8 +125,16 @@ void StateStore::Observe(int type,

for (std::set<std::string>::iterator key = registered_keys_.begin();
key != registered_keys_.end(); ++key) {
store_.Remove(GetFullKey(extension_id, *key));
task_queue_->InvokeWhenReady(
base::Bind(&ValueStoreFrontend::Remove, base::Unretained(&store_),
GetFullKey(extension_id, *key)));
}
}

void StateStore::Init(const FilePath& db_path) {
if (!db_path.empty())
store_.Init(db_path);
task_queue_->SetReady();
}

} // namespace extensions
7 changes: 7 additions & 0 deletions chrome/browser/extensions/state_store.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,18 +45,25 @@ class StateStore
scoped_ptr<base::Value> value);

private:
class DelayedTaskQueue;

// content::NotificationObserver
virtual void Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) OVERRIDE;

void Init(const FilePath& db_path);

// The store that holds our key/values.
ValueStoreFrontend store_;

// List of all known keys. They will be cleared for each extension when it is
// (un)installed.
std::set<std::string> registered_keys_;

// Keeps track of tasks we have delayed while starting up.
scoped_ptr<DelayedTaskQueue> task_queue_;

content::NotificationRegistrar registrar_;
};

Expand Down
14 changes: 11 additions & 3 deletions chrome/browser/value_store/value_store_frontend.cc
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,13 @@ class ValueStoreFrontend::Backend : public base::RefCountedThreadSafe<Backend> {
DISALLOW_COPY_AND_ASSIGN(Backend);
};

ValueStoreFrontend::ValueStoreFrontend()
: backend_(new Backend()) {
}

ValueStoreFrontend::ValueStoreFrontend(const FilePath& db_path)
: backend_(new Backend()) {
BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
base::Bind(&ValueStoreFrontend::Backend::Init,
backend_, db_path));
Init(db_path);
}

ValueStoreFrontend::ValueStoreFrontend(ValueStore* value_store)
Expand All @@ -100,6 +102,12 @@ ValueStoreFrontend::~ValueStoreFrontend() {
DCHECK(CalledOnValidThread());
}

void ValueStoreFrontend::Init(const FilePath& db_path) {
BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
base::Bind(&ValueStoreFrontend::Backend::Init,
backend_, db_path));
}

void ValueStoreFrontend::Get(const std::string& key,
const ReadCallback& callback) {
DCHECK(CalledOnValidThread());
Expand Down
3 changes: 3 additions & 0 deletions chrome/browser/value_store/value_store_frontend.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,14 @@ class ValueStoreFrontend
public:
typedef base::Callback<void(scoped_ptr<base::Value>)> ReadCallback;

ValueStoreFrontend();
explicit ValueStoreFrontend(const FilePath& db_path);
// This variant is useful for testing (using a mock ValueStore).
explicit ValueStoreFrontend(ValueStore* value_store);
~ValueStoreFrontend();

void Init(const FilePath& db_path);

// Retrieves a value from the database asynchronously, passing a copy to
// |callback| when ready. NULL is passed if no matching entry is found.
void Get(const std::string& key, const ReadCallback& callback);
Expand Down

0 comments on commit ebaa018

Please sign in to comment.