1 // Copyright (c) 2009 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 // StatusController handles all counter and status related number crunching and 6 // state tracking on behalf of a SyncSession. It 'controls' the model data 7 // defined in session_state.h. The most important feature of StatusController 8 // is the ScopedModelSafetyRestriction. When one of these is active, the 9 // underlying data set exposed via accessors is swapped out to the appropriate 10 // set for the restricted ModelSafeGroup behind the scenes. For example, if 11 // GROUP_UI is set, then accessors such as conflict_progress() and commit_ids() 12 // are implicitly restricted to returning only data pertaining to GROUP_UI. 13 // You can see which parts of status fall into this "restricted" category, or 14 // the global "shared" category for all model types, by looking at the struct 15 // declarations in session_state.h. If these accessors are invoked without a 16 // restriction in place, this is a violation and will cause debug assertions 17 // to surface improper use of the API in development. Likewise for 18 // invocation of "shared" accessors when a restriction is in place; for 19 // safety's sake, an assertion will fire. 20 // 21 // NOTE: There is no concurrent access protection provided by this class. It 22 // assumes one single thread is accessing this class for each unique 23 // ModelSafeGroup, and also only one single thread (in practice, the 24 // SyncerThread) responsible for all "shared" access when no restriction is in 25 // place. Thus, every bit of data is to be accessed mutually exclusively with 26 // respect to threads. 27 // 28 // StatusController can also track if changes occur to certain parts of state 29 // so that various parts of the sync engine can avoid broadcasting 30 // notifications if no changes occurred. 31 32 #ifndef CHROME_BROWSER_SYNC_SESSIONS_STATUS_CONTROLLER_H_ 33 #define CHROME_BROWSER_SYNC_SESSIONS_STATUS_CONTROLLER_H_ 34 #pragma once 35 36 #include <map> 37 38 #include "base/stl_util-inl.h" 39 #include "chrome/browser/sync/sessions/ordered_commit_set.h" 40 #include "chrome/browser/sync/sessions/session_state.h" 41 42 namespace browser_sync { 43 namespace sessions { 44 45 class StatusController { 46 public: 47 explicit StatusController(const ModelSafeRoutingInfo& routes); 48 ~StatusController(); 49 50 // Returns true if some portion of the session state has changed (is dirty) 51 // since it was created or was last reset. 52 bool TestAndClearIsDirty(); 53 54 // Progress counters. conflict_progress()55 const ConflictProgress& conflict_progress() { 56 return GetOrCreateModelSafeGroupState(true, 57 group_restriction_)->conflict_progress; 58 } mutable_conflict_progress()59 ConflictProgress* mutable_conflict_progress() { 60 return &GetOrCreateModelSafeGroupState(true, 61 group_restriction_)->conflict_progress; 62 } update_progress()63 const UpdateProgress& update_progress() { 64 return GetOrCreateModelSafeGroupState(true, 65 group_restriction_)->update_progress; 66 } mutable_update_progress()67 UpdateProgress* mutable_update_progress() { 68 return &GetOrCreateModelSafeGroupState(true, 69 group_restriction_)->update_progress; 70 } 71 // Some unrestricted, non-ModelChangingSyncerCommand commands need to store 72 // meta information about updates. GetUnrestrictedUpdateProgress(ModelSafeGroup group)73 UpdateProgress* GetUnrestrictedUpdateProgress(ModelSafeGroup group) { 74 return &GetOrCreateModelSafeGroupState(false, group)->update_progress; 75 } 76 77 // ClientToServer messages. commit_message()78 const ClientToServerMessage& commit_message() { 79 return shared_.commit_message; 80 } mutable_commit_message()81 ClientToServerMessage* mutable_commit_message() { 82 return &shared_.commit_message; 83 } commit_response()84 const ClientToServerResponse& commit_response() const { 85 return shared_.commit_response; 86 } mutable_commit_response()87 ClientToServerResponse* mutable_commit_response() { 88 return &shared_.commit_response; 89 } updates_request_types()90 const syncable::ModelTypeBitSet& updates_request_types() const { 91 return shared_.updates_request_types; 92 } set_updates_request_types(const syncable::ModelTypeBitSet & value)93 void set_updates_request_types(const syncable::ModelTypeBitSet& value) { 94 shared_.updates_request_types = value; 95 } updates_response()96 const ClientToServerResponse& updates_response() const { 97 return shared_.updates_response; 98 } mutable_updates_response()99 ClientToServerResponse* mutable_updates_response() { 100 return &shared_.updates_response; 101 } 102 103 // Errors and SyncerStatus. error_counters()104 const ErrorCounters& error_counters() const { 105 return shared_.error_counters.value(); 106 } syncer_status()107 const SyncerStatus& syncer_status() const { 108 return shared_.syncer_status.value(); 109 } 110 111 // Changelog related state. num_server_changes_remaining()112 int64 num_server_changes_remaining() const { 113 return shared_.num_server_changes_remaining.value(); 114 } 115 116 // Commit path data. commit_ids()117 const std::vector<syncable::Id>& commit_ids() const { 118 DCHECK(!group_restriction_in_effect_) << "Group restriction in effect!"; 119 return shared_.commit_set.GetAllCommitIds(); 120 } commit_id_projection()121 const OrderedCommitSet::Projection& commit_id_projection() { 122 DCHECK(group_restriction_in_effect_) 123 << "No group restriction for projection."; 124 return shared_.commit_set.GetCommitIdProjection(group_restriction_); 125 } GetCommitIdAt(size_t index)126 const syncable::Id& GetCommitIdAt(size_t index) { 127 DCHECK(CurrentCommitIdProjectionHasIndex(index)); 128 return shared_.commit_set.GetCommitIdAt(index); 129 } GetCommitIdModelTypeAt(size_t index)130 syncable::ModelType GetCommitIdModelTypeAt(size_t index) { 131 DCHECK(CurrentCommitIdProjectionHasIndex(index)); 132 return shared_.commit_set.GetModelTypeAt(index); 133 } unsynced_handles()134 const std::vector<int64>& unsynced_handles() const { 135 DCHECK(!group_restriction_in_effect_) 136 << "unsynced_handles is unrestricted."; 137 return shared_.unsynced_handles.value(); 138 } 139 140 // Control parameters for sync cycles. conflict_sets_built()141 bool conflict_sets_built() const { 142 return shared_.control_params.conflict_sets_built; 143 } conflicts_resolved()144 bool conflicts_resolved() const { 145 return shared_.control_params.conflicts_resolved; 146 } did_commit_items()147 bool did_commit_items() const { 148 return shared_.control_params.items_committed; 149 } 150 151 // If a GetUpdates for any data type resulted in downloading an update that 152 // is in conflict, this method returns true. 153 bool HasConflictingUpdates() const; 154 155 // Aggregate sum of ConflictingItemSize() over all ConflictProgress objects 156 // (one for each ModelSafeGroup currently in-use). 157 int TotalNumConflictingItems() const; 158 159 // Returns the number of updates received from the sync server. 160 int64 CountUpdates() const; 161 162 // Returns true iff any of the commit ids added during this session are 163 // bookmark related, and the bookmark group restriction is in effect. HasBookmarkCommitActivity()164 bool HasBookmarkCommitActivity() const { 165 return ActiveGroupRestrictionIncludesModel(syncable::BOOKMARKS) && 166 shared_.commit_set.HasBookmarkCommitId(); 167 } 168 169 // Returns true if the last download_updates_command received a valid 170 // server response. download_updates_succeeded()171 bool download_updates_succeeded() const { 172 return updates_response().has_get_updates(); 173 } 174 175 // Returns true if the last updates response indicated that we were fully 176 // up to date. This is subtle: if it's false, it could either mean that 177 // the server said there WAS more to download, or it could mean that we 178 // were unable to reach the server. If we didn't request every enabled 179 // datatype, then we can't say for sure that there's nothing left to 180 // download: in that case, this also returns false. 181 bool ServerSaysNothingMoreToDownload() const; 182 group_restriction()183 ModelSafeGroup group_restriction() const { 184 return group_restriction_; 185 } 186 187 // Check whether a particular model is included by the active group 188 // restriction. ActiveGroupRestrictionIncludesModel(syncable::ModelType model)189 bool ActiveGroupRestrictionIncludesModel(syncable::ModelType model) const { 190 if (!group_restriction_in_effect_) 191 return true; 192 ModelSafeRoutingInfo::const_iterator it = routing_info_.find(model); 193 if (it == routing_info_.end()) 194 return false; 195 return group_restriction() == it->second; 196 } 197 198 // A toolbelt full of methods for updating counters and flags. 199 void increment_num_conflicting_commits_by(int value); 200 void reset_num_conflicting_commits(); 201 void set_num_consecutive_transient_error_commits(int value); 202 void increment_num_consecutive_transient_error_commits_by(int value); 203 void set_num_consecutive_errors(int value); 204 void increment_num_consecutive_errors(); 205 void increment_num_consecutive_errors_by(int value); 206 void set_num_server_changes_remaining(int64 changes_remaining); 207 void set_invalid_store(bool invalid_store); 208 void set_syncer_stuck(bool syncer_stuck); 209 void set_syncing(bool syncing); 210 void set_num_successful_bookmark_commits(int value); 211 void increment_num_successful_commits(); 212 void increment_num_successful_bookmark_commits(); 213 void increment_num_updates_downloaded_by(int value); 214 void increment_num_tombstone_updates_downloaded_by(int value); 215 void set_types_needing_local_migration(const syncable::ModelTypeSet& types); 216 void set_unsynced_handles(const std::vector<int64>& unsynced_handles); 217 218 void set_commit_set(const OrderedCommitSet& commit_set); 219 void update_conflict_sets_built(bool built); 220 void update_conflicts_resolved(bool resolved); 221 void reset_conflicts_resolved(); 222 void set_items_committed(); 223 224 private: 225 friend class ScopedModelSafeGroupRestriction; 226 227 // Returns true iff the commit id projection for |group_restriction_| 228 // references position |index| into the full set of commit ids in play. 229 bool CurrentCommitIdProjectionHasIndex(size_t index); 230 231 // Helper to lazily create objects for per-ModelSafeGroup state. 232 PerModelSafeGroupState* GetOrCreateModelSafeGroupState(bool restrict, 233 ModelSafeGroup group); 234 235 AllModelTypeState shared_; 236 std::map<ModelSafeGroup, PerModelSafeGroupState*> per_model_group_; 237 238 STLValueDeleter<std::map<ModelSafeGroup, PerModelSafeGroupState*> > 239 per_model_group_deleter_; 240 241 // Set to true if any DirtyOnWrite pieces of state we maintain are changed. 242 // Reset to false by TestAndClearIsDirty. 243 bool is_dirty_; 244 245 // Used to fail read/write operations on state that don't obey the current 246 // active ModelSafeWorker contract. 247 bool group_restriction_in_effect_; 248 ModelSafeGroup group_restriction_; 249 250 const ModelSafeRoutingInfo routing_info_; 251 252 DISALLOW_COPY_AND_ASSIGN(StatusController); 253 }; 254 255 // A utility to restrict access to only those parts of the given 256 // StatusController that pertain to the specified ModelSafeGroup. 257 class ScopedModelSafeGroupRestriction { 258 public: ScopedModelSafeGroupRestriction(StatusController * to_restrict,ModelSafeGroup restriction)259 ScopedModelSafeGroupRestriction(StatusController* to_restrict, 260 ModelSafeGroup restriction) 261 : status_(to_restrict) { 262 DCHECK(!status_->group_restriction_in_effect_); 263 status_->group_restriction_ = restriction; 264 status_->group_restriction_in_effect_ = true; 265 } ~ScopedModelSafeGroupRestriction()266 ~ScopedModelSafeGroupRestriction() { 267 DCHECK(status_->group_restriction_in_effect_); 268 status_->group_restriction_in_effect_ = false; 269 } 270 private: 271 StatusController* status_; 272 DISALLOW_COPY_AND_ASSIGN(ScopedModelSafeGroupRestriction); 273 }; 274 275 } 276 } 277 278 #endif // CHROME_BROWSER_SYNC_SESSIONS_STATUS_CONTROLLER_H_ 279