Commit 788eaf69 authored by Gabriel Charette's avatar Gabriel Charette Committed by Commit Bot

[PrefService] Introduce a synchronous option to CommitPendingWrite()

This is required to remove the |local_state_task_runner| member
of BrowserProcessImpl only used to implicitly wait on pref store.
Ref. https://crrev.com/c/1163628.
Synchronous callback semantics are required on EndSession() as a nested
RunLoop is not suitable to observe a reply.
https://chromium-review.googlesource.com/c/chromium/src/+/1163628/8/chrome/browser/browser_process_impl.cc#594

Also implemented in services/preferences' SegregatedPrefStore but
not in the Mojom interface where I don't think it's used yet? Or if
it is then it was already wrong as |local_state_task_runner| is
decoupled from that Mojom. The DCHECK will tell and make this future
proof.

Bug: 848615
Cq-Include-Trybots: luci.chromium.try:linux_mojo;master.tryserver.chromium.android:android_cronet_tester;master.tryserver.chromium.mac:ios-simulator-cronet
Change-Id: Ie72f2d30d30bfa7f96a04d780d1591949a173b78
Reviewed-on: https://chromium-review.googlesource.com/1164522Reviewed-by: default avatarJonathan Ross <jonross@chromium.org>
Reviewed-by: default avatarBernhard Bauer <bauerb@chromium.org>
Commit-Queue: Gabriel Charette <gab@chromium.org>
Cr-Commit-Position: refs/heads/master@{#581324}
parent 920d2d6b
...@@ -272,7 +272,9 @@ void JsonPrefStore::ReadPrefsAsync(ReadErrorDelegate* error_delegate) { ...@@ -272,7 +272,9 @@ void JsonPrefStore::ReadPrefsAsync(ReadErrorDelegate* error_delegate) {
base::Bind(&JsonPrefStore::OnFileRead, AsWeakPtr())); base::Bind(&JsonPrefStore::OnFileRead, AsWeakPtr()));
} }
void JsonPrefStore::CommitPendingWrite(base::OnceClosure done_callback) { void JsonPrefStore::CommitPendingWrite(
base::OnceClosure reply_callback,
base::OnceClosure synchronous_done_callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Schedule a write for any lossy writes that are outstanding to ensure that // Schedule a write for any lossy writes that are outstanding to ensure that
...@@ -282,13 +284,19 @@ void JsonPrefStore::CommitPendingWrite(base::OnceClosure done_callback) { ...@@ -282,13 +284,19 @@ void JsonPrefStore::CommitPendingWrite(base::OnceClosure done_callback) {
if (writer_.HasPendingWrite() && !read_only_) if (writer_.HasPendingWrite() && !read_only_)
writer_.DoScheduledWrite(); writer_.DoScheduledWrite();
if (done_callback) {
// Since disk operations occur on |file_task_runner_|, the reply of a task // Since disk operations occur on |file_task_runner_|, the reply of a task
// posted to |file_task_runner_| will run after currently pending disk // posted to |file_task_runner_| will run after currently pending disk
// operations. Also, by definition of PostTaskAndReply(), the reply will run // operations. Also, by definition of PostTaskAndReply(), the reply (in the
// on the current sequence. // |reply_callback| case will run on the current sequence.
if (synchronous_done_callback) {
file_task_runner_->PostTask(FROM_HERE,
std::move(synchronous_done_callback));
}
if (reply_callback) {
file_task_runner_->PostTaskAndReply(FROM_HERE, base::DoNothing(), file_task_runner_->PostTaskAndReply(FROM_HERE, base::DoNothing(),
std::move(done_callback)); std::move(reply_callback));
} }
} }
...@@ -444,7 +452,7 @@ void JsonPrefStore::OnFileRead(std::unique_ptr<ReadResult> read_result) { ...@@ -444,7 +452,7 @@ void JsonPrefStore::OnFileRead(std::unique_ptr<ReadResult> read_result) {
JsonPrefStore::~JsonPrefStore() { JsonPrefStore::~JsonPrefStore() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CommitPendingWrite(base::OnceClosure()); CommitPendingWrite();
} }
bool JsonPrefStore::SerializeData(std::string* output) { bool JsonPrefStore::SerializeData(std::string* output) {
......
...@@ -94,7 +94,10 @@ class COMPONENTS_PREFS_EXPORT JsonPrefStore ...@@ -94,7 +94,10 @@ class COMPONENTS_PREFS_EXPORT JsonPrefStore
// See details in pref_filter.h. // See details in pref_filter.h.
PrefReadError ReadPrefs() override; PrefReadError ReadPrefs() override;
void ReadPrefsAsync(ReadErrorDelegate* error_delegate) override; void ReadPrefsAsync(ReadErrorDelegate* error_delegate) override;
void CommitPendingWrite(base::OnceClosure done_callback) override; void CommitPendingWrite(
base::OnceClosure reply_callback = base::OnceClosure(),
base::OnceClosure synchronous_done_callback =
base::OnceClosure()) override;
void SchedulePendingLossyWrites() override; void SchedulePendingLossyWrites() override;
void ReportValueChanged(const std::string& key, uint32_t flags) override; void ReportValueChanged(const std::string& key, uint32_t flags) override;
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <utility> #include <utility>
#include "base/bind.h" #include "base/bind.h"
#include "base/compiler_specific.h"
#include "base/files/file_util.h" #include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h" #include "base/files/scoped_temp_dir.h"
#include "base/location.h" #include "base/location.h"
...@@ -22,6 +23,7 @@ ...@@ -22,6 +23,7 @@
#include "base/strings/string_number_conversions.h" #include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h" #include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "base/synchronization/waitable_event.h"
#include "base/test/metrics/histogram_tester.h" #include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_task_environment.h" #include "base/test/scoped_task_environment.h"
#include "base/test/simple_test_clock.h" #include "base/test/simple_test_clock.h"
...@@ -130,19 +132,50 @@ class MockReadErrorDelegate : public PersistentPrefStore::ReadErrorDelegate { ...@@ -130,19 +132,50 @@ class MockReadErrorDelegate : public PersistentPrefStore::ReadErrorDelegate {
}; };
enum class CommitPendingWriteMode { enum class CommitPendingWriteMode {
// Basic mode.
WITHOUT_CALLBACK, WITHOUT_CALLBACK,
// With reply callback.
WITH_CALLBACK, WITH_CALLBACK,
// With synchronous notify callback (synchronous after the write -- shouldn't
// require pumping messages to observe).
WITH_SYNCHRONOUS_CALLBACK,
}; };
base::test::ScopedTaskEnvironment::ExecutionMode GetExecutionMode(
CommitPendingWriteMode commit_mode) {
switch (commit_mode) {
case CommitPendingWriteMode::WITHOUT_CALLBACK:
FALLTHROUGH;
case CommitPendingWriteMode::WITH_CALLBACK:
return base::test::ScopedTaskEnvironment::ExecutionMode::QUEUED;
case CommitPendingWriteMode::WITH_SYNCHRONOUS_CALLBACK:
// Synchronous callbacks require async tasks to run on their own.
return base::test::ScopedTaskEnvironment::ExecutionMode::ASYNC;
}
}
void CommitPendingWrite( void CommitPendingWrite(
JsonPrefStore* pref_store, JsonPrefStore* pref_store,
CommitPendingWriteMode commit_pending_write_mode, CommitPendingWriteMode commit_pending_write_mode,
base::test::ScopedTaskEnvironment* scoped_task_environment) { base::test::ScopedTaskEnvironment* scoped_task_environment) {
if (commit_pending_write_mode == CommitPendingWriteMode::WITHOUT_CALLBACK) { switch (commit_pending_write_mode) {
pref_store->CommitPendingWrite(OnceClosure()); case CommitPendingWriteMode::WITHOUT_CALLBACK: {
pref_store->CommitPendingWrite();
scoped_task_environment->RunUntilIdle(); scoped_task_environment->RunUntilIdle();
} else { break;
}
case CommitPendingWriteMode::WITH_CALLBACK: {
TestCommitPendingWriteWithCallback(pref_store, scoped_task_environment); TestCommitPendingWriteWithCallback(pref_store, scoped_task_environment);
break;
}
case CommitPendingWriteMode::WITH_SYNCHRONOUS_CALLBACK: {
base::WaitableEvent written;
pref_store->CommitPendingWrite(
base::OnceClosure(),
base::BindOnce(&base::WaitableEvent::Signal, Unretained(&written)));
written.Wait();
break;
}
} }
} }
...@@ -152,7 +185,7 @@ class JsonPrefStoreTest ...@@ -152,7 +185,7 @@ class JsonPrefStoreTest
JsonPrefStoreTest() JsonPrefStoreTest()
: scoped_task_environment_( : scoped_task_environment_(
base::test::ScopedTaskEnvironment::MainThreadType::DEFAULT, base::test::ScopedTaskEnvironment::MainThreadType::DEFAULT,
base::test::ScopedTaskEnvironment::ExecutionMode::QUEUED) {} GetExecutionMode(GetParam())) {}
protected: protected:
void SetUp() override { void SetUp() override {
...@@ -170,7 +203,7 @@ class JsonPrefStoreTest ...@@ -170,7 +203,7 @@ class JsonPrefStoreTest
} // namespace } // namespace
// Test fallback behavior for a nonexistent file. // Test fallback behavior for a nonexistent file.
TEST_F(JsonPrefStoreTest, NonExistentFile) { TEST_P(JsonPrefStoreTest, NonExistentFile) {
base::FilePath bogus_input_file = temp_dir_.GetPath().AppendASCII("read.txt"); base::FilePath bogus_input_file = temp_dir_.GetPath().AppendASCII("read.txt");
ASSERT_FALSE(PathExists(bogus_input_file)); ASSERT_FALSE(PathExists(bogus_input_file));
auto pref_store = base::MakeRefCounted<JsonPrefStore>(bogus_input_file); auto pref_store = base::MakeRefCounted<JsonPrefStore>(bogus_input_file);
...@@ -180,7 +213,7 @@ TEST_F(JsonPrefStoreTest, NonExistentFile) { ...@@ -180,7 +213,7 @@ TEST_F(JsonPrefStoreTest, NonExistentFile) {
} }
// Test fallback behavior for an invalid file. // Test fallback behavior for an invalid file.
TEST_F(JsonPrefStoreTest, InvalidFile) { TEST_P(JsonPrefStoreTest, InvalidFile) {
base::FilePath invalid_file = temp_dir_.GetPath().AppendASCII("invalid.json"); base::FilePath invalid_file = temp_dir_.GetPath().AppendASCII("invalid.json");
ASSERT_LT(0, base::WriteFile(invalid_file, ASSERT_LT(0, base::WriteFile(invalid_file,
kInvalidJson, arraysize(kInvalidJson) - 1)); kInvalidJson, arraysize(kInvalidJson) - 1));
...@@ -371,7 +404,7 @@ TEST_P(JsonPrefStoreTest, PreserveEmptyValues) { ...@@ -371,7 +404,7 @@ TEST_P(JsonPrefStoreTest, PreserveEmptyValues) {
// This test is just documenting some potentially non-obvious behavior. It // This test is just documenting some potentially non-obvious behavior. It
// shouldn't be taken as normative. // shouldn't be taken as normative.
TEST_F(JsonPrefStoreTest, RemoveClearsEmptyParent) { TEST_P(JsonPrefStoreTest, RemoveClearsEmptyParent) {
FilePath pref_file = temp_dir_.GetPath().AppendASCII("empty_values.json"); FilePath pref_file = temp_dir_.GetPath().AppendASCII("empty_values.json");
auto pref_store = base::MakeRefCounted<JsonPrefStore>(pref_file); auto pref_store = base::MakeRefCounted<JsonPrefStore>(pref_file);
...@@ -390,7 +423,7 @@ TEST_F(JsonPrefStoreTest, RemoveClearsEmptyParent) { ...@@ -390,7 +423,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_P(JsonPrefStoreTest, AsyncNonExistingFile) {
base::FilePath bogus_input_file = temp_dir_.GetPath().AppendASCII("read.txt"); base::FilePath bogus_input_file = temp_dir_.GetPath().AppendASCII("read.txt");
ASSERT_FALSE(PathExists(bogus_input_file)); ASSERT_FALSE(PathExists(bogus_input_file));
auto pref_store = base::MakeRefCounted<JsonPrefStore>(bogus_input_file); auto pref_store = base::MakeRefCounted<JsonPrefStore>(bogus_input_file);
...@@ -514,7 +547,7 @@ TEST_P(JsonPrefStoreTest, ReadAsyncWithInterceptor) { ...@@ -514,7 +547,7 @@ TEST_P(JsonPrefStoreTest, ReadAsyncWithInterceptor) {
&scoped_task_environment_); &scoped_task_environment_);
} }
TEST_F(JsonPrefStoreTest, WriteCountHistogramTestBasic) { TEST_P(JsonPrefStoreTest, WriteCountHistogramTestBasic) {
base::HistogramTester histogram_tester; base::HistogramTester histogram_tester;
SimpleTestClock* test_clock = new SimpleTestClock; SimpleTestClock* test_clock = new SimpleTestClock;
...@@ -542,7 +575,7 @@ TEST_F(JsonPrefStoreTest, WriteCountHistogramTestBasic) { ...@@ -542,7 +575,7 @@ TEST_F(JsonPrefStoreTest, WriteCountHistogramTestBasic) {
ASSERT_TRUE(histogram.GetHistogram()->HasConstructionArguments(1, 30, 31)); ASSERT_TRUE(histogram.GetHistogram()->HasConstructionArguments(1, 30, 31));
} }
TEST_F(JsonPrefStoreTest, WriteCountHistogramTestSinglePeriod) { TEST_P(JsonPrefStoreTest, WriteCountHistogramTestSinglePeriod) {
base::HistogramTester histogram_tester; base::HistogramTester histogram_tester;
SimpleTestClock* test_clock = new SimpleTestClock; SimpleTestClock* test_clock = new SimpleTestClock;
...@@ -580,7 +613,7 @@ TEST_F(JsonPrefStoreTest, WriteCountHistogramTestSinglePeriod) { ...@@ -580,7 +613,7 @@ TEST_F(JsonPrefStoreTest, WriteCountHistogramTestSinglePeriod) {
histogram_tester.ExpectTotalCount(histogram_name, 1); histogram_tester.ExpectTotalCount(histogram_name, 1);
} }
TEST_F(JsonPrefStoreTest, WriteCountHistogramTestMultiplePeriods) { TEST_P(JsonPrefStoreTest, WriteCountHistogramTestMultiplePeriods) {
base::HistogramTester histogram_tester; base::HistogramTester histogram_tester;
SimpleTestClock* test_clock = new SimpleTestClock; SimpleTestClock* test_clock = new SimpleTestClock;
...@@ -620,7 +653,7 @@ TEST_F(JsonPrefStoreTest, WriteCountHistogramTestMultiplePeriods) { ...@@ -620,7 +653,7 @@ TEST_F(JsonPrefStoreTest, WriteCountHistogramTestMultiplePeriods) {
histogram_tester.ExpectTotalCount(histogram_name, 3); histogram_tester.ExpectTotalCount(histogram_name, 3);
} }
TEST_F(JsonPrefStoreTest, WriteCountHistogramTestPeriodWithGaps) { TEST_P(JsonPrefStoreTest, WriteCountHistogramTestPeriodWithGaps) {
base::HistogramTester histogram_tester; base::HistogramTester histogram_tester;
SimpleTestClock* test_clock = new SimpleTestClock; SimpleTestClock* test_clock = new SimpleTestClock;
...@@ -671,6 +704,10 @@ INSTANTIATE_TEST_CASE_P( ...@@ -671,6 +704,10 @@ INSTANTIATE_TEST_CASE_P(
WithCallback, WithCallback,
JsonPrefStoreTest, JsonPrefStoreTest,
::testing::Values(CommitPendingWriteMode::WITH_CALLBACK)); ::testing::Values(CommitPendingWriteMode::WITH_CALLBACK));
INSTANTIATE_TEST_CASE_P(
WithSynchronousCallback,
JsonPrefStoreTest,
::testing::Values(CommitPendingWriteMode::WITH_SYNCHRONOUS_CALLBACK));
class JsonPrefStoreLossyWriteTest : public JsonPrefStoreTest { class JsonPrefStoreLossyWriteTest : public JsonPrefStoreTest {
public: public:
...@@ -706,7 +743,7 @@ class JsonPrefStoreLossyWriteTest : public JsonPrefStoreTest { ...@@ -706,7 +743,7 @@ class JsonPrefStoreLossyWriteTest : public JsonPrefStoreTest {
DISALLOW_COPY_AND_ASSIGN(JsonPrefStoreLossyWriteTest); DISALLOW_COPY_AND_ASSIGN(JsonPrefStoreLossyWriteTest);
}; };
TEST_F(JsonPrefStoreLossyWriteTest, LossyWriteBasic) { TEST_P(JsonPrefStoreLossyWriteTest, LossyWriteBasic) {
scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore(); scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore();
ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store.get()); ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store.get());
...@@ -751,7 +788,7 @@ TEST_F(JsonPrefStoreLossyWriteTest, LossyWriteBasic) { ...@@ -751,7 +788,7 @@ TEST_F(JsonPrefStoreLossyWriteTest, LossyWriteBasic) {
GetTestFileContents()); GetTestFileContents());
} }
TEST_F(JsonPrefStoreLossyWriteTest, LossyWriteMixedLossyFirst) { TEST_P(JsonPrefStoreLossyWriteTest, LossyWriteMixedLossyFirst) {
scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore(); scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore();
ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store.get()); ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store.get());
...@@ -773,7 +810,7 @@ TEST_F(JsonPrefStoreLossyWriteTest, LossyWriteMixedLossyFirst) { ...@@ -773,7 +810,7 @@ TEST_F(JsonPrefStoreLossyWriteTest, LossyWriteMixedLossyFirst) {
ASSERT_FALSE(file_writer->HasPendingWrite()); ASSERT_FALSE(file_writer->HasPendingWrite());
} }
TEST_F(JsonPrefStoreLossyWriteTest, LossyWriteMixedLossySecond) { TEST_P(JsonPrefStoreLossyWriteTest, LossyWriteMixedLossySecond) {
scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore(); scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore();
ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store.get()); ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store.get());
...@@ -795,7 +832,7 @@ TEST_F(JsonPrefStoreLossyWriteTest, LossyWriteMixedLossySecond) { ...@@ -795,7 +832,7 @@ TEST_F(JsonPrefStoreLossyWriteTest, LossyWriteMixedLossySecond) {
ASSERT_FALSE(file_writer->HasPendingWrite()); ASSERT_FALSE(file_writer->HasPendingWrite());
} }
TEST_F(JsonPrefStoreLossyWriteTest, ScheduleLossyWrite) { TEST_P(JsonPrefStoreLossyWriteTest, ScheduleLossyWrite) {
scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore(); scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore();
ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store.get()); ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store.get());
...@@ -815,6 +852,19 @@ TEST_F(JsonPrefStoreLossyWriteTest, ScheduleLossyWrite) { ...@@ -815,6 +852,19 @@ TEST_F(JsonPrefStoreLossyWriteTest, ScheduleLossyWrite) {
ASSERT_EQ("{\"lossy\":\"lossy\"}", GetTestFileContents()); ASSERT_EQ("{\"lossy\":\"lossy\"}", GetTestFileContents());
} }
INSTANTIATE_TEST_CASE_P(
WithoutCallback,
JsonPrefStoreLossyWriteTest,
::testing::Values(CommitPendingWriteMode::WITHOUT_CALLBACK));
INSTANTIATE_TEST_CASE_P(
WithReply,
JsonPrefStoreLossyWriteTest,
::testing::Values(CommitPendingWriteMode::WITH_CALLBACK));
INSTANTIATE_TEST_CASE_P(
WithNotify,
JsonPrefStoreLossyWriteTest,
::testing::Values(CommitPendingWriteMode::WITH_SYNCHRONOUS_CALLBACK));
class SuccessfulWriteReplyObserver { class SuccessfulWriteReplyObserver {
public: public:
SuccessfulWriteReplyObserver() = default; SuccessfulWriteReplyObserver() = default;
...@@ -912,13 +962,13 @@ WriteCallbacksObserver::GetAndResetPostWriteObservationState() { ...@@ -912,13 +962,13 @@ WriteCallbacksObserver::GetAndResetPostWriteObservationState() {
return state; return state;
} }
class JsonPrefStoreCallbackTest : public JsonPrefStoreTest { class JsonPrefStoreCallbackTest : public testing::Test {
public: public:
JsonPrefStoreCallbackTest() = default; JsonPrefStoreCallbackTest() = default;
protected: protected:
void SetUp() override { void SetUp() override {
JsonPrefStoreTest::SetUp(); ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
test_file_ = temp_dir_.GetPath().AppendASCII("test.json"); test_file_ = temp_dir_.GetPath().AppendASCII("test.json");
} }
...@@ -943,6 +993,13 @@ class JsonPrefStoreCallbackTest : public JsonPrefStoreTest { ...@@ -943,6 +993,13 @@ class JsonPrefStoreCallbackTest : public JsonPrefStoreTest {
SuccessfulWriteReplyObserver successful_write_reply_observer_; SuccessfulWriteReplyObserver successful_write_reply_observer_;
WriteCallbacksObserver write_callback_observer_; WriteCallbacksObserver write_callback_observer_;
protected:
base::test::ScopedTaskEnvironment scoped_task_environment_{
base::test::ScopedTaskEnvironment::MainThreadType::DEFAULT,
base::test::ScopedTaskEnvironment::ExecutionMode::QUEUED};
base::ScopedTempDir temp_dir_;
private: private:
base::FilePath test_file_; base::FilePath test_file_;
......
...@@ -180,8 +180,11 @@ void OverlayUserPrefStore::ReadPrefsAsync( ...@@ -180,8 +180,11 @@ void OverlayUserPrefStore::ReadPrefsAsync(
OnInitializationCompleted(/* ephemeral */ false, true); OnInitializationCompleted(/* ephemeral */ false, true);
} }
void OverlayUserPrefStore::CommitPendingWrite(base::OnceClosure done_callback) { void OverlayUserPrefStore::CommitPendingWrite(
persistent_user_pref_store_->CommitPendingWrite(std::move(done_callback)); base::OnceClosure reply_callback,
base::OnceClosure synchronous_done_callback) {
persistent_user_pref_store_->CommitPendingWrite(
std::move(reply_callback), std::move(synchronous_done_callback));
// We do not write our content intentionally. // We do not write our content intentionally.
} }
......
...@@ -57,7 +57,8 @@ class COMPONENTS_PREFS_EXPORT OverlayUserPrefStore ...@@ -57,7 +57,8 @@ class COMPONENTS_PREFS_EXPORT OverlayUserPrefStore
PrefReadError GetReadError() const override; PrefReadError GetReadError() const override;
PrefReadError ReadPrefs() override; PrefReadError ReadPrefs() override;
void ReadPrefsAsync(ReadErrorDelegate* delegate) override; void ReadPrefsAsync(ReadErrorDelegate* delegate) override;
void CommitPendingWrite(base::OnceClosure done_callback) override; void CommitPendingWrite(base::OnceClosure reply_callback,
base::OnceClosure synchronous_done_callback) override;
void SchedulePendingLossyWrites() override; void SchedulePendingLossyWrites() override;
void ReportValueChanged(const std::string& key, uint32_t flags) override; void ReportValueChanged(const std::string& key, uint32_t flags) override;
......
...@@ -8,11 +8,20 @@ ...@@ -8,11 +8,20 @@
#include "base/threading/sequenced_task_runner_handle.h" #include "base/threading/sequenced_task_runner_handle.h"
void PersistentPrefStore::CommitPendingWrite(base::OnceClosure done_callback) { void PersistentPrefStore::CommitPendingWrite(
base::OnceClosure reply_callback,
base::OnceClosure synchronous_done_callback) {
// Default behavior for PersistentPrefStore implementation that don't issue // Default behavior for PersistentPrefStore implementation that don't issue
// disk operations: schedule the callback immediately. // disk operations: schedule the callback immediately.
if (done_callback) { // |synchronous_done_callback| is allowed to be invoked synchronously (and
// must be here since we have no other way to post it which isn't the current
// sequence).
if (synchronous_done_callback)
std::move(synchronous_done_callback).Run();
if (reply_callback) {
base::SequencedTaskRunnerHandle::Get()->PostTask(FROM_HERE, base::SequencedTaskRunnerHandle::Get()->PostTask(FROM_HERE,
std::move(done_callback)); std::move(reply_callback));
} }
} }
...@@ -62,10 +62,16 @@ class COMPONENTS_PREFS_EXPORT PersistentPrefStore : public WriteablePrefStore { ...@@ -62,10 +62,16 @@ class COMPONENTS_PREFS_EXPORT PersistentPrefStore : public WriteablePrefStore {
// Owns |error_delegate|. // Owns |error_delegate|.
virtual void ReadPrefsAsync(ReadErrorDelegate* error_delegate) = 0; virtual void ReadPrefsAsync(ReadErrorDelegate* error_delegate) = 0;
// Starts an asynchronous attempt to commit pending writes to disk. Posts a // Lands pending writes to disk. |reply_callback| will be posted to the
// task to run |done_callback| on the current sequence when disk operations, // current sequence when changes have been written.
// if any, are complete (even if they are unsuccessful). // |synchronous_done_callback| on the other hand will be invoked right away
virtual void CommitPendingWrite(base::OnceClosure done_callback); // wherever the writes complete (could even be invoked synchronously if no
// writes need to occur); this is useful when the current thread cannot pump
// messages to observe the reply (e.g. nested loops banned on main thread
// during shutdown). |synchronous_done_callback| must be thread-safe.
virtual void CommitPendingWrite(
base::OnceClosure reply_callback = base::OnceClosure(),
base::OnceClosure synchronous_done_callback = base::OnceClosure());
// Schedule a write if there is any lossy data pending. Unlike // Schedule a write if there is any lossy data pending. Unlike
// CommitPendingWrite() this does not immediately sync to disk, instead it // CommitPendingWrite() this does not immediately sync to disk, instead it
......
...@@ -97,13 +97,12 @@ void PrefService::InitFromStorage(bool async) { ...@@ -97,13 +97,12 @@ void PrefService::InitFromStorage(bool async) {
} }
} }
void PrefService::CommitPendingWrite() { void PrefService::CommitPendingWrite(
CommitPendingWrite(base::OnceClosure()); base::OnceClosure reply_callback,
} base::OnceClosure synchronous_done_callback) {
void PrefService::CommitPendingWrite(base::OnceClosure done_callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
user_pref_store_->CommitPendingWrite(std::move(done_callback)); user_pref_store_->CommitPendingWrite(std::move(reply_callback),
std::move(synchronous_done_callback));
} }
void PrefService::SchedulePendingLossyWrites() { void PrefService::SchedulePendingLossyWrites() {
......
...@@ -176,13 +176,16 @@ class COMPONENTS_PREFS_EXPORT PrefService { ...@@ -176,13 +176,16 @@ class COMPONENTS_PREFS_EXPORT PrefService {
virtual ~PrefService(); virtual ~PrefService();
// Lands pending writes to disk. This should only be used if we need to save // Lands pending writes to disk. This should only be used if we need to save
// immediately (basically, during shutdown). // immediately (basically, during shutdown). |reply_callback| will be posted
void CommitPendingWrite(); // to the current sequence when changes have been written.
// |synchronous_done_callback| on the other hand will be invoked right away
// Lands pending writes to disk. This should only be used if we need to save // wherever the writes complete (could even be invoked synchronously if no
// immediately. |done_callback| will be invoked when changes have been // writes need to occur); this is useful when the current thread cannot pump
// written. // messages to observe the reply (e.g. nested loops banned on main thread
void CommitPendingWrite(base::OnceClosure done_callback); // during shutdown). |synchronous_done_callback| must be thread-safe.
void CommitPendingWrite(
base::OnceClosure reply_callback = base::OnceClosure(),
base::OnceClosure synchronous_done_callback = base::OnceClosure());
// Schedule a write if there is any lossy data pending. Unlike // Schedule a write if there is any lossy data pending. Unlike
// CommitPendingWrite() this does not immediately sync to disk, instead it // CommitPendingWrite() this does not immediately sync to disk, instead it
......
...@@ -98,9 +98,12 @@ void TestingPrefStore::ReadPrefsAsync(ReadErrorDelegate* error_delegate) { ...@@ -98,9 +98,12 @@ void TestingPrefStore::ReadPrefsAsync(ReadErrorDelegate* error_delegate) {
NotifyInitializationCompleted(); NotifyInitializationCompleted();
} }
void TestingPrefStore::CommitPendingWrite(base::OnceClosure done_callback) { void TestingPrefStore::CommitPendingWrite(
base::OnceClosure reply_callback,
base::OnceClosure synchronous_done_callback) {
committed_ = true; committed_ = true;
PersistentPrefStore::CommitPendingWrite(std::move(done_callback)); PersistentPrefStore::CommitPendingWrite(std::move(reply_callback),
std::move(synchronous_done_callback));
} }
void TestingPrefStore::SchedulePendingLossyWrites() {} void TestingPrefStore::SchedulePendingLossyWrites() {}
......
...@@ -45,7 +45,8 @@ class TestingPrefStore : public PersistentPrefStore { ...@@ -45,7 +45,8 @@ class TestingPrefStore : public PersistentPrefStore {
PrefReadError GetReadError() const override; PrefReadError GetReadError() const override;
PersistentPrefStore::PrefReadError ReadPrefs() override; PersistentPrefStore::PrefReadError ReadPrefs() override;
void ReadPrefsAsync(ReadErrorDelegate* error_delegate) override; void ReadPrefsAsync(ReadErrorDelegate* error_delegate) override;
void CommitPendingWrite(base::OnceClosure done_callback) override; void CommitPendingWrite(base::OnceClosure reply_callback,
base::OnceClosure synchronous_done_callback) override;
void SchedulePendingLossyWrites() override; void SchedulePendingLossyWrites() override;
// Marks the store as having completed initialization. // Marks the store as having completed initialization.
......
...@@ -129,6 +129,10 @@ class PersistentPrefStoreImpl::Connection : public mojom::PersistentPrefStore { ...@@ -129,6 +129,10 @@ class PersistentPrefStoreImpl::Connection : public mojom::PersistentPrefStore {
} }
void CommitPendingWrite(CommitPendingWriteCallback done_callback) override { void CommitPendingWrite(CommitPendingWriteCallback done_callback) override {
// Note: PersistentPrefStore's synchronous callback part of the
// CommitPendingWrite() API isn't supported on mojom::PersistentPrefStore at
// the moment (see PersistentPrefStoreClient::CommitPendingWrite() for
// details).
pref_store_->CommitPendingWrite(std::move(done_callback)); pref_store_->CommitPendingWrite(std::move(done_callback));
} }
void SchedulePendingLossyWrites() override { void SchedulePendingLossyWrites() override {
......
...@@ -23,9 +23,12 @@ namespace { ...@@ -23,9 +23,12 @@ namespace {
class PersistentPrefStoreMock : public InMemoryPrefStore { class PersistentPrefStoreMock : public InMemoryPrefStore {
public: public:
void CommitPendingWrite(base::OnceClosure callback) override { void CommitPendingWrite(
base::OnceClosure reply_callback,
base::OnceClosure synchronous_done_callback) override {
CommitPendingWriteMock(); CommitPendingWriteMock();
InMemoryPrefStore::CommitPendingWrite(std::move(callback)); InMemoryPrefStore::CommitPendingWrite(std::move(reply_callback),
std::move(synchronous_done_callback));
} }
MOCK_METHOD0(CommitPendingWriteMock, void()); MOCK_METHOD0(CommitPendingWriteMock, void());
......
...@@ -225,11 +225,17 @@ void PersistentPrefStoreClient::ReadPrefsAsync( ...@@ -225,11 +225,17 @@ void PersistentPrefStoreClient::ReadPrefsAsync(
ReadErrorDelegate* error_delegate) {} ReadErrorDelegate* error_delegate) {}
void PersistentPrefStoreClient::CommitPendingWrite( void PersistentPrefStoreClient::CommitPendingWrite(
base::OnceClosure done_callback) { base::OnceClosure reply_callback,
base::OnceClosure synchronous_done_callback) {
// Supporting |synchronous_done_callback| semantics would require a sync IPC.
// This isn't implemented as such at the moment as this functionality isn't
// used in practice (if it ever becomes necessary, this check will fire).
DCHECK(!synchronous_done_callback);
DCHECK(pref_store_); DCHECK(pref_store_);
if (!pending_writes_.empty()) if (!pending_writes_.empty())
FlushPendingWrites(); FlushPendingWrites();
pref_store_->CommitPendingWrite(std::move(done_callback)); pref_store_->CommitPendingWrite(std::move(reply_callback));
} }
void PersistentPrefStoreClient::SchedulePendingLossyWrites() { void PersistentPrefStoreClient::SchedulePendingLossyWrites() {
...@@ -251,7 +257,7 @@ PersistentPrefStoreClient::~PersistentPrefStoreClient() { ...@@ -251,7 +257,7 @@ PersistentPrefStoreClient::~PersistentPrefStoreClient() {
if (!pref_store_) if (!pref_store_)
return; return;
CommitPendingWrite(base::OnceClosure()); CommitPendingWrite();
} }
void PersistentPrefStoreClient::QueueWrite( void PersistentPrefStoreClient::QueueWrite(
......
...@@ -54,7 +54,10 @@ class PersistentPrefStoreClient ...@@ -54,7 +54,10 @@ class PersistentPrefStoreClient
PrefReadError GetReadError() const override; PrefReadError GetReadError() const override;
PrefReadError ReadPrefs() override; PrefReadError ReadPrefs() override;
void ReadPrefsAsync(ReadErrorDelegate* error_delegate) override; void ReadPrefsAsync(ReadErrorDelegate* error_delegate) override;
void CommitPendingWrite(base::OnceClosure done_callback) override; void CommitPendingWrite(
base::OnceClosure reply_callback = base::OnceClosure(),
base::OnceClosure synchronous_done_callback =
base::OnceClosure()) override;
void SchedulePendingLossyWrites() override; void SchedulePendingLossyWrites() override;
void ClearMutableValues() override; void ClearMutableValues() override;
void OnStoreDeletionFromDisk() override; void OnStoreDeletionFromDisk() override;
......
...@@ -161,12 +161,27 @@ void SegregatedPrefStore::ReadPrefsAsync(ReadErrorDelegate* error_delegate) { ...@@ -161,12 +161,27 @@ void SegregatedPrefStore::ReadPrefsAsync(ReadErrorDelegate* error_delegate) {
selected_pref_store_->ReadPrefsAsync(NULL); selected_pref_store_->ReadPrefsAsync(NULL);
} }
void SegregatedPrefStore::CommitPendingWrite(base::OnceClosure done_callback) { void SegregatedPrefStore::CommitPendingWrite(
base::RepeatingClosure done_callback_wrapper = base::OnceClosure reply_callback,
done_callback ? base::BarrierClosure(2, std::move(done_callback)) base::OnceClosure synchronous_done_callback) {
// A BarrierClosure will run its callback wherever the last instance of the
// returned wrapper is invoked. As such it is guaranteed to respect the reply
// vs synchronous semantics assuming |default_pref_store_| and
// |selected_pref_store_| honor it.
base::RepeatingClosure reply_callback_wrapper =
reply_callback ? base::BarrierClosure(2, std::move(reply_callback))
: base::RepeatingClosure(); : base::RepeatingClosure();
default_pref_store_->CommitPendingWrite(done_callback_wrapper);
selected_pref_store_->CommitPendingWrite(done_callback_wrapper); base::RepeatingClosure synchronous_callback_wrapper =
synchronous_done_callback
? base::BarrierClosure(2, std::move(synchronous_done_callback))
: base::RepeatingClosure();
default_pref_store_->CommitPendingWrite(reply_callback_wrapper,
synchronous_callback_wrapper);
selected_pref_store_->CommitPendingWrite(reply_callback_wrapper,
synchronous_callback_wrapper);
} }
void SegregatedPrefStore::SchedulePendingLossyWrites() { void SegregatedPrefStore::SchedulePendingLossyWrites() {
......
...@@ -72,7 +72,10 @@ class SegregatedPrefStore : public PersistentPrefStore { ...@@ -72,7 +72,10 @@ class SegregatedPrefStore : public PersistentPrefStore {
PrefReadError GetReadError() const override; PrefReadError GetReadError() const override;
PrefReadError ReadPrefs() override; PrefReadError ReadPrefs() override;
void ReadPrefsAsync(ReadErrorDelegate* error_delegate) override; void ReadPrefsAsync(ReadErrorDelegate* error_delegate) override;
void CommitPendingWrite(base::OnceClosure done_callback) override; void CommitPendingWrite(
base::OnceClosure reply_callback = base::OnceClosure(),
base::OnceClosure synchronous_done_callback =
base::OnceClosure()) override;
void SchedulePendingLossyWrites() override; void SchedulePendingLossyWrites() override;
void ClearMutableValues() override; void ClearMutableValues() override;
void OnStoreDeletionFromDisk() override; void OnStoreDeletionFromDisk() override;
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "base/callback.h" #include "base/callback.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/run_loop.h" #include "base/run_loop.h"
#include "base/synchronization/waitable_event.h"
#include "base/test/scoped_task_environment.h" #include "base/test/scoped_task_environment.h"
#include "base/values.h" #include "base/values.h"
#include "components/prefs/persistent_pref_store.h" #include "components/prefs/persistent_pref_store.h"
...@@ -56,8 +57,13 @@ class MockReadErrorDelegate : public PersistentPrefStore::ReadErrorDelegate { ...@@ -56,8 +57,13 @@ class MockReadErrorDelegate : public PersistentPrefStore::ReadErrorDelegate {
}; };
enum class CommitPendingWriteMode { enum class CommitPendingWriteMode {
// Basic mode.
WITHOUT_CALLBACK, WITHOUT_CALLBACK,
// With reply callback.
WITH_CALLBACK, WITH_CALLBACK,
// With synchronous notify callback (synchronous after the write -- shouldn't
// require pumping messages to observe).
WITH_SYNCHRONOUS_CALLBACK,
}; };
class SegregatedPrefStoreTest class SegregatedPrefStoreTest
...@@ -131,13 +137,28 @@ TEST_P(SegregatedPrefStoreTest, StoreValues) { ...@@ -131,13 +137,28 @@ TEST_P(SegregatedPrefStoreTest, StoreValues) {
ASSERT_FALSE(selected_store_->committed()); ASSERT_FALSE(selected_store_->committed());
ASSERT_FALSE(default_store_->committed()); ASSERT_FALSE(default_store_->committed());
if (GetParam() == CommitPendingWriteMode::WITHOUT_CALLBACK) { switch (GetParam()) {
segregated_store_->CommitPendingWrite(base::OnceClosure()); case CommitPendingWriteMode::WITHOUT_CALLBACK: {
segregated_store_->CommitPendingWrite();
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
} else { break;
}
case CommitPendingWriteMode::WITH_CALLBACK: {
base::RunLoop run_loop; base::RunLoop run_loop;
segregated_store_->CommitPendingWrite(run_loop.QuitClosure()); segregated_store_->CommitPendingWrite(run_loop.QuitClosure());
run_loop.Run(); run_loop.Run();
break;
}
case CommitPendingWriteMode::WITH_SYNCHRONOUS_CALLBACK: {
base::WaitableEvent written;
segregated_store_->CommitPendingWrite(
base::OnceClosure(),
base::BindOnce(&base::WaitableEvent::Signal, Unretained(&written)));
written.Wait();
break;
}
} }
ASSERT_TRUE(selected_store_->committed()); ASSERT_TRUE(selected_store_->committed());
...@@ -327,3 +348,7 @@ INSTANTIATE_TEST_CASE_P( ...@@ -327,3 +348,7 @@ INSTANTIATE_TEST_CASE_P(
WithCallback, WithCallback,
SegregatedPrefStoreTest, SegregatedPrefStoreTest,
::testing::Values(CommitPendingWriteMode::WITH_CALLBACK)); ::testing::Values(CommitPendingWriteMode::WITH_CALLBACK));
INSTANTIATE_TEST_CASE_P(
WithSynchronousCallback,
SegregatedPrefStoreTest,
::testing::Values(CommitPendingWriteMode::WITH_SYNCHRONOUS_CALLBACK));
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