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 15namespace sync_notifier { 16 17RegistrationManager::PendingRegistrationInfo::PendingRegistrationInfo() {} 18 19RegistrationManager::RegistrationStatus::RegistrationStatus() 20 : model_type(syncable::UNSPECIFIED), 21 registration_manager(NULL), 22 state(invalidation::RegistrationState_UNREGISTERED) {} 23 24RegistrationManager::RegistrationStatus::~RegistrationStatus() {} 25 26void 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 37const int RegistrationManager::kInitialRegistrationDelaySeconds = 5; 38const int RegistrationManager::kRegistrationDelayExponent = 2; 39const double RegistrationManager::kRegistrationDelayMaxJitter = 0.5; 40const int RegistrationManager::kMinRegistrationDelaySeconds = 1; 41// 1 hour. 42const int RegistrationManager::kMaxRegistrationDelaySeconds = 60 * 60; 43 44RegistrationManager::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 = ®istration_statuses_[model_type]; 53 status->model_type = model_type; 54 status->registration_manager = this; 55 } 56} 57 58RegistrationManager::~RegistrationManager() { 59 DCHECK(non_thread_safe_.CalledOnValidThread()); 60} 61 62void 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 81void 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 89void 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 100syncable::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 113RegistrationManager::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 134void 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 = ®istration_statuses_[model_type]; 140 if (status->registration_timer.IsRunning()) { 141 status->DoRegister(); 142 } 143 } 144} 145 146// static 147double 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 165double 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 173void RegistrationManager::TryRegisterType(syncable::ModelType model_type, 174 bool is_retry) { 175 DCHECK(non_thread_safe_.CalledOnValidThread()); 176 RegistrationStatus* status = ®istration_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 217void 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 = ®istration_statuses_[model_type]; 226 status->state = invalidation::RegistrationState_REGISTERED; 227 status->last_registration_request = base::Time::Now(); 228} 229 230void 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 = ®istration_statuses_[model_type]; 239 status->state = invalidation::RegistrationState_UNREGISTERED; 240} 241 242bool 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