Commit 385d1322 authored by Rushan Suleymanov's avatar Rushan Suleymanov Committed by Commit Bot

[Sync] Fix race during device info bridge loading.

This patch fixes the case when the sync service is restarted while
device info bridge is loading. Check that the device info provider was
actually initialized, IsTrackingMetadata does not guarantee that because
the code right after calling ModelReadyToSync might not run yet.

Bug: 1055584
Change-Id: Id0c8252bde40ba8814a48a3c06f8e628612b0f4d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2072377Reviewed-by: default avatarJan Krcal <jkrcal@chromium.org>
Commit-Queue: Rushan Suleymanov <rushans@google.com>
Cr-Commit-Position: refs/heads/master@{#745433}
parent 2072c031
...@@ -206,7 +206,15 @@ void ClientTagBasedModelTypeProcessor::ConnectIfReady() { ...@@ -206,7 +206,15 @@ void ClientTagBasedModelTypeProcessor::ConnectIfReady() {
std::make_unique<ModelTypeProcessorProxy>( std::make_unique<ModelTypeProcessorProxy>(
weak_ptr_factory_for_worker_.GetWeakPtr(), weak_ptr_factory_for_worker_.GetWeakPtr(),
base::SequencedTaskRunnerHandle::Get()); base::SequencedTaskRunnerHandle::Get());
std::move(start_callback_).Run(std::move(activation_response));
// Defer invoking of |start_callback_| to avoid synchronous call from the
// |bridge_|. It might cause a situation when inside the ModelReadyToSync()
// another methods of the bridge eventually were called. This behavior would
// be complicated and be unexpected in some bridges.
// See crbug.com/1055584 for more details.
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(start_callback_),
std::move(activation_response)));
} }
bool ClientTagBasedModelTypeProcessor::IsConnected() const { bool ClientTagBasedModelTypeProcessor::IsConnected() const {
......
...@@ -98,6 +98,7 @@ class ClientTagBasedModelTypeProcessor : public ModelTypeProcessor, ...@@ -98,6 +98,7 @@ class ClientTagBasedModelTypeProcessor : public ModelTypeProcessor,
UpdateResponseDataList updates) override; UpdateResponseDataList updates) override;
// ModelTypeControllerDelegate implementation. // ModelTypeControllerDelegate implementation.
// |start_callback| will never be called synchronously.
void OnSyncStarting(const DataTypeActivationRequest& request, void OnSyncStarting(const DataTypeActivationRequest& request,
StartCallback callback) override; StartCallback callback) override;
void OnSyncStopping(SyncStopMetadataFate metadata_fate) override; void OnSyncStopping(SyncStopMetadataFate metadata_fate) override;
...@@ -238,6 +239,7 @@ class ClientTagBasedModelTypeProcessor : public ModelTypeProcessor, ...@@ -238,6 +239,7 @@ class ClientTagBasedModelTypeProcessor : public ModelTypeProcessor,
//////////////// ////////////////
// Stores the start callback in between OnSyncStarting() and ReadyToConnect(). // Stores the start callback in between OnSyncStarting() and ReadyToConnect().
// |start_callback_| will never be called synchronously.
StartCallback start_callback_; StartCallback start_callback_;
// The request context passed in as part of OnSyncStarting(). // The request context passed in as part of OnSyncStarting().
......
...@@ -103,6 +103,19 @@ class TestModelTypeSyncBridge : public FakeModelTypeSyncBridge { ...@@ -103,6 +103,19 @@ class TestModelTypeSyncBridge : public FakeModelTypeSyncBridge {
EXPECT_FALSE(data_callback_); EXPECT_FALSE(data_callback_);
} }
void OnSyncStarting(
const syncer::DataTypeActivationRequest& request) override {
FakeModelTypeSyncBridge::OnSyncStarting(request);
sync_started_ = true;
}
void ApplyStopSyncChanges(std::unique_ptr<MetadataChangeList>
delete_metadata_change_list) override {
sync_started_ = false;
FakeModelTypeSyncBridge::ApplyStopSyncChanges(
std::move(delete_metadata_change_list));
}
std::string GetStorageKey(const EntityData& entity_data) override { std::string GetStorageKey(const EntityData& entity_data) override {
get_storage_key_call_count_++; get_storage_key_call_count_++;
return FakeModelTypeSyncBridge::GetStorageKey(entity_data); return FakeModelTypeSyncBridge::GetStorageKey(entity_data);
...@@ -131,6 +144,8 @@ class TestModelTypeSyncBridge : public FakeModelTypeSyncBridge { ...@@ -131,6 +144,8 @@ class TestModelTypeSyncBridge : public FakeModelTypeSyncBridge {
int get_storage_key_call_count() const { return get_storage_key_call_count_; } int get_storage_key_call_count() const { return get_storage_key_call_count_; }
int commit_failures_count() const { return commit_failures_count_; } int commit_failures_count() const { return commit_failures_count_; }
bool sync_started() const { return sync_started_; }
// FakeModelTypeSyncBridge overrides. // FakeModelTypeSyncBridge overrides.
bool SupportsIncrementalUpdates() const override { bool SupportsIncrementalUpdates() const override {
...@@ -193,6 +208,7 @@ class TestModelTypeSyncBridge : public FakeModelTypeSyncBridge { ...@@ -193,6 +208,7 @@ class TestModelTypeSyncBridge : public FakeModelTypeSyncBridge {
const ModelType model_type_; const ModelType model_type_;
bool supports_incremental_updates_; bool supports_incremental_updates_;
bool sync_started_ = false;
// The number of times MergeSyncData has been called. // The number of times MergeSyncData has been called.
int merge_call_count_ = 0; int merge_call_count_ = 0;
...@@ -251,6 +267,7 @@ class ClientTagBasedModelTypeProcessorTest : public ::testing::Test { ...@@ -251,6 +267,7 @@ class ClientTagBasedModelTypeProcessorTest : public ::testing::Test {
void ModelReadyToSync() { void ModelReadyToSync() {
type_processor()->ModelReadyToSync(db()->CreateMetadataBatch()); type_processor()->ModelReadyToSync(db()->CreateMetadataBatch());
WaitForStartCallbackIfNeeded();
} }
void OnCommitDataLoaded() { bridge()->OnCommitDataLoaded(); } void OnCommitDataLoaded() { bridge()->OnCommitDataLoaded(); }
...@@ -267,10 +284,16 @@ class ClientTagBasedModelTypeProcessorTest : public ::testing::Test { ...@@ -267,10 +284,16 @@ class ClientTagBasedModelTypeProcessorTest : public ::testing::Test {
request.authenticated_account_id = CoreAccountId(authenticated_account_id); request.authenticated_account_id = CoreAccountId(authenticated_account_id);
request.sync_mode = sync_mode; request.sync_mode = sync_mode;
request.configuration_start_time = base::Time::Now(); request.configuration_start_time = base::Time::Now();
// |run_loop_| may exist here if OnSyncStarting is called without resetting
// state. But it is safe to remove it.
ASSERT_TRUE(!run_loop_ || !run_loop_->running());
run_loop_ = std::make_unique<base::RunLoop>();
type_processor()->OnSyncStarting( type_processor()->OnSyncStarting(
request, request,
base::BindOnce(&ClientTagBasedModelTypeProcessorTest::OnReadyToConnect, base::BindOnce(&ClientTagBasedModelTypeProcessorTest::OnReadyToConnect,
base::Unretained(this))); base::Unretained(this)));
WaitForStartCallbackIfNeeded();
} }
void DisconnectSync() { void DisconnectSync() {
...@@ -314,6 +337,7 @@ class ClientTagBasedModelTypeProcessorTest : public ::testing::Test { ...@@ -314,6 +337,7 @@ class ClientTagBasedModelTypeProcessorTest : public ::testing::Test {
IsCommitOnly(), GetModelType(), IsCommitOnly(), GetModelType(),
SupportsIncrementalUpdates()); SupportsIncrementalUpdates());
worker_ = nullptr; worker_ = nullptr;
run_loop_.reset();
CheckPostConditions(); CheckPostConditions();
} }
...@@ -384,15 +408,30 @@ class ClientTagBasedModelTypeProcessorTest : public ::testing::Test { ...@@ -384,15 +408,30 @@ class ClientTagBasedModelTypeProcessorTest : public ::testing::Test {
// side-stepping that completely and connecting directly to the real // side-stepping that completely and connecting directly to the real
// processor, since these tests are single-threaded and don't need proxies. // processor, since these tests are single-threaded and don't need proxies.
type_processor()->ConnectSync(std::move(worker)); type_processor()->ConnectSync(std::move(worker));
ASSERT_TRUE(run_loop_);
run_loop_->Quit();
} }
void ErrorReceived(const ModelError& error) { void ErrorReceived(const ModelError& error) {
EXPECT_TRUE(expect_error_); EXPECT_TRUE(expect_error_);
expect_error_ = false; expect_error_ = false;
// Do not expect for a start callback anymore.
if (run_loop_) {
run_loop_->Quit();
}
} }
sync_pb::ModelTypeState model_type_state() { return model_type_state_; } sync_pb::ModelTypeState model_type_state() { return model_type_state_; }
void WaitForStartCallbackIfNeeded() {
if (!type_processor()->IsModelReadyToSyncForTest() ||
!bridge()->sync_started()) {
return;
}
ASSERT_TRUE(run_loop_);
run_loop_->Run();
}
private: private:
std::unique_ptr<TestModelTypeSyncBridge> bridge_; std::unique_ptr<TestModelTypeSyncBridge> bridge_;
sync_pb::ModelTypeState model_type_state_; sync_pb::ModelTypeState model_type_state_;
...@@ -401,6 +440,9 @@ class ClientTagBasedModelTypeProcessorTest : public ::testing::Test { ...@@ -401,6 +440,9 @@ class ClientTagBasedModelTypeProcessorTest : public ::testing::Test {
// processor will pick up as the sync task runner. // processor will pick up as the sync task runner.
base::test::SingleThreadTaskEnvironment task_environment_; base::test::SingleThreadTaskEnvironment task_environment_;
// This run loop is used to wait for OnReadyToConnect is called.
std::unique_ptr<base::RunLoop> run_loop_;
// The current mock queue, which is owned by |type_processor()|. // The current mock queue, which is owned by |type_processor()|.
MockModelTypeWorker* worker_; MockModelTypeWorker* worker_;
......
...@@ -552,6 +552,10 @@ void DeviceInfoSyncBridge::OnReadAllMetadata( ...@@ -552,6 +552,10 @@ void DeviceInfoSyncBridge::OnReadAllMetadata(
return; return;
} }
// If OnSyncStarting() was already called then cache GUID must be the same.
// Otherwise IsTrackingMetadata would return false due to cache GUID mismatch.
DCHECK(local_cache_guid_.empty() ||
local_cache_guid_ == local_cache_guid_in_metadata);
// If sync already enabled (usual case without data corruption), we can // If sync already enabled (usual case without data corruption), we can
// initialize the provider immediately. // initialize the provider immediately.
local_cache_guid_ = local_cache_guid_in_metadata; local_cache_guid_ = local_cache_guid_in_metadata;
......
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