Chromium Code Reviews| Index: sync/engine/sync_thread_sync_entity.cc |
| diff --git a/sync/engine/sync_thread_sync_entity.cc b/sync/engine/sync_thread_sync_entity.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..f3418046fc2eb532be815339b0abbb4fe69a4315 |
| --- /dev/null |
| +++ b/sync/engine/sync_thread_sync_entity.cc |
| @@ -0,0 +1,244 @@ |
| +// Copyright 2014 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "sync/engine/sync_thread_sync_entity.h" |
| + |
| +#include "base/logging.h" |
| +#include "sync/engine/non_blocking_sync_common.h" |
| +#include "sync/internal_api/public/base/model_type.h" |
| +#include "sync/syncable/syncable_util.h" |
| +#include "sync/util/time.h" |
| + |
| +namespace syncer { |
| + |
| +SyncThreadSyncEntity* SyncThreadSyncEntity::FromServerUpdate( |
| + const std::string& id_string, |
| + const std::string& client_tag_hash, |
| + int64 received_version) { |
| + return new SyncThreadSyncEntity( |
| + id_string, client_tag_hash, 0, received_version); |
| +} |
| + |
| +SyncThreadSyncEntity* SyncThreadSyncEntity::FromCommitRequest( |
| + const std::string& id_string, |
| + const std::string& client_tag_hash, |
| + int64 sequence_number, |
| + int64 base_version, |
| + base::Time ctime, |
| + base::Time mtime, |
| + const std::string& non_unique_name, |
| + bool deleted, |
| + const sync_pb::EntitySpecifics& specifics) { |
| + return new SyncThreadSyncEntity(id_string, |
| + client_tag_hash, |
| + 0, |
| + 0, |
| + true, |
| + sequence_number, |
| + base_version, |
| + ctime, |
| + mtime, |
| + non_unique_name, |
| + deleted, |
| + specifics); |
| +} |
| + |
| +// Constructor that does not set any pending commit fields. |
| +SyncThreadSyncEntity::SyncThreadSyncEntity( |
| + const std::string& id, |
| + const std::string& client_tag_hash, |
| + int64 highest_commit_response_version, |
| + int64 highest_gu_response_version) |
| + : id_(id), |
| + client_tag_hash_(client_tag_hash), |
| + highest_commit_response_version_(highest_commit_response_version), |
| + highest_gu_response_version_(highest_gu_response_version), |
| + is_commit_pending_(false), |
| + sequence_number_(0), |
| + base_version_(0), |
| + deleted_(false) { |
| +} |
| + |
| +SyncThreadSyncEntity::SyncThreadSyncEntity( |
| + const std::string& id, |
| + const std::string& client_tag_hash, |
| + int64 highest_commit_response_version, |
| + int64 highest_gu_response_version, |
| + bool is_commit_pending, |
| + int64 sequence_number, |
| + int64 base_version, |
| + base::Time ctime, |
| + base::Time mtime, |
| + const std::string& non_unique_name, |
| + bool deleted, |
| + const sync_pb::EntitySpecifics& specifics) |
| + : id_(id), |
| + client_tag_hash_(client_tag_hash), |
| + highest_commit_response_version_(highest_commit_response_version), |
| + highest_gu_response_version_(highest_gu_response_version), |
| + is_commit_pending_(is_commit_pending), |
| + sequence_number_(sequence_number), |
| + base_version_(base_version), |
| + ctime_(ctime), |
| + mtime_(mtime), |
| + non_unique_name_(non_unique_name), |
| + deleted_(deleted), |
| + specifics_(specifics) { |
| +} |
| + |
| +SyncThreadSyncEntity::~SyncThreadSyncEntity() { |
| +} |
| + |
| +bool SyncThreadSyncEntity::IsCommitPending() const { |
| + return is_commit_pending_; |
| +} |
| + |
| +void SyncThreadSyncEntity::PrepareCommitProto( |
| + sync_pb::SyncEntity* commit_entity, |
| + int64* sequence_number) const { |
| + // Set ID if we have a server-assigned ID. Otherwise, it will be up to |
| + // our caller to assign a client-unique initial ID. |
| + if (base_version_ != kUncommittedVersion) { |
| + commit_entity->set_id_string(id_); |
| + } |
| + |
| + commit_entity->set_client_defined_unique_tag(client_tag_hash_); |
| + commit_entity->set_version(base_version_); |
| + commit_entity->set_deleted(deleted_); |
| + commit_entity->set_folder(false); |
| + commit_entity->set_name(non_unique_name_); |
| + if (!deleted_) { |
| + commit_entity->set_ctime(TimeToProtoTime(ctime_)); |
| + commit_entity->set_mtime(TimeToProtoTime(mtime_)); |
| + commit_entity->mutable_specifics()->CopyFrom(specifics_); |
| + } |
| + |
| + *sequence_number = sequence_number_; |
| +} |
| + |
| +void SyncThreadSyncEntity::RequestCommit( |
| + const std::string& id, |
| + const std::string& client_tag_hash, |
| + int64 sequence_number, |
| + int64 base_version, |
| + base::Time ctime, |
| + base::Time mtime, |
| + const std::string& non_unique_name, |
| + bool deleted, |
| + const sync_pb::EntitySpecifics& specifics) { |
| + if (base_version < base_version_) { |
| + NOTREACHED() << "Base version should never decrease"; |
| + return; |
| + } |
| + |
| + if (sequence_number < sequence_number_) { |
| + NOTREACHED() << "Sequence number should never decrease"; |
| + return; |
| + } |
| + |
| + // Update our book-keeping counters. |
| + base_version_ = base_version; |
| + sequence_number_ = sequence_number; |
| + |
| + // Do our counter values indicate a conflict? If so, don't commit. |
| + // |
| + // There's no need to inform the model thread of the conflict. The |
| + // conflicting update has already been posted to its task runner; it will |
| + // figure it out as soon as it runs that task. |
| + is_commit_pending_ = true; |
| + if (IsInConflict()) { |
| + ClearPendingCommit(); |
| + return; |
| + } |
| + |
| + // We don't commit deletions of server-unknown items. |
| + if (deleted && !IsServerKnown()) { |
| + ClearPendingCommit(); |
| + return; |
| + } |
| + |
| + // Otherwise, we should store the data associated with this pending commit |
| + // so we're ready to commit at the next possible opportunity. |
| + |
| + // We intentionally don't update the id_ here. Good ID values come from the |
| + // server and always pass through the sync thread first. There's no way the |
| + // model thread could have a better ID value than we do. |
| + |
| + // This entity is identified by its client tag. That value can never change. |
| + DCHECK_EQ(client_tag_hash_, client_tag_hash); |
| + |
| + // Set the fields for the pending commit. |
| + ctime_ = ctime; |
| + mtime_ = mtime; |
| + non_unique_name_ = non_unique_name; |
| + deleted_ = deleted; |
| + specifics_ = specifics; |
| +} |
| + |
| +void SyncThreadSyncEntity::ReceiveCommitResponse(const std::string& response_id, |
| + int64 response_version, |
| + int64 sequence_number) { |
| + // Commit responses, especially after the first commit, can update our ID. |
| + id_ = response_id; |
| + |
| + DCHECK_GT(response_version, highest_commit_response_version_) |
| + << "Had expected higher response version." |
| + << " id: " << id_; |
| + |
| + // Commits are synchronous, so there should there's no reason why the |
|
Nicolas Zea
2014/05/28 23:56:16
nit: there should there's
rlarocque
2014/05/29 20:54:52
Done.
|
| + // sequence numbers wouldn't match. |
| + DCHECK_EQ(sequence_number_, sequence_number) |
| + << "Unexpected sequence number mismatch." |
| + << " id: " << id_; |
| + |
| + highest_commit_response_version_ = response_version; |
| + |
| + // Because an in-progress commit blocks the sync thread, we can assume that |
| + // the item we just committed successfully is exactly the one we have now. |
| + // Nothing changed it while the commit was happening. Since we're now in |
| + // sync with the server, we can clear the pending commit. |
| + ClearPendingCommit(); |
| +} |
| + |
| +void SyncThreadSyncEntity::ReceiveUpdate(int64 version) { |
| + highest_gu_response_version_ = |
| + std::max(highest_gu_response_version_, version); |
| + |
| + if (IsInConflict()) { |
| + // Incoming update clobbers the pending commit on the sync thread. |
| + // The model thread can re-request this commit later if it wants to. |
| + ClearPendingCommit(); |
| + } |
| +} |
| + |
| +bool SyncThreadSyncEntity::IsInConflict() const { |
| + if (!is_commit_pending_) |
| + return false; |
| + |
| + if (highest_gu_response_version_ <= highest_commit_response_version_) { |
| + // The most recent server state was created in a commit made by this |
| + // client. We're fully up to date, and therefore not in conflict. |
| + return false; |
| + } else { |
| + // The most recent server state was written by someone else. |
| + // Did the model thread have the most up to date version when it issued the |
| + // commit request? |
| + if (base_version_ >= highest_gu_response_version_) { |
| + return false; // Yes. |
| + } else { |
| + return true; // No. |
| + } |
| + } |
| +} |
| + |
| +bool SyncThreadSyncEntity::IsServerKnown() const { |
| + return base_version_ != kUncommittedVersion; |
| +} |
| + |
| +void SyncThreadSyncEntity::ClearPendingCommit() { |
| + is_commit_pending_ = false; |
| + specifics_.Clear(); |
|
Nicolas Zea
2014/05/28 23:56:16
why clear the specific here?
rlarocque
2014/05/29 20:54:52
It's an optimization. At this point we've given u
Nicolas Zea
2014/06/02 20:27:17
Does the protobuf library actually free memory whe
rlarocque
2014/06/02 21:39:13
According to the documentation [1], protobufs can
|
| +} |
| + |
| +} // namespace syncer |