Commit 4b6e14e8 authored by raymes's avatar raymes Committed by Commit bot

Implement lossy pref behavior for JsonPrefStore.

Writes to lossy prefs should not trigger writes to disk to be scheduled.
This behavior is implemented for JsonPrefStore. CommitPendingWrites will
ensure that all outstanding lossy writes are also written to disk.

BUG=476800

Committed: https://crrev.com/e9d8c9882ca272c803711e26ded4dd7d5203a59e
Cr-Commit-Position: refs/heads/master@{#329122}

Review URL: https://codereview.chromium.org/1127963002

Cr-Commit-Position: refs/heads/master@{#329287}
parent 4261538f
...@@ -150,17 +150,10 @@ JsonPrefStore::JsonPrefStore( ...@@ -150,17 +150,10 @@ JsonPrefStore::JsonPrefStore(
const base::FilePath& filename, const base::FilePath& filename,
const scoped_refptr<base::SequencedTaskRunner>& sequenced_task_runner, const scoped_refptr<base::SequencedTaskRunner>& sequenced_task_runner,
scoped_ptr<PrefFilter> pref_filter) scoped_ptr<PrefFilter> pref_filter)
: path_(filename), : JsonPrefStore(filename,
sequenced_task_runner_(sequenced_task_runner), base::FilePath(),
prefs_(new base::DictionaryValue()), sequenced_task_runner,
read_only_(false), pref_filter.Pass()) {
writer_(filename, sequenced_task_runner),
pref_filter_(pref_filter.Pass()),
initialized_(false),
filtering_in_progress_(false),
read_error_(PREF_READ_ERROR_NONE),
write_count_histogram_(writer_.commit_interval(), path_) {
DCHECK(!path_.empty());
} }
JsonPrefStore::JsonPrefStore( JsonPrefStore::JsonPrefStore(
...@@ -177,6 +170,7 @@ JsonPrefStore::JsonPrefStore( ...@@ -177,6 +170,7 @@ JsonPrefStore::JsonPrefStore(
pref_filter_(pref_filter.Pass()), pref_filter_(pref_filter.Pass()),
initialized_(false), initialized_(false),
filtering_in_progress_(false), filtering_in_progress_(false),
pending_lossy_write_(false),
read_error_(PREF_READ_ERROR_NONE), read_error_(PREF_READ_ERROR_NONE),
write_count_histogram_(writer_.commit_interval(), path_) { write_count_histogram_(writer_.commit_interval(), path_) {
DCHECK(!path_.empty()); DCHECK(!path_.empty());
...@@ -252,8 +246,7 @@ void JsonPrefStore::SetValueSilently(const std::string& key, ...@@ -252,8 +246,7 @@ void JsonPrefStore::SetValueSilently(const std::string& key,
prefs_->Get(key, &old_value); prefs_->Get(key, &old_value);
if (!old_value || !value->Equals(old_value)) { if (!old_value || !value->Equals(old_value)) {
prefs_->Set(key, new_value.release()); prefs_->Set(key, new_value.release());
if (!read_only_) ScheduleWrite(flags);
writer_.ScheduleWrite(this);
} }
} }
...@@ -268,8 +261,7 @@ void JsonPrefStore::RemoveValueSilently(const std::string& key, uint32 flags) { ...@@ -268,8 +261,7 @@ void JsonPrefStore::RemoveValueSilently(const std::string& key, uint32 flags) {
DCHECK(CalledOnValidThread()); DCHECK(CalledOnValidThread());
prefs_->RemovePath(key, NULL); prefs_->RemovePath(key, NULL);
if (!read_only_) ScheduleWrite(flags);
writer_.ScheduleWrite(this);
} }
bool JsonPrefStore::ReadOnly() const { bool JsonPrefStore::ReadOnly() const {
...@@ -309,6 +301,11 @@ void JsonPrefStore::ReadPrefsAsync(ReadErrorDelegate* error_delegate) { ...@@ -309,6 +301,11 @@ void JsonPrefStore::ReadPrefsAsync(ReadErrorDelegate* error_delegate) {
void JsonPrefStore::CommitPendingWrite() { void JsonPrefStore::CommitPendingWrite() {
DCHECK(CalledOnValidThread()); DCHECK(CalledOnValidThread());
// Schedule a write for any lossy writes that are outstanding to ensure that
// they get flushed when this function is called.
if (pending_lossy_write_)
writer_.ScheduleWrite(this);
if (writer_.HasPendingWrite() && !read_only_) if (writer_.HasPendingWrite() && !read_only_)
writer_.DoScheduledWrite(); writer_.DoScheduledWrite();
} }
...@@ -321,8 +318,7 @@ void JsonPrefStore::ReportValueChanged(const std::string& key, uint32 flags) { ...@@ -321,8 +318,7 @@ void JsonPrefStore::ReportValueChanged(const std::string& key, uint32 flags) {
FOR_EACH_OBSERVER(PrefStore::Observer, observers_, OnPrefValueChanged(key)); FOR_EACH_OBSERVER(PrefStore::Observer, observers_, OnPrefValueChanged(key));
if (!read_only_) ScheduleWrite(flags);
writer_.ScheduleWrite(this);
} }
void JsonPrefStore::RegisterOnNextSuccessfulWriteCallback( void JsonPrefStore::RegisterOnNextSuccessfulWriteCallback(
...@@ -401,6 +397,8 @@ JsonPrefStore::~JsonPrefStore() { ...@@ -401,6 +397,8 @@ JsonPrefStore::~JsonPrefStore() {
bool JsonPrefStore::SerializeData(std::string* output) { bool JsonPrefStore::SerializeData(std::string* output) {
DCHECK(CalledOnValidThread()); DCHECK(CalledOnValidThread());
pending_lossy_write_ = false;
write_count_histogram_.RecordWriteOccured(); write_count_histogram_.RecordWriteOccured();
if (pref_filter_) if (pref_filter_)
...@@ -432,8 +430,8 @@ void JsonPrefStore::FinalizeFileRead(bool initialization_successful, ...@@ -432,8 +430,8 @@ void JsonPrefStore::FinalizeFileRead(bool initialization_successful,
initialized_ = true; initialized_ = true;
if (schedule_write && !read_only_) if (schedule_write)
writer_.ScheduleWrite(this); ScheduleWrite(DEFAULT_PREF_WRITE_FLAGS);
if (error_delegate_ && read_error_ != PREF_READ_ERROR_NONE) if (error_delegate_ && read_error_ != PREF_READ_ERROR_NONE)
error_delegate_->OnError(read_error_); error_delegate_->OnError(read_error_);
...@@ -445,6 +443,16 @@ void JsonPrefStore::FinalizeFileRead(bool initialization_successful, ...@@ -445,6 +443,16 @@ void JsonPrefStore::FinalizeFileRead(bool initialization_successful,
return; return;
} }
void JsonPrefStore::ScheduleWrite(uint32 flags) {
if (read_only_)
return;
if (flags & LOSSY_PREF_WRITE_FLAG)
pending_lossy_write_ = true;
else
writer_.ScheduleWrite(this);
}
// NOTE: This value should NOT be changed without renaming the histogram // NOTE: This value should NOT be changed without renaming the histogram
// otherwise it will create incompatible buckets. // otherwise it will create incompatible buckets.
const int32_t const int32_t
......
...@@ -29,6 +29,7 @@ class Clock; ...@@ -29,6 +29,7 @@ class Clock;
class DictionaryValue; class DictionaryValue;
class FilePath; class FilePath;
class HistogramBase; class HistogramBase;
class JsonPrefStoreLossyWriteTest;
class SequencedTaskRunner; class SequencedTaskRunner;
class SequencedWorkerPool; class SequencedWorkerPool;
class Value; class Value;
...@@ -165,6 +166,7 @@ class BASE_PREFS_EXPORT JsonPrefStore ...@@ -165,6 +166,7 @@ class BASE_PREFS_EXPORT JsonPrefStore
WriteCountHistogramTestMultiplePeriods); WriteCountHistogramTestMultiplePeriods);
FRIEND_TEST_ALL_PREFIXES(base::JsonPrefStoreTest, FRIEND_TEST_ALL_PREFIXES(base::JsonPrefStoreTest,
WriteCountHistogramTestPeriodWithGaps); WriteCountHistogramTestPeriodWithGaps);
friend class base::JsonPrefStoreLossyWriteTest;
~JsonPrefStore() override; ~JsonPrefStore() override;
...@@ -190,6 +192,10 @@ class BASE_PREFS_EXPORT JsonPrefStore ...@@ -190,6 +192,10 @@ class BASE_PREFS_EXPORT JsonPrefStore
scoped_ptr<base::DictionaryValue> prefs, scoped_ptr<base::DictionaryValue> prefs,
bool schedule_write); bool schedule_write);
// Schedule a write with the file writer as long as |flags| doesn't contain
// WriteablePrefStore::LOSSY_PREF_WRITE_FLAG.
void ScheduleWrite(uint32 flags);
const base::FilePath path_; const base::FilePath path_;
const base::FilePath alternate_path_; const base::FilePath alternate_path_;
const scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner_; const scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner_;
...@@ -208,6 +214,7 @@ class BASE_PREFS_EXPORT JsonPrefStore ...@@ -208,6 +214,7 @@ class BASE_PREFS_EXPORT JsonPrefStore
bool initialized_; bool initialized_;
bool filtering_in_progress_; bool filtering_in_progress_;
bool pending_lossy_write_;
PrefReadError read_error_; PrefReadError read_error_;
std::set<std::string> keys_need_empty_value_; std::set<std::string> keys_need_empty_value_;
......
...@@ -108,9 +108,7 @@ class JsonPrefStoreTest : public testing::Test { ...@@ -108,9 +108,7 @@ class JsonPrefStoreTest : public testing::Test {
void TearDown() override { void TearDown() override {
// Make sure all pending tasks have been processed (e.g., deleting the // Make sure all pending tasks have been processed (e.g., deleting the
// JsonPrefStore may post write tasks). // JsonPrefStore may post write tasks).
message_loop_.task_runner()->PostTask(FROM_HERE, RunLoop().RunUntilIdle();
MessageLoop::QuitWhenIdleClosure());
message_loop_.Run();
} }
// The path to temporary directory used to contain the test operations. // The path to temporary directory used to contain the test operations.
...@@ -127,7 +125,7 @@ class JsonPrefStoreTest : public testing::Test { ...@@ -127,7 +125,7 @@ class JsonPrefStoreTest : public testing::Test {
// Test fallback behavior for a nonexistent file. // Test fallback behavior for a nonexistent file.
TEST_F(JsonPrefStoreTest, NonExistentFile) { TEST_F(JsonPrefStoreTest, NonExistentFile) {
base::FilePath bogus_input_file = data_dir_.AppendASCII("read.txt"); base::FilePath bogus_input_file = temp_dir_.path().AppendASCII("read.txt");
ASSERT_FALSE(PathExists(bogus_input_file)); ASSERT_FALSE(PathExists(bogus_input_file));
scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore( scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
bogus_input_file, message_loop_.task_runner(), scoped_ptr<PrefFilter>()); bogus_input_file, message_loop_.task_runner(), scoped_ptr<PrefFilter>());
...@@ -138,9 +136,9 @@ TEST_F(JsonPrefStoreTest, NonExistentFile) { ...@@ -138,9 +136,9 @@ TEST_F(JsonPrefStoreTest, NonExistentFile) {
// Test fallback behavior for a nonexistent file and alternate file. // Test fallback behavior for a nonexistent file and alternate file.
TEST_F(JsonPrefStoreTest, NonExistentFileAndAlternateFile) { TEST_F(JsonPrefStoreTest, NonExistentFileAndAlternateFile) {
base::FilePath bogus_input_file = data_dir_.AppendASCII("read.txt"); base::FilePath bogus_input_file = temp_dir_.path().AppendASCII("read.txt");
base::FilePath bogus_alternate_input_file = base::FilePath bogus_alternate_input_file =
data_dir_.AppendASCII("read_alternate.txt"); temp_dir_.path().AppendASCII("read_alternate.txt");
ASSERT_FALSE(PathExists(bogus_input_file)); ASSERT_FALSE(PathExists(bogus_input_file));
ASSERT_FALSE(PathExists(bogus_alternate_input_file)); ASSERT_FALSE(PathExists(bogus_alternate_input_file));
scoped_refptr<JsonPrefStore> pref_store = scoped_refptr<JsonPrefStore> pref_store =
...@@ -321,7 +319,7 @@ TEST_F(JsonPrefStoreTest, PreserveEmptyValues) { ...@@ -321,7 +319,7 @@ TEST_F(JsonPrefStoreTest, PreserveEmptyValues) {
// Write to file. // Write to file.
pref_store->CommitPendingWrite(); pref_store->CommitPendingWrite();
MessageLoop::current()->RunUntilIdle(); RunLoop().RunUntilIdle();
// Reload. // Reload.
pref_store = new JsonPrefStore(pref_file, message_loop_.task_runner(), pref_store = new JsonPrefStore(pref_file, message_loop_.task_runner(),
...@@ -360,7 +358,7 @@ TEST_F(JsonPrefStoreTest, RemoveClearsEmptyParent) { ...@@ -360,7 +358,7 @@ TEST_F(JsonPrefStoreTest, RemoveClearsEmptyParent) {
// Tests asynchronous reading of the file when there is no file. // Tests asynchronous reading of the file when there is no file.
TEST_F(JsonPrefStoreTest, AsyncNonExistingFile) { TEST_F(JsonPrefStoreTest, AsyncNonExistingFile) {
base::FilePath bogus_input_file = data_dir_.AppendASCII("read.txt"); base::FilePath bogus_input_file = temp_dir_.path().AppendASCII("read.txt");
ASSERT_FALSE(PathExists(bogus_input_file)); ASSERT_FALSE(PathExists(bogus_input_file));
scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore( scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
bogus_input_file, message_loop_.task_runner(), scoped_ptr<PrefFilter>()); bogus_input_file, message_loop_.task_runner(), scoped_ptr<PrefFilter>());
...@@ -809,4 +807,125 @@ TEST_F(JsonPrefStoreTest, WriteCountHistogramTestPeriodWithGaps) { ...@@ -809,4 +807,125 @@ TEST_F(JsonPrefStoreTest, WriteCountHistogramTestPeriodWithGaps) {
ASSERT_EQ(6, samples->TotalCount()); ASSERT_EQ(6, samples->TotalCount());
} }
class JsonPrefStoreLossyWriteTest : public JsonPrefStoreTest {
protected:
void SetUp() override {
JsonPrefStoreTest::SetUp();
test_file_ = temp_dir_.path().AppendASCII("test.json");
}
// Creates a JsonPrefStore with the given |file_writer|.
scoped_refptr<JsonPrefStore> CreatePrefStore() {
return new JsonPrefStore(test_file_, message_loop_.task_runner(),
scoped_ptr<PrefFilter>());
}
// Return the ImportantFileWriter for a given JsonPrefStore.
ImportantFileWriter* GetImportantFileWriter(
scoped_refptr<JsonPrefStore> pref_store) {
return &(pref_store->writer_);
}
// Get the contents of kTestFile. Pumps the message loop before returning the
// result.
std::string GetTestFileContents() {
RunLoop().RunUntilIdle();
std::string file_contents;
ReadFileToString(test_file_, &file_contents);
return file_contents;
}
private:
base::FilePath test_file_;
};
TEST_F(JsonPrefStoreLossyWriteTest, LossyWriteBasic) {
scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore();
ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store);
// Set a normal pref and check that it gets scheduled to be written.
ASSERT_FALSE(file_writer->HasPendingWrite());
pref_store->SetValue("normal", new base::StringValue("normal"),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
ASSERT_TRUE(file_writer->HasPendingWrite());
file_writer->DoScheduledWrite();
ASSERT_EQ("{\"normal\":\"normal\"}", GetTestFileContents());
ASSERT_FALSE(file_writer->HasPendingWrite());
// Set a lossy pref and check that it is not scheduled to be written.
// SetValue/RemoveValue.
pref_store->SetValue("lossy", new base::StringValue("lossy"),
WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
ASSERT_FALSE(file_writer->HasPendingWrite());
pref_store->RemoveValue("lossy", WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
ASSERT_FALSE(file_writer->HasPendingWrite());
// SetValueSilently/RemoveValueSilently.
pref_store->SetValueSilently("lossy", new base::StringValue("lossy"),
WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
ASSERT_FALSE(file_writer->HasPendingWrite());
pref_store->RemoveValueSilently("lossy",
WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
ASSERT_FALSE(file_writer->HasPendingWrite());
// ReportValueChanged.
pref_store->SetValue("lossy", new base::StringValue("lossy"),
WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
ASSERT_FALSE(file_writer->HasPendingWrite());
pref_store->ReportValueChanged("lossy",
WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
ASSERT_FALSE(file_writer->HasPendingWrite());
// Call CommitPendingWrite and check that the lossy pref and the normal pref
// are there with the last values set above.
pref_store->CommitPendingWrite();
ASSERT_FALSE(file_writer->HasPendingWrite());
ASSERT_EQ("{\"lossy\":\"lossy\",\"normal\":\"normal\"}",
GetTestFileContents());
}
TEST_F(JsonPrefStoreLossyWriteTest, LossyWriteMixedLossyFirst) {
scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore();
ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store);
// Set a lossy pref and check that it is not scheduled to be written.
ASSERT_FALSE(file_writer->HasPendingWrite());
pref_store->SetValue("lossy", new base::StringValue("lossy"),
WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
ASSERT_FALSE(file_writer->HasPendingWrite());
// Set a normal pref and check that it is scheduled to be written.
pref_store->SetValue("normal", new base::StringValue("normal"),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
ASSERT_TRUE(file_writer->HasPendingWrite());
// Call DoScheduledWrite and check both prefs get written.
file_writer->DoScheduledWrite();
ASSERT_EQ("{\"lossy\":\"lossy\",\"normal\":\"normal\"}",
GetTestFileContents());
ASSERT_FALSE(file_writer->HasPendingWrite());
}
TEST_F(JsonPrefStoreLossyWriteTest, LossyWriteMixedLossySecond) {
scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore();
ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store);
// Set a normal pref and check that it is scheduled to be written.
ASSERT_FALSE(file_writer->HasPendingWrite());
pref_store->SetValue("normal", new base::StringValue("normal"),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
ASSERT_TRUE(file_writer->HasPendingWrite());
// Set a lossy pref and check that the write is still scheduled.
pref_store->SetValue("lossy", new base::StringValue("lossy"),
WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
ASSERT_TRUE(file_writer->HasPendingWrite());
// Call DoScheduledWrite and check both prefs get written.
file_writer->DoScheduledWrite();
ASSERT_EQ("{\"lossy\":\"lossy\",\"normal\":\"normal\"}",
GetTestFileContents());
ASSERT_FALSE(file_writer->HasPendingWrite());
}
} // namespace base } // namespace base
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment