summaryrefslogtreecommitdiffstats
path: root/sync/sessions/status_controller.h
blob: 0780a939f7cac6dfdef08df6a4c769d08d8fc518 (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
// Copyright (c) 2012 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 SYNC_SESSIONS_STATUS_CONTROLLER_H_
#define SYNC_SESSIONS_STATUS_CONTROLLER_H_
#pragma once

#include <map>
#include <vector>

#include "base/logging.h"
#include "base/stl_util.h"
#include "base/time.h"
#include "sync/internal_api/public/sessions/model_neutral_state.h"
#include "sync/sessions/ordered_commit_set.h"
#include "sync/sessions/session_state.h"

namespace syncer {
namespace sessions {

class StatusController {
 public:
  explicit StatusController(const ModelSafeRoutingInfo& routes);
  ~StatusController();

  // Progress counters.  All const methods may return NULL if the
  // progress structure doesn't exist, but all non-const methods
  // auto-create.
  const ConflictProgress* conflict_progress() const;
  ConflictProgress* mutable_conflict_progress();
  const UpdateProgress* update_progress() const;
  UpdateProgress* mutable_update_progress();
  const ConflictProgress* GetUnrestrictedConflictProgress(
      ModelSafeGroup group) const;
  ConflictProgress* GetUnrestrictedMutableConflictProgressForTest(
      ModelSafeGroup group);
  const UpdateProgress* GetUnrestrictedUpdateProgress(
      ModelSafeGroup group) const;
  UpdateProgress* GetUnrestrictedMutableUpdateProgressForTest(
      ModelSafeGroup group);

  // ClientToServer messages.
  const syncable::ModelTypeSet updates_request_types() const {
    return model_neutral_.updates_request_types;
  }
  void set_updates_request_types(syncable::ModelTypeSet value) {
    model_neutral_.updates_request_types = value;
  }
  const ClientToServerResponse& updates_response() const {
    return model_neutral_.updates_response;
  }
  ClientToServerResponse* mutable_updates_response() {
    return &model_neutral_.updates_response;
  }

  // Changelog related state.
  int64 num_server_changes_remaining() const {
    return model_neutral_.num_server_changes_remaining;
  }

  const OrderedCommitSet::Projection& commit_id_projection(
      const sessions::OrderedCommitSet &commit_set) {
    DCHECK(group_restriction_in_effect_)
        << "No group restriction for projection.";
    return commit_set.GetCommitIdProjection(group_restriction_);
  }

  // Control parameters for sync cycles.
  bool conflicts_resolved() const {
    return model_neutral_.conflicts_resolved;
  }

  // If a GetUpdates for any data type resulted in downloading an update that
  // is in conflict, this method returns true.
  // Note: this includes unresolvable conflicts.
  bool HasConflictingUpdates() const;

  // Aggregate sums of various types of conflict counters accross all
  // ConflictProgress objects (one for each ModelSafeGroup currently in-use).
  int TotalNumEncryptionConflictingItems() const;
  int TotalNumHierarchyConflictingItems() const;
  int TotalNumServerConflictingItems() const;
  int TotalNumSimpleConflictingItems() const;

  // Aggregate sum of SimpleConflictingItemSize() and other
  // ${Type}ConflictingItemSize() methods 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 if the last download_updates_command received a valid
  // server response.
  bool download_updates_succeeded() const {
    return model_neutral_.last_download_updates_result
        == SYNCER_OK;
  }

  // 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_;
  }

  base::Time sync_start_time() const {
    // The time at which we sent the first GetUpdates command for this sync.
    return sync_start_time_;
  }

  bool HasBookmarkCommitActivity() const {
    return ActiveGroupRestrictionIncludesModel(syncable::BOOKMARKS);
  }

  const ModelNeutralState& model_neutral_state() const {
    return model_neutral_;
  }

  // A toolbelt full of methods for updating counters and flags.
  void set_num_server_changes_remaining(int64 changes_remaining);
  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 increment_num_reflected_updates_downloaded_by(int value);
  void set_types_needing_local_migration(syncable::ModelTypeSet types);
  void increment_num_local_overwrites();
  void increment_num_server_overwrites();
  void set_sync_protocol_error(const SyncProtocolError& error);
  void set_last_download_updates_result(const SyncerError result);
  void set_commit_result(const SyncerError result);

  void update_conflicts_resolved(bool resolved);
  void reset_conflicts_resolved();

  void UpdateStartTime();

  void set_debug_info_sent();

  bool debug_info_sent() const;

 private:
  friend class ScopedModelSafeGroupRestriction;

  // 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;
  }

  // Returns the state, if it exists, or NULL otherwise.
  const PerModelSafeGroupState* GetModelSafeGroupState(
      bool restrict, ModelSafeGroup group) const;

  // Helper to lazily create objects for per-ModelSafeGroup state.
  PerModelSafeGroupState* GetOrCreateModelSafeGroupState(
      bool restrict, ModelSafeGroup group);

  ModelNeutralState model_neutral_;
  std::map<ModelSafeGroup, PerModelSafeGroupState*> per_model_group_;

  STLValueDeleter<std::map<ModelSafeGroup, PerModelSafeGroupState*> >
      per_model_group_deleter_;

  // 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_;

  base::Time sync_start_time_;

  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);
};

}  // namespace sessions
}  // namespace syncer

#endif  // SYNC_SESSIONS_STATUS_CONTROLLER_H_