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