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 |