summaryrefslogtreecommitdiffstats
path: root/chrome/browser/sync/sessions/status_controller.h
blob: f367b50d530027a03e14441385e1268e8a86cfa1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
// 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, the
// global "shared" category for all model types, or the single per-model type
// category 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);

  // 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::MultiTypeTimeStamp& updates_request_parameters() const {
    return shared_.updates_request_parameters;
  }
  void set_updates_request_parameters(
      const syncable::MultiTypeTimeStamp& value) {
    shared_.updates_request_parameters = 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();
  }
  // Aggregate max over all data type timestamps, used for UI reporting.
  int64 ComputeMaxLocalTimestamp() const;

  // 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_current_download_timestamp(syncable::ModelType model,
                                      int64 current_timestamp);
  void set_num_server_changes_remaining(int64 changes_remaining);
  void set_over_quota(bool over_quota);
  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 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);
  // Helper to lazily create objects for per-model type state.
  PerModelTypeState* GetOrCreateModelTypeState(bool restrict,
                                               syncable::ModelType model);

  AllModelTypeState shared_;
  std::map<ModelSafeGroup, PerModelSafeGroupState*> per_model_group_;
  std::map<syncable::ModelType, PerModelTypeState*> per_model_type_;

  STLValueDeleter<std::map<ModelSafeGroup, PerModelSafeGroupState*> >
      per_model_group_deleter_;
  STLValueDeleter<std::map<syncable::ModelType, PerModelTypeState*> >
      per_model_type_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_