Home | History | Annotate | Download | only in notifier
      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 
     17 RegistrationManager::PendingRegistrationInfo::PendingRegistrationInfo() {}
     18 
     19 RegistrationManager::RegistrationStatus::RegistrationStatus()
     20     : model_type(syncable::UNSPECIFIED),
     21       registration_manager(NULL),
     22       state(invalidation::RegistrationState_UNREGISTERED) {}
     23 
     24 RegistrationManager::RegistrationStatus::~RegistrationStatus() {}
     25 
     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 
     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 
     58 RegistrationManager::~RegistrationManager() {
     59   DCHECK(non_thread_safe_.CalledOnValidThread());
     60 }
     61 
     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 
     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 
     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 
    100 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
    114     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 
    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
    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 
    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 
    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 
    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 
    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 
    242 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