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 21 RegistrationManager::PendingRegistrationInfo::PendingRegistrationInfo() {} 22 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 32 RegistrationManager::RegistrationStatus::~RegistrationStatus() {} 33 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 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 58 RegistrationManager::RegistrationManager( 59 invalidation::InvalidationClient* invalidation_client) 60 : invalidation_client_(invalidation_client) { 61 DCHECK(invalidation_client_); 62 } 63 64 RegistrationManager::~RegistrationManager() { 65 DCHECK(CalledOnValidThread()); 66 STLDeleteValues(®istration_statuses_); 67 } 68 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 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 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 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 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 157 ObjectIdSet RegistrationManager::GetRegisteredIdsForTest() const { 158 return GetRegisteredIds(); 159 } 160 161 RegistrationManager::PendingRegistrationMap 162 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 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 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 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 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 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 283 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 296 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