1 // Copyright (c) 2010 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 #include "chrome/browser/sync/notifier/registration_manager.h"
6
7 #include <algorithm>
8 #include <cstddef>
9 #include <string>
10
11 #include "base/rand_util.h"
12 #include "chrome/browser/sync/notifier/invalidation_util.h"
13 #include "chrome/browser/sync/syncable/model_type.h"
14
15 namespace sync_notifier {
16
PendingRegistrationInfo()17 RegistrationManager::PendingRegistrationInfo::PendingRegistrationInfo() {}
18
RegistrationStatus()19 RegistrationManager::RegistrationStatus::RegistrationStatus()
20 : model_type(syncable::UNSPECIFIED),
21 registration_manager(NULL),
22 state(invalidation::RegistrationState_UNREGISTERED) {}
23
~RegistrationStatus()24 RegistrationManager::RegistrationStatus::~RegistrationStatus() {}
25
DoRegister()26 void RegistrationManager::RegistrationStatus::DoRegister() {
27 DCHECK_NE(model_type, syncable::UNSPECIFIED);
28 DCHECK(registration_manager);
29 // We might be called explicitly, so stop the timer manually and
30 // reset the delay.
31 registration_timer.Stop();
32 delay = base::TimeDelta();
33 registration_manager->DoRegisterType(model_type);
34 DCHECK(!last_registration_request.is_null());
35 }
36
37 const int RegistrationManager::kInitialRegistrationDelaySeconds = 5;
38 const int RegistrationManager::kRegistrationDelayExponent = 2;
39 const double RegistrationManager::kRegistrationDelayMaxJitter = 0.5;
40 const int RegistrationManager::kMinRegistrationDelaySeconds = 1;
41 // 1 hour.
42 const int RegistrationManager::kMaxRegistrationDelaySeconds = 60 * 60;
43
RegistrationManager(invalidation::InvalidationClient * invalidation_client)44 RegistrationManager::RegistrationManager(
45 invalidation::InvalidationClient* invalidation_client)
46 : invalidation_client_(invalidation_client) {
47 DCHECK(invalidation_client_);
48 // Initialize statuses.
49 for (int i = syncable::FIRST_REAL_MODEL_TYPE;
50 i < syncable::MODEL_TYPE_COUNT; ++i) {
51 syncable::ModelType model_type = syncable::ModelTypeFromInt(i);
52 RegistrationStatus* status = ®istration_statuses_[model_type];
53 status->model_type = model_type;
54 status->registration_manager = this;
55 }
56 }
57
~RegistrationManager()58 RegistrationManager::~RegistrationManager() {
59 DCHECK(non_thread_safe_.CalledOnValidThread());
60 }
61
SetRegisteredTypes(const syncable::ModelTypeSet & types)62 void RegistrationManager::SetRegisteredTypes(
63 const syncable::ModelTypeSet& types) {
64 DCHECK(non_thread_safe_.CalledOnValidThread());
65
66 for (int i = syncable::FIRST_REAL_MODEL_TYPE;
67 i < syncable::MODEL_TYPE_COUNT; ++i) {
68 syncable::ModelType model_type = syncable::ModelTypeFromInt(i);
69 if (types.count(model_type) > 0) {
70 if (!IsTypeRegistered(model_type)) {
71 TryRegisterType(model_type, false /* is_retry */);
72 }
73 } else {
74 if (IsTypeRegistered(model_type)) {
75 UnregisterType(model_type);
76 }
77 }
78 }
79 }
80
MarkRegistrationLost(syncable::ModelType model_type)81 void RegistrationManager::MarkRegistrationLost(
82 syncable::ModelType model_type) {
83 DCHECK(non_thread_safe_.CalledOnValidThread());
84 registration_statuses_[model_type].state =
85 invalidation::RegistrationState_UNREGISTERED;
86 TryRegisterType(model_type, true /* is_retry */);
87 }
88
MarkAllRegistrationsLost()89 void RegistrationManager::MarkAllRegistrationsLost() {
90 DCHECK(non_thread_safe_.CalledOnValidThread());
91 for (int i = syncable::FIRST_REAL_MODEL_TYPE;
92 i < syncable::MODEL_TYPE_COUNT; ++i) {
93 syncable::ModelType model_type = syncable::ModelTypeFromInt(i);
94 if (IsTypeRegistered(model_type)) {
95 MarkRegistrationLost(model_type);
96 }
97 }
98 }
99
GetRegisteredTypes() const100 syncable::ModelTypeSet RegistrationManager::GetRegisteredTypes() const {
101 DCHECK(non_thread_safe_.CalledOnValidThread());
102 syncable::ModelTypeSet registered_types;
103 for (int i = syncable::FIRST_REAL_MODEL_TYPE;
104 i < syncable::MODEL_TYPE_COUNT; ++i) {
105 syncable::ModelType model_type = syncable::ModelTypeFromInt(i);
106 if (IsTypeRegistered(model_type)) {
107 registered_types.insert(model_type);
108 }
109 }
110 return registered_types;
111 }
112
113 RegistrationManager::PendingRegistrationMap
GetPendingRegistrations() const114 RegistrationManager::GetPendingRegistrations() const {
115 DCHECK(non_thread_safe_.CalledOnValidThread());
116 PendingRegistrationMap pending_registrations;
117 for (int i = syncable::FIRST_REAL_MODEL_TYPE;
118 i < syncable::MODEL_TYPE_COUNT; ++i) {
119 syncable::ModelType model_type = syncable::ModelTypeFromInt(i);
120 const RegistrationStatus& status = registration_statuses_[model_type];
121 if (status.registration_timer.IsRunning()) {
122 pending_registrations[model_type].last_registration_request =
123 status.last_registration_request;
124 pending_registrations[model_type].registration_attempt =
125 status.last_registration_attempt;
126 pending_registrations[model_type].delay = status.delay;
127 pending_registrations[model_type].actual_delay =
128 status.registration_timer.GetCurrentDelay();
129 }
130 }
131 return pending_registrations;
132 }
133
FirePendingRegistrationsForTest()134 void RegistrationManager::FirePendingRegistrationsForTest() {
135 DCHECK(non_thread_safe_.CalledOnValidThread());
136 for (int i = syncable::FIRST_REAL_MODEL_TYPE;
137 i < syncable::MODEL_TYPE_COUNT; ++i) {
138 syncable::ModelType model_type = syncable::ModelTypeFromInt(i);
139 RegistrationStatus* status = ®istration_statuses_[model_type];
140 if (status->registration_timer.IsRunning()) {
141 status->DoRegister();
142 }
143 }
144 }
145
146 // static
CalculateBackoff(double retry_interval,double initial_retry_interval,double min_retry_interval,double max_retry_interval,double backoff_exponent,double jitter,double max_jitter)147 double RegistrationManager::CalculateBackoff(
148 double retry_interval,
149 double initial_retry_interval,
150 double min_retry_interval,
151 double max_retry_interval,
152 double backoff_exponent,
153 double jitter,
154 double max_jitter) {
155 // scaled_jitter lies in [-max_jitter, max_jitter].
156 double scaled_jitter = jitter * max_jitter;
157 double new_retry_interval =
158 (retry_interval == 0.0) ?
159 (initial_retry_interval * (1.0 + scaled_jitter)) :
160 (retry_interval * (backoff_exponent + scaled_jitter));
161 return std::max(min_retry_interval,
162 std::min(max_retry_interval, new_retry_interval));
163 }
164
GetJitter()165 double RegistrationManager::GetJitter() {
166 // |jitter| lies in [-1.0, 1.0), which is low-biased, but only
167 // barely.
168 //
169 // TODO(akalin): Fix the bias.
170 return 2.0 * base::RandDouble() - 1.0;
171 }
172
TryRegisterType(syncable::ModelType model_type,bool is_retry)173 void RegistrationManager::TryRegisterType(syncable::ModelType model_type,
174 bool is_retry) {
175 DCHECK(non_thread_safe_.CalledOnValidThread());
176 RegistrationStatus* status = ®istration_statuses_[model_type];
177 status->last_registration_attempt = base::Time::Now();
178 if (is_retry) {
179 // If we're a retry, we must have tried at least once before.
180 DCHECK(!status->last_registration_request.is_null());
181 // delay = max(0, (now - last request) + next_delay)
182 status->delay =
183 (status->last_registration_request -
184 status->last_registration_attempt) +
185 status->next_delay;
186 base::TimeDelta delay =
187 (status->delay <= base::TimeDelta()) ?
188 base::TimeDelta() : status->delay;
189 VLOG(2) << "Registering "
190 << syncable::ModelTypeToString(model_type) << " in "
191 << delay.InMilliseconds() << " ms";
192 status->registration_timer.Stop();
193 status->registration_timer.Start(
194 delay, status, &RegistrationManager::RegistrationStatus::DoRegister);
195 double next_delay_seconds =
196 CalculateBackoff(static_cast<double>(status->next_delay.InSeconds()),
197 kInitialRegistrationDelaySeconds,
198 kMinRegistrationDelaySeconds,
199 kMaxRegistrationDelaySeconds,
200 kRegistrationDelayExponent,
201 GetJitter(),
202 kRegistrationDelayMaxJitter);
203 status->next_delay =
204 base::TimeDelta::FromSeconds(static_cast<int64>(next_delay_seconds));
205 VLOG(2) << "New next delay for "
206 << syncable::ModelTypeToString(model_type) << " is "
207 << status->next_delay.InSeconds() << " seconds";
208 } else {
209 VLOG(2) << "Not a retry -- registering "
210 << syncable::ModelTypeToString(model_type) << " immediately";
211 status->delay = base::TimeDelta();
212 status->next_delay = base::TimeDelta();
213 status->DoRegister();
214 }
215 }
216
DoRegisterType(syncable::ModelType model_type)217 void RegistrationManager::DoRegisterType(syncable::ModelType model_type) {
218 DCHECK(non_thread_safe_.CalledOnValidThread());
219 invalidation::ObjectId object_id;
220 if (!RealModelTypeToObjectId(model_type, &object_id)) {
221 LOG(DFATAL) << "Invalid model type: " << model_type;
222 return;
223 }
224 invalidation_client_->Register(object_id);
225 RegistrationStatus* status = ®istration_statuses_[model_type];
226 status->state = invalidation::RegistrationState_REGISTERED;
227 status->last_registration_request = base::Time::Now();
228 }
229
UnregisterType(syncable::ModelType model_type)230 void RegistrationManager::UnregisterType(syncable::ModelType model_type) {
231 DCHECK(non_thread_safe_.CalledOnValidThread());
232 invalidation::ObjectId object_id;
233 if (!RealModelTypeToObjectId(model_type, &object_id)) {
234 LOG(DFATAL) << "Invalid model type: " << model_type;
235 return;
236 }
237 invalidation_client_->Unregister(object_id);
238 RegistrationStatus* status = ®istration_statuses_[model_type];
239 status->state = invalidation::RegistrationState_UNREGISTERED;
240 }
241
IsTypeRegistered(syncable::ModelType model_type) const242 bool RegistrationManager::IsTypeRegistered(
243 syncable::ModelType model_type) const {
244 DCHECK(non_thread_safe_.CalledOnValidThread());
245 return registration_statuses_[model_type].state ==
246 invalidation::RegistrationState_REGISTERED;
247 }
248
249 } // namespace sync_notifier
250