| // Copyright (c) 2009 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. |
| |
| // StatusController handles all counter and status related number crunching and |
| // state tracking on behalf of a SyncSession. It 'controls' the model data |
| // defined in session_state.h. The most important feature of StatusController |
| // is the ScopedModelSafetyRestriction. When one of these is active, the |
| // underlying data set exposed via accessors is swapped out to the appropriate |
| // set for the restricted ModelSafeGroup behind the scenes. For example, if |
| // GROUP_UI is set, then accessors such as conflict_progress() and commit_ids() |
| // are implicitly restricted to returning only data pertaining to GROUP_UI. |
| // You can see which parts of status fall into this "restricted" category, or |
| // the global "shared" category for all model types, by looking at the struct |
| // declarations in session_state.h. If these accessors are invoked without a |
| // restriction in place, this is a violation and will cause debug assertions |
| // to surface improper use of the API in development. Likewise for |
| // invocation of "shared" accessors when a restriction is in place; for |
| // safety's sake, an assertion will fire. |
| // |
| // NOTE: There is no concurrent access protection provided by this class. It |
| // assumes one single thread is accessing this class for each unique |
| // ModelSafeGroup, and also only one single thread (in practice, the |
| // SyncerThread) responsible for all "shared" access when no restriction is in |
| // place. Thus, every bit of data is to be accessed mutually exclusively with |
| // respect to threads. |
| // |
| // StatusController can also track if changes occur to certain parts of state |
| // so that various parts of the sync engine can avoid broadcasting |
| // notifications if no changes occurred. |
| |
| #ifndef CHROME_BROWSER_SYNC_SESSIONS_STATUS_CONTROLLER_H_ |
| #define CHROME_BROWSER_SYNC_SESSIONS_STATUS_CONTROLLER_H_ |
| #pragma once |
| |
| #include <map> |
| |
| #include "base/stl_util-inl.h" |
| #include "chrome/browser/sync/sessions/ordered_commit_set.h" |
| #include "chrome/browser/sync/sessions/session_state.h" |
| |
| namespace browser_sync { |
| namespace sessions { |
| |
| class StatusController { |
| public: |
| explicit StatusController(const ModelSafeRoutingInfo& routes); |
| ~StatusController(); |
| |
| // Returns true if some portion of the session state has changed (is dirty) |
| // since it was created or was last reset. |
| bool TestAndClearIsDirty(); |
| |
| // Progress counters. |
| const ConflictProgress& conflict_progress() { |
| return GetOrCreateModelSafeGroupState(true, |
| group_restriction_)->conflict_progress; |
| } |
| ConflictProgress* mutable_conflict_progress() { |
| return &GetOrCreateModelSafeGroupState(true, |
| group_restriction_)->conflict_progress; |
| } |
| const UpdateProgress& update_progress() { |
| return GetOrCreateModelSafeGroupState(true, |
| group_restriction_)->update_progress; |
| } |
| UpdateProgress* mutable_update_progress() { |
| return &GetOrCreateModelSafeGroupState(true, |
| group_restriction_)->update_progress; |
| } |
| // Some unrestricted, non-ModelChangingSyncerCommand commands need to store |
| // meta information about updates. |
| UpdateProgress* GetUnrestrictedUpdateProgress(ModelSafeGroup group) { |
| return &GetOrCreateModelSafeGroupState(false, group)->update_progress; |
| } |
| |
| // ClientToServer messages. |
| const ClientToServerMessage& commit_message() { |
| return shared_.commit_message; |
| } |
| ClientToServerMessage* mutable_commit_message() { |
| return &shared_.commit_message; |
| } |
| const ClientToServerResponse& commit_response() const { |
| return shared_.commit_response; |
| } |
| ClientToServerResponse* mutable_commit_response() { |
| return &shared_.commit_response; |
| } |
| const syncable::ModelTypeBitSet& updates_request_types() const { |
| return shared_.updates_request_types; |
| } |
| void set_updates_request_types(const syncable::ModelTypeBitSet& value) { |
| shared_.updates_request_types = value; |
| } |
| const ClientToServerResponse& updates_response() const { |
| return shared_.updates_response; |
| } |
| ClientToServerResponse* mutable_updates_response() { |
| return &shared_.updates_response; |
| } |
| |
| // Errors and SyncerStatus. |
| const ErrorCounters& error_counters() const { |
| return shared_.error_counters.value(); |
| } |
| const SyncerStatus& syncer_status() const { |
| return shared_.syncer_status.value(); |
| } |
| |
| // Changelog related state. |
| int64 num_server_changes_remaining() const { |
| return shared_.num_server_changes_remaining.value(); |
| } |
| |
| // Commit path data. |
| const std::vector<syncable::Id>& commit_ids() const { |
| DCHECK(!group_restriction_in_effect_) << "Group restriction in effect!"; |
| return shared_.commit_set.GetAllCommitIds(); |
| } |
| const OrderedCommitSet::Projection& commit_id_projection() { |
| DCHECK(group_restriction_in_effect_) |
| << "No group restriction for projection."; |
| return shared_.commit_set.GetCommitIdProjection(group_restriction_); |
| } |
| const syncable::Id& GetCommitIdAt(size_t index) { |
| DCHECK(CurrentCommitIdProjectionHasIndex(index)); |
| return shared_.commit_set.GetCommitIdAt(index); |
| } |
| syncable::ModelType GetCommitIdModelTypeAt(size_t index) { |
| DCHECK(CurrentCommitIdProjectionHasIndex(index)); |
| return shared_.commit_set.GetModelTypeAt(index); |
| } |
| const std::vector<int64>& unsynced_handles() const { |
| DCHECK(!group_restriction_in_effect_) |
| << "unsynced_handles is unrestricted."; |
| return shared_.unsynced_handles.value(); |
| } |
| |
| // Control parameters for sync cycles. |
| bool conflict_sets_built() const { |
| return shared_.control_params.conflict_sets_built; |
| } |
| bool conflicts_resolved() const { |
| return shared_.control_params.conflicts_resolved; |
| } |
| bool did_commit_items() const { |
| return shared_.control_params.items_committed; |
| } |
| |
| // If a GetUpdates for any data type resulted in downloading an update that |
| // is in conflict, this method returns true. |
| bool HasConflictingUpdates() const; |
| |
| // Aggregate sum of ConflictingItemSize() over all ConflictProgress objects |
| // (one for each ModelSafeGroup currently in-use). |
| int TotalNumConflictingItems() const; |
| |
| // Returns the number of updates received from the sync server. |
| int64 CountUpdates() const; |
| |
| // Returns true iff any of the commit ids added during this session are |
| // bookmark related, and the bookmark group restriction is in effect. |
| bool HasBookmarkCommitActivity() const { |
| return ActiveGroupRestrictionIncludesModel(syncable::BOOKMARKS) && |
| shared_.commit_set.HasBookmarkCommitId(); |
| } |
| |
| // Returns true if the last download_updates_command received a valid |
| // server response. |
| bool download_updates_succeeded() const { |
| return updates_response().has_get_updates(); |
| } |
| |
| // Returns true if the last updates response indicated that we were fully |
| // up to date. This is subtle: if it's false, it could either mean that |
| // the server said there WAS more to download, or it could mean that we |
| // were unable to reach the server. If we didn't request every enabled |
| // datatype, then we can't say for sure that there's nothing left to |
| // download: in that case, this also returns false. |
| bool ServerSaysNothingMoreToDownload() const; |
| |
| ModelSafeGroup group_restriction() const { |
| return group_restriction_; |
| } |
| |
| // Check whether a particular model is included by the active group |
| // restriction. |
| bool ActiveGroupRestrictionIncludesModel(syncable::ModelType model) const { |
| if (!group_restriction_in_effect_) |
| return true; |
| ModelSafeRoutingInfo::const_iterator it = routing_info_.find(model); |
| if (it == routing_info_.end()) |
| return false; |
| return group_restriction() == it->second; |
| } |
| |
| // A toolbelt full of methods for updating counters and flags. |
| void increment_num_conflicting_commits_by(int value); |
| void reset_num_conflicting_commits(); |
| void set_num_consecutive_transient_error_commits(int value); |
| void increment_num_consecutive_transient_error_commits_by(int value); |
| void set_num_consecutive_errors(int value); |
| void increment_num_consecutive_errors(); |
| void increment_num_consecutive_errors_by(int value); |
| void set_num_server_changes_remaining(int64 changes_remaining); |
| void set_invalid_store(bool invalid_store); |
| void set_syncer_stuck(bool syncer_stuck); |
| void set_syncing(bool syncing); |
| void set_num_successful_bookmark_commits(int value); |
| void increment_num_successful_commits(); |
| void increment_num_successful_bookmark_commits(); |
| void increment_num_updates_downloaded_by(int value); |
| void increment_num_tombstone_updates_downloaded_by(int value); |
| void set_types_needing_local_migration(const syncable::ModelTypeSet& types); |
| void set_unsynced_handles(const std::vector<int64>& unsynced_handles); |
| |
| void set_commit_set(const OrderedCommitSet& commit_set); |
| void update_conflict_sets_built(bool built); |
| void update_conflicts_resolved(bool resolved); |
| void reset_conflicts_resolved(); |
| void set_items_committed(); |
| |
| private: |
| friend class ScopedModelSafeGroupRestriction; |
| |
| // Returns true iff the commit id projection for |group_restriction_| |
| // references position |index| into the full set of commit ids in play. |
| bool CurrentCommitIdProjectionHasIndex(size_t index); |
| |
| // Helper to lazily create objects for per-ModelSafeGroup state. |
| PerModelSafeGroupState* GetOrCreateModelSafeGroupState(bool restrict, |
| ModelSafeGroup group); |
| |
| AllModelTypeState shared_; |
| std::map<ModelSafeGroup, PerModelSafeGroupState*> per_model_group_; |
| |
| STLValueDeleter<std::map<ModelSafeGroup, PerModelSafeGroupState*> > |
| per_model_group_deleter_; |
| |
| // Set to true if any DirtyOnWrite pieces of state we maintain are changed. |
| // Reset to false by TestAndClearIsDirty. |
| bool is_dirty_; |
| |
| // Used to fail read/write operations on state that don't obey the current |
| // active ModelSafeWorker contract. |
| bool group_restriction_in_effect_; |
| ModelSafeGroup group_restriction_; |
| |
| const ModelSafeRoutingInfo routing_info_; |
| |
| DISALLOW_COPY_AND_ASSIGN(StatusController); |
| }; |
| |
| // A utility to restrict access to only those parts of the given |
| // StatusController that pertain to the specified ModelSafeGroup. |
| class ScopedModelSafeGroupRestriction { |
| public: |
| ScopedModelSafeGroupRestriction(StatusController* to_restrict, |
| ModelSafeGroup restriction) |
| : status_(to_restrict) { |
| DCHECK(!status_->group_restriction_in_effect_); |
| status_->group_restriction_ = restriction; |
| status_->group_restriction_in_effect_ = true; |
| } |
| ~ScopedModelSafeGroupRestriction() { |
| DCHECK(status_->group_restriction_in_effect_); |
| status_->group_restriction_in_effect_ = false; |
| } |
| private: |
| StatusController* status_; |
| DISALLOW_COPY_AND_ASSIGN(ScopedModelSafeGroupRestriction); |
| }; |
| |
| } |
| } |
| |
| #endif // CHROME_BROWSER_SYNC_SESSIONS_STATUS_CONTROLLER_H_ |