• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 = &registration_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 = &registration_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 = &registration_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 = &registration_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 = &registration_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