| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 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 "sync/notifier/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 "google/cacheinvalidation/include/invalidation-client.h" | |
| 16 #include "google/cacheinvalidation/include/types.h" | |
| 17 #include "sync/notifier/invalidation_util.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 | |
| OLD | NEW |