Home | History | Annotate | Download | only in sessions
      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.
     55   const ConflictProgress& conflict_progress() {
     56     return GetOrCreateModelSafeGroupState(true,
     57         group_restriction_)->conflict_progress;
     58   }
     59   ConflictProgress* mutable_conflict_progress() {
     60     return &GetOrCreateModelSafeGroupState(true,
     61         group_restriction_)->conflict_progress;
     62   }
     63   const UpdateProgress& update_progress() {
     64     return GetOrCreateModelSafeGroupState(true,
     65         group_restriction_)->update_progress;
     66   }
     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.
     73   UpdateProgress* GetUnrestrictedUpdateProgress(ModelSafeGroup group) {
     74     return &GetOrCreateModelSafeGroupState(false, group)->update_progress;
     75   }
     76 
     77   // ClientToServer messages.
     78   const ClientToServerMessage& commit_message() {
     79     return shared_.commit_message;
     80   }
     81   ClientToServerMessage* mutable_commit_message() {
     82     return &shared_.commit_message;
     83   }
     84   const ClientToServerResponse& commit_response() const {
     85     return shared_.commit_response;
     86   }
     87   ClientToServerResponse* mutable_commit_response() {
     88     return &shared_.commit_response;
     89   }
     90   const syncable::ModelTypeBitSet& updates_request_types() const {
     91     return shared_.updates_request_types;
     92   }
     93   void set_updates_request_types(const syncable::ModelTypeBitSet& value) {
     94     shared_.updates_request_types = value;
     95   }
     96   const ClientToServerResponse& updates_response() const {
     97     return shared_.updates_response;
     98   }
     99   ClientToServerResponse* mutable_updates_response() {
    100     return &shared_.updates_response;
    101   }
    102 
    103   // Errors and SyncerStatus.
    104   const ErrorCounters& error_counters() const {
    105     return shared_.error_counters.value();
    106   }
    107   const SyncerStatus& syncer_status() const {
    108     return shared_.syncer_status.value();
    109   }
    110 
    111   // Changelog related state.
    112   int64 num_server_changes_remaining() const {
    113     return shared_.num_server_changes_remaining.value();
    114   }
    115 
    116   // Commit path data.
    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   }
    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   }
    126   const syncable::Id& GetCommitIdAt(size_t index) {
    127     DCHECK(CurrentCommitIdProjectionHasIndex(index));
    128     return shared_.commit_set.GetCommitIdAt(index);
    129   }
    130   syncable::ModelType GetCommitIdModelTypeAt(size_t index) {
    131     DCHECK(CurrentCommitIdProjectionHasIndex(index));
    132     return shared_.commit_set.GetModelTypeAt(index);
    133   }
    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.
    141   bool conflict_sets_built() const {
    142     return shared_.control_params.conflict_sets_built;
    143   }
    144   bool conflicts_resolved() const {
    145     return shared_.control_params.conflicts_resolved;
    146   }
    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.
    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.
    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 
    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.
    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:
    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   }
    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