// 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 #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& 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& 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& 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 per_model_group_; STLValueDeleter > 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_