| OLD | NEW |
| (Empty) |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "sync/engine/sync_thread_sync_entity.h" | |
| 6 | |
| 7 #include "base/logging.h" | |
| 8 #include "sync/engine/non_blocking_sync_common.h" | |
| 9 #include "sync/internal_api/public/base/model_type.h" | |
| 10 #include "sync/syncable/syncable_util.h" | |
| 11 #include "sync/util/time.h" | |
| 12 | |
| 13 namespace syncer { | |
| 14 | |
| 15 SyncThreadSyncEntity* SyncThreadSyncEntity::FromServerUpdate( | |
| 16 const std::string& id_string, | |
| 17 const std::string& client_tag_hash, | |
| 18 int64 received_version) { | |
| 19 return new SyncThreadSyncEntity( | |
| 20 id_string, client_tag_hash, 0, received_version); | |
| 21 } | |
| 22 | |
| 23 SyncThreadSyncEntity* SyncThreadSyncEntity::FromCommitRequest( | |
| 24 const std::string& id_string, | |
| 25 const std::string& client_tag_hash, | |
| 26 int64 sequence_number, | |
| 27 int64 base_version, | |
| 28 base::Time ctime, | |
| 29 base::Time mtime, | |
| 30 const std::string& non_unique_name, | |
| 31 bool deleted, | |
| 32 const sync_pb::EntitySpecifics& specifics) { | |
| 33 return new SyncThreadSyncEntity(id_string, | |
| 34 client_tag_hash, | |
| 35 0, | |
| 36 0, | |
| 37 true, | |
| 38 sequence_number, | |
| 39 base_version, | |
| 40 ctime, | |
| 41 mtime, | |
| 42 non_unique_name, | |
| 43 deleted, | |
| 44 specifics); | |
| 45 } | |
| 46 | |
| 47 // Constructor that does not set any pending commit fields. | |
| 48 SyncThreadSyncEntity::SyncThreadSyncEntity( | |
| 49 const std::string& id, | |
| 50 const std::string& client_tag_hash, | |
| 51 int64 highest_commit_response_version, | |
| 52 int64 highest_gu_response_version) | |
| 53 : id_(id), | |
| 54 client_tag_hash_(client_tag_hash), | |
| 55 highest_commit_response_version_(highest_commit_response_version), | |
| 56 highest_gu_response_version_(highest_gu_response_version), | |
| 57 is_commit_pending_(false), | |
| 58 sequence_number_(0), | |
| 59 base_version_(0), | |
| 60 deleted_(false) { | |
| 61 } | |
| 62 | |
| 63 SyncThreadSyncEntity::SyncThreadSyncEntity( | |
| 64 const std::string& id, | |
| 65 const std::string& client_tag_hash, | |
| 66 int64 highest_commit_response_version, | |
| 67 int64 highest_gu_response_version, | |
| 68 bool is_commit_pending, | |
| 69 int64 sequence_number, | |
| 70 int64 base_version, | |
| 71 base::Time ctime, | |
| 72 base::Time mtime, | |
| 73 const std::string& non_unique_name, | |
| 74 bool deleted, | |
| 75 const sync_pb::EntitySpecifics& specifics) | |
| 76 : id_(id), | |
| 77 client_tag_hash_(client_tag_hash), | |
| 78 highest_commit_response_version_(highest_commit_response_version), | |
| 79 highest_gu_response_version_(highest_gu_response_version), | |
| 80 is_commit_pending_(is_commit_pending), | |
| 81 sequence_number_(sequence_number), | |
| 82 base_version_(base_version), | |
| 83 ctime_(ctime), | |
| 84 mtime_(mtime), | |
| 85 non_unique_name_(non_unique_name), | |
| 86 deleted_(deleted), | |
| 87 specifics_(specifics) { | |
| 88 } | |
| 89 | |
| 90 SyncThreadSyncEntity::~SyncThreadSyncEntity() { | |
| 91 } | |
| 92 | |
| 93 bool SyncThreadSyncEntity::IsCommitPending() const { | |
| 94 return is_commit_pending_; | |
| 95 } | |
| 96 | |
| 97 void SyncThreadSyncEntity::PrepareCommitProto( | |
| 98 sync_pb::SyncEntity* commit_entity, | |
| 99 int64* sequence_number) const { | |
| 100 // Set ID if we have a server-assigned ID. Otherwise, it will be up to | |
| 101 // our caller to assign a client-unique initial ID. | |
| 102 if (base_version_ != kUncommittedVersion) { | |
| 103 commit_entity->set_id_string(id_); | |
| 104 } | |
| 105 | |
| 106 commit_entity->set_client_defined_unique_tag(client_tag_hash_); | |
| 107 commit_entity->set_version(base_version_); | |
| 108 commit_entity->set_deleted(deleted_); | |
| 109 commit_entity->set_folder(false); | |
| 110 commit_entity->set_name(non_unique_name_); | |
| 111 if (!deleted_) { | |
| 112 commit_entity->set_ctime(TimeToProtoTime(ctime_)); | |
| 113 commit_entity->set_mtime(TimeToProtoTime(mtime_)); | |
| 114 commit_entity->mutable_specifics()->CopyFrom(specifics_); | |
| 115 } | |
| 116 | |
| 117 *sequence_number = sequence_number_; | |
| 118 } | |
| 119 | |
| 120 void SyncThreadSyncEntity::RequestCommit( | |
| 121 const std::string& id, | |
| 122 const std::string& client_tag_hash, | |
| 123 int64 sequence_number, | |
| 124 int64 base_version, | |
| 125 base::Time ctime, | |
| 126 base::Time mtime, | |
| 127 const std::string& non_unique_name, | |
| 128 bool deleted, | |
| 129 const sync_pb::EntitySpecifics& specifics) { | |
| 130 DCHECK_GE(base_version, base_version_) | |
| 131 << "Base version should never decrease"; | |
| 132 | |
| 133 DCHECK_GE(sequence_number, sequence_number_) | |
| 134 << "Sequence number should never decrease"; | |
| 135 | |
| 136 // Update our book-keeping counters. | |
| 137 base_version_ = base_version; | |
| 138 sequence_number_ = sequence_number; | |
| 139 | |
| 140 // Do our counter values indicate a conflict? If so, don't commit. | |
| 141 // | |
| 142 // There's no need to inform the model thread of the conflict. The | |
| 143 // conflicting update has already been posted to its task runner; it will | |
| 144 // figure it out as soon as it runs that task. | |
| 145 is_commit_pending_ = true; | |
| 146 if (IsInConflict()) { | |
| 147 ClearPendingCommit(); | |
| 148 return; | |
| 149 } | |
| 150 | |
| 151 // We don't commit deletions of server-unknown items. | |
| 152 if (deleted && !IsServerKnown()) { | |
| 153 ClearPendingCommit(); | |
| 154 return; | |
| 155 } | |
| 156 | |
| 157 // Otherwise, we should store the data associated with this pending commit | |
| 158 // so we're ready to commit at the next possible opportunity. | |
| 159 | |
| 160 // We intentionally don't update the id_ here. Good ID values come from the | |
| 161 // server and always pass through the sync thread first. There's no way the | |
| 162 // model thread could have a better ID value than we do. | |
| 163 | |
| 164 // This entity is identified by its client tag. That value can never change. | |
| 165 DCHECK_EQ(client_tag_hash_, client_tag_hash); | |
| 166 | |
| 167 // Set the fields for the pending commit. | |
| 168 ctime_ = ctime; | |
| 169 mtime_ = mtime; | |
| 170 non_unique_name_ = non_unique_name; | |
| 171 deleted_ = deleted; | |
| 172 specifics_ = specifics; | |
| 173 } | |
| 174 | |
| 175 void SyncThreadSyncEntity::ReceiveCommitResponse(const std::string& response_id, | |
| 176 int64 response_version, | |
| 177 int64 sequence_number) { | |
| 178 // Commit responses, especially after the first commit, can update our ID. | |
| 179 id_ = response_id; | |
| 180 | |
| 181 DCHECK_GT(response_version, highest_commit_response_version_) | |
| 182 << "Had expected higher response version." | |
| 183 << " id: " << id_; | |
| 184 | |
| 185 // Commits are synchronous, so there's no reason why the sequence numbers | |
| 186 // wouldn't match. | |
| 187 DCHECK_EQ(sequence_number_, sequence_number) | |
| 188 << "Unexpected sequence number mismatch." | |
| 189 << " id: " << id_; | |
| 190 | |
| 191 highest_commit_response_version_ = response_version; | |
| 192 | |
| 193 // Because an in-progress commit blocks the sync thread, we can assume that | |
| 194 // the item we just committed successfully is exactly the one we have now. | |
| 195 // Nothing changed it while the commit was happening. Since we're now in | |
| 196 // sync with the server, we can clear the pending commit. | |
| 197 ClearPendingCommit(); | |
| 198 } | |
| 199 | |
| 200 void SyncThreadSyncEntity::ReceiveUpdate(int64 version) { | |
| 201 highest_gu_response_version_ = | |
| 202 std::max(highest_gu_response_version_, version); | |
| 203 | |
| 204 if (IsInConflict()) { | |
| 205 // Incoming update clobbers the pending commit on the sync thread. | |
| 206 // The model thread can re-request this commit later if it wants to. | |
| 207 ClearPendingCommit(); | |
| 208 } | |
| 209 } | |
| 210 | |
| 211 bool SyncThreadSyncEntity::IsInConflict() const { | |
| 212 if (!is_commit_pending_) | |
| 213 return false; | |
| 214 | |
| 215 if (highest_gu_response_version_ <= highest_commit_response_version_) { | |
| 216 // The most recent server state was created in a commit made by this | |
| 217 // client. We're fully up to date, and therefore not in conflict. | |
| 218 return false; | |
| 219 } else { | |
| 220 // The most recent server state was written by someone else. | |
| 221 // Did the model thread have the most up to date version when it issued the | |
| 222 // commit request? | |
| 223 if (base_version_ >= highest_gu_response_version_) { | |
| 224 return false; // Yes. | |
| 225 } else { | |
| 226 return true; // No. | |
| 227 } | |
| 228 } | |
| 229 } | |
| 230 | |
| 231 bool SyncThreadSyncEntity::IsServerKnown() const { | |
| 232 return base_version_ != kUncommittedVersion; | |
| 233 } | |
| 234 | |
| 235 void SyncThreadSyncEntity::ClearPendingCommit() { | |
| 236 is_commit_pending_ = false; | |
| 237 | |
| 238 // Clearing the specifics might free up some memory. It can't hurt to try. | |
| 239 specifics_.Clear(); | |
| 240 } | |
| 241 | |
| 242 } // namespace syncer | |
| OLD | NEW |