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 = ®istration_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 = ®istration_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 = ®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 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 = ®istration_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 = ®istration_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