1 // Copyright (c) 2012 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.
5 #include "sync/notifier/registration_manager.h"
13 #include "base/rand_util.h"
14 #include "base/stl_util.h"
15 #include "google/cacheinvalidation/include/invalidation-client.h"
16 #include "google/cacheinvalidation/include/types.h"
17 #include "sync/notifier/invalidation_util.h"
21 RegistrationManager::PendingRegistrationInfo::PendingRegistrationInfo() {}
23 RegistrationManager::RegistrationStatus::RegistrationStatus(
24 const invalidation::ObjectId
& id
, RegistrationManager
* manager
)
26 registration_manager(manager
),
28 state(invalidation::InvalidationListener::UNREGISTERED
) {
29 DCHECK(registration_manager
);
32 RegistrationManager::RegistrationStatus::~RegistrationStatus() {}
34 void RegistrationManager::RegistrationStatus::DoRegister() {
36 // We might be called explicitly, so stop the timer manually and
38 registration_timer
.Stop();
39 delay
= base::TimeDelta();
40 registration_manager
->DoRegisterId(id
);
41 DCHECK(!last_registration_request
.is_null());
44 void RegistrationManager::RegistrationStatus::Disable() {
46 state
= invalidation::InvalidationListener::UNREGISTERED
;
47 registration_timer
.Stop();
48 delay
= base::TimeDelta();
51 const int RegistrationManager::kInitialRegistrationDelaySeconds
= 5;
52 const int RegistrationManager::kRegistrationDelayExponent
= 2;
53 const double RegistrationManager::kRegistrationDelayMaxJitter
= 0.5;
54 const int RegistrationManager::kMinRegistrationDelaySeconds
= 1;
56 const int RegistrationManager::kMaxRegistrationDelaySeconds
= 60 * 60;
58 RegistrationManager::RegistrationManager(
59 invalidation::InvalidationClient
* invalidation_client
)
60 : invalidation_client_(invalidation_client
) {
61 DCHECK(invalidation_client_
);
64 RegistrationManager::~RegistrationManager() {
65 DCHECK(CalledOnValidThread());
66 STLDeleteValues(®istration_statuses_
);
69 ObjectIdSet
RegistrationManager::UpdateRegisteredIds(const ObjectIdSet
& ids
) {
70 DCHECK(CalledOnValidThread());
72 const ObjectIdSet
& old_ids
= GetRegisteredIds();
73 const ObjectIdSet
& to_register
= ids
;
74 ObjectIdSet to_unregister
;
75 std::set_difference(old_ids
.begin(), old_ids
.end(),
76 ids
.begin(), ids
.end(),
77 std::inserter(to_unregister
, to_unregister
.begin()),
80 for (ObjectIdSet::const_iterator it
= to_unregister
.begin();
81 it
!= to_unregister
.end(); ++it
) {
85 for (ObjectIdSet::const_iterator it
= to_register
.begin();
86 it
!= to_register
.end(); ++it
) {
87 if (!ContainsKey(registration_statuses_
, *it
)) {
88 registration_statuses_
.insert(
89 std::make_pair(*it
, new RegistrationStatus(*it
, this)));
91 if (!IsIdRegistered(*it
)) {
92 TryRegisterId(*it
, false /* is-retry */);
99 void RegistrationManager::MarkRegistrationLost(
100 const invalidation::ObjectId
& id
) {
101 DCHECK(CalledOnValidThread());
102 RegistrationStatusMap::const_iterator it
= registration_statuses_
.find(id
);
103 if (it
== registration_statuses_
.end()) {
104 DVLOG(1) << "Attempt to mark non-existent registration for "
105 << ObjectIdToString(id
) << " as lost";
108 if (!it
->second
->enabled
) {
111 it
->second
->state
= invalidation::InvalidationListener::UNREGISTERED
;
112 bool is_retry
= !it
->second
->last_registration_request
.is_null();
113 TryRegisterId(id
, is_retry
);
116 void RegistrationManager::MarkAllRegistrationsLost() {
117 DCHECK(CalledOnValidThread());
118 for (RegistrationStatusMap::const_iterator it
=
119 registration_statuses_
.begin();
120 it
!= registration_statuses_
.end(); ++it
) {
121 if (IsIdRegistered(it
->first
)) {
122 MarkRegistrationLost(it
->first
);
127 void RegistrationManager::DisableId(const invalidation::ObjectId
& id
) {
128 DCHECK(CalledOnValidThread());
129 RegistrationStatusMap::const_iterator it
= registration_statuses_
.find(id
);
130 if (it
== registration_statuses_
.end()) {
131 DVLOG(1) << "Attempt to disable non-existent registration for "
132 << ObjectIdToString(id
);
135 it
->second
->Disable();
139 double RegistrationManager::CalculateBackoff(
140 double retry_interval
,
141 double initial_retry_interval
,
142 double min_retry_interval
,
143 double max_retry_interval
,
144 double backoff_exponent
,
147 // scaled_jitter lies in [-max_jitter, max_jitter].
148 double scaled_jitter
= jitter
* max_jitter
;
149 double new_retry_interval
=
150 (retry_interval
== 0.0) ?
151 (initial_retry_interval
* (1.0 + scaled_jitter
)) :
152 (retry_interval
* (backoff_exponent
+ scaled_jitter
));
153 return std::max(min_retry_interval
,
154 std::min(max_retry_interval
, new_retry_interval
));
157 ObjectIdSet
RegistrationManager::GetRegisteredIdsForTest() const {
158 return GetRegisteredIds();
161 RegistrationManager::PendingRegistrationMap
162 RegistrationManager::GetPendingRegistrationsForTest() const {
163 DCHECK(CalledOnValidThread());
164 PendingRegistrationMap pending_registrations
;
165 for (RegistrationStatusMap::const_iterator it
=
166 registration_statuses_
.begin();
167 it
!= registration_statuses_
.end(); ++it
) {
168 const invalidation::ObjectId
& id
= it
->first
;
169 RegistrationStatus
* status
= it
->second
;
170 if (status
->registration_timer
.IsRunning()) {
171 pending_registrations
[id
].last_registration_request
=
172 status
->last_registration_request
;
173 pending_registrations
[id
].registration_attempt
=
174 status
->last_registration_attempt
;
175 pending_registrations
[id
].delay
= status
->delay
;
176 pending_registrations
[id
].actual_delay
=
177 status
->registration_timer
.GetCurrentDelay();
180 return pending_registrations
;
183 void RegistrationManager::FirePendingRegistrationsForTest() {
184 DCHECK(CalledOnValidThread());
185 for (RegistrationStatusMap::const_iterator it
=
186 registration_statuses_
.begin();
187 it
!= registration_statuses_
.end(); ++it
) {
188 if (it
->second
->registration_timer
.IsRunning()) {
189 it
->second
->DoRegister();
194 double RegistrationManager::GetJitter() {
195 // |jitter| lies in [-1.0, 1.0), which is low-biased, but only
198 // TODO(akalin): Fix the bias.
199 return 2.0 * base::RandDouble() - 1.0;
202 void RegistrationManager::TryRegisterId(const invalidation::ObjectId
& id
,
204 DCHECK(CalledOnValidThread());
205 RegistrationStatusMap::const_iterator it
= registration_statuses_
.find(id
);
206 if (it
== registration_statuses_
.end()) {
207 NOTREACHED() << "TryRegisterId called on " << ObjectIdToString(id
)
208 << " which is not in the registration map";
211 RegistrationStatus
* status
= it
->second
;
212 if (!status
->enabled
) {
213 // Disabled, so do nothing.
216 status
->last_registration_attempt
= base::Time::Now();
218 // If we're a retry, we must have tried at least once before.
219 DCHECK(!status
->last_registration_request
.is_null());
220 // delay = max(0, (now - last request) + next_delay)
222 (status
->last_registration_request
-
223 status
->last_registration_attempt
) +
225 base::TimeDelta delay
=
226 (status
->delay
<= base::TimeDelta()) ?
227 base::TimeDelta() : status
->delay
;
228 DVLOG(2) << "Registering "
229 << ObjectIdToString(id
) << " in "
230 << delay
.InMilliseconds() << " ms";
231 status
->registration_timer
.Stop();
232 status
->registration_timer
.Start(FROM_HERE
,
233 delay
, status
, &RegistrationManager::RegistrationStatus::DoRegister
);
234 double next_delay_seconds
=
235 CalculateBackoff(static_cast<double>(status
->next_delay
.InSeconds()),
236 kInitialRegistrationDelaySeconds
,
237 kMinRegistrationDelaySeconds
,
238 kMaxRegistrationDelaySeconds
,
239 kRegistrationDelayExponent
,
241 kRegistrationDelayMaxJitter
);
243 base::TimeDelta::FromSeconds(static_cast<int64
>(next_delay_seconds
));
244 DVLOG(2) << "New next delay for "
245 << ObjectIdToString(id
) << " is "
246 << status
->next_delay
.InSeconds() << " seconds";
248 DVLOG(2) << "Not a retry -- registering "
249 << ObjectIdToString(id
) << " immediately";
250 status
->delay
= base::TimeDelta();
251 status
->next_delay
= base::TimeDelta();
252 status
->DoRegister();
256 void RegistrationManager::DoRegisterId(const invalidation::ObjectId
& id
) {
257 DCHECK(CalledOnValidThread());
258 invalidation_client_
->Register(id
);
259 RegistrationStatusMap::const_iterator it
= registration_statuses_
.find(id
);
260 if (it
== registration_statuses_
.end()) {
261 NOTREACHED() << "DoRegisterId called on " << ObjectIdToString(id
)
262 << " which is not in the registration map";
265 it
->second
->state
= invalidation::InvalidationListener::REGISTERED
;
266 it
->second
->last_registration_request
= base::Time::Now();
269 void RegistrationManager::UnregisterId(const invalidation::ObjectId
& id
) {
270 DCHECK(CalledOnValidThread());
271 invalidation_client_
->Unregister(id
);
272 RegistrationStatusMap::iterator it
= registration_statuses_
.find(id
);
273 if (it
== registration_statuses_
.end()) {
274 NOTREACHED() << "UnregisterId called on " << ObjectIdToString(id
)
275 << " which is not in the registration map";
279 registration_statuses_
.erase(it
);
283 ObjectIdSet
RegistrationManager::GetRegisteredIds() const {
284 DCHECK(CalledOnValidThread());
286 for (RegistrationStatusMap::const_iterator it
=
287 registration_statuses_
.begin();
288 it
!= registration_statuses_
.end(); ++it
) {
289 if (IsIdRegistered(it
->first
)) {
290 ids
.insert(it
->first
);
296 bool RegistrationManager::IsIdRegistered(
297 const invalidation::ObjectId
& id
) const {
298 DCHECK(CalledOnValidThread());
299 RegistrationStatusMap::const_iterator it
=
300 registration_statuses_
.find(id
);
301 return it
!= registration_statuses_
.end() &&
302 it
->second
->state
== invalidation::InvalidationListener::REGISTERED
;
305 } // namespace syncer