• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 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 "components/invalidation/registration_manager.h"
6 
7 #include <algorithm>
8 #include <cstddef>
9 #include <iterator>
10 #include <string>
11 #include <utility>
12 
13 #include "base/rand_util.h"
14 #include "base/stl_util.h"
15 #include "components/invalidation/invalidation_util.h"
16 #include "google/cacheinvalidation/include/invalidation-client.h"
17 #include "google/cacheinvalidation/include/types.h"
18 
19 namespace syncer {
20 
PendingRegistrationInfo()21 RegistrationManager::PendingRegistrationInfo::PendingRegistrationInfo() {}
22 
RegistrationStatus(const invalidation::ObjectId & id,RegistrationManager * manager)23 RegistrationManager::RegistrationStatus::RegistrationStatus(
24     const invalidation::ObjectId& id, RegistrationManager* manager)
25     : id(id),
26       registration_manager(manager),
27       enabled(true),
28       state(invalidation::InvalidationListener::UNREGISTERED) {
29   DCHECK(registration_manager);
30 }
31 
~RegistrationStatus()32 RegistrationManager::RegistrationStatus::~RegistrationStatus() {}
33 
DoRegister()34 void RegistrationManager::RegistrationStatus::DoRegister() {
35   CHECK(enabled);
36   // We might be called explicitly, so stop the timer manually and
37   // reset the delay.
38   registration_timer.Stop();
39   delay = base::TimeDelta();
40   registration_manager->DoRegisterId(id);
41   DCHECK(!last_registration_request.is_null());
42 }
43 
Disable()44 void RegistrationManager::RegistrationStatus::Disable() {
45   enabled = false;
46   state = invalidation::InvalidationListener::UNREGISTERED;
47   registration_timer.Stop();
48   delay = base::TimeDelta();
49 }
50 
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;
55 // 1 hour.
56 const int RegistrationManager::kMaxRegistrationDelaySeconds = 60 * 60;
57 
RegistrationManager(invalidation::InvalidationClient * invalidation_client)58 RegistrationManager::RegistrationManager(
59     invalidation::InvalidationClient* invalidation_client)
60     : invalidation_client_(invalidation_client) {
61   DCHECK(invalidation_client_);
62 }
63 
~RegistrationManager()64 RegistrationManager::~RegistrationManager() {
65   DCHECK(CalledOnValidThread());
66   STLDeleteValues(&registration_statuses_);
67 }
68 
UpdateRegisteredIds(const ObjectIdSet & ids)69 ObjectIdSet RegistrationManager::UpdateRegisteredIds(const ObjectIdSet& ids) {
70   DCHECK(CalledOnValidThread());
71 
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()),
78                       ObjectIdLessThan());
79 
80   for (ObjectIdSet::const_iterator it = to_unregister.begin();
81        it != to_unregister.end(); ++it) {
82     UnregisterId(*it);
83   }
84 
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)));
90     }
91     if (!IsIdRegistered(*it)) {
92       TryRegisterId(*it, false /* is-retry */);
93     }
94   }
95 
96   return to_unregister;
97 }
98 
MarkRegistrationLost(const invalidation::ObjectId & id)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";
106     return;
107   }
108   if (!it->second->enabled) {
109     return;
110   }
111   it->second->state = invalidation::InvalidationListener::UNREGISTERED;
112   bool is_retry = !it->second->last_registration_request.is_null();
113   TryRegisterId(id, is_retry);
114 }
115 
MarkAllRegistrationsLost()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);
123     }
124   }
125 }
126 
DisableId(const invalidation::ObjectId & id)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);
133     return;
134   }
135   it->second->Disable();
136 }
137 
138 // 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)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,
145     double jitter,
146     double max_jitter) {
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));
155 }
156 
GetRegisteredIdsForTest() const157 ObjectIdSet RegistrationManager::GetRegisteredIdsForTest() const {
158   return GetRegisteredIds();
159 }
160 
161 RegistrationManager::PendingRegistrationMap
GetPendingRegistrationsForTest() const162     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();
178     }
179   }
180   return pending_registrations;
181 }
182 
FirePendingRegistrationsForTest()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();
190     }
191   }
192 }
193 
GetJitter()194 double RegistrationManager::GetJitter() {
195   // |jitter| lies in [-1.0, 1.0), which is low-biased, but only
196   // barely.
197   //
198   // TODO(akalin): Fix the bias.
199   return 2.0 * base::RandDouble() - 1.0;
200 }
201 
TryRegisterId(const invalidation::ObjectId & id,bool is_retry)202 void RegistrationManager::TryRegisterId(const invalidation::ObjectId& id,
203                                         bool is_retry) {
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";
209     return;
210   }
211   RegistrationStatus* status = it->second;
212   if (!status->enabled) {
213     // Disabled, so do nothing.
214     return;
215   }
216   status->last_registration_attempt = base::Time::Now();
217   if (is_retry) {
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)
221     status->delay =
222         (status->last_registration_request -
223          status->last_registration_attempt) +
224         status->next_delay;
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,
240                          GetJitter(),
241                          kRegistrationDelayMaxJitter);
242     status->next_delay =
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";
247   } else {
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();
253   }
254 }
255 
DoRegisterId(const invalidation::ObjectId & id)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";
263     return;
264   }
265   it->second->state = invalidation::InvalidationListener::REGISTERED;
266   it->second->last_registration_request = base::Time::Now();
267 }
268 
UnregisterId(const invalidation::ObjectId & id)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";
276     return;
277   }
278   delete it->second;
279   registration_statuses_.erase(it);
280 }
281 
282 
GetRegisteredIds() const283 ObjectIdSet RegistrationManager::GetRegisteredIds() const {
284   DCHECK(CalledOnValidThread());
285   ObjectIdSet ids;
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);
291     }
292   }
293   return ids;
294 }
295 
IsIdRegistered(const invalidation::ObjectId & id) const296 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;
303 }
304 
305 }  // namespace syncer
306