Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(632)

Side by Side Diff: chrome/browser/sync/engine/sync_scheduler.cc

Issue 9699057: [Sync] Move 'sync' target to sync/ (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Address Tim's comments Created 8 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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 "chrome/browser/sync/engine/sync_scheduler.h"
6
7 #include <algorithm>
8 #include <cstring>
9
10 #include "base/bind.h"
11 #include "base/compiler_specific.h"
12 #include "base/location.h"
13 #include "base/logging.h"
14 #include "base/message_loop.h"
15 #include "base/rand_util.h"
16 #include "chrome/browser/sync/engine/syncer.h"
17 #include "chrome/browser/sync/protocol/proto_enum_conversions.h"
18 #include "chrome/browser/sync/util/data_type_histogram.h"
19 #include "chrome/browser/sync/util/logging.h"
20 #include "sync/protocol/sync.pb.h"
21
22 using base::TimeDelta;
23 using base::TimeTicks;
24
25 namespace browser_sync {
26
27 using sessions::SyncSession;
28 using sessions::SyncSessionSnapshot;
29 using sessions::SyncSourceInfo;
30 using syncable::ModelTypeSet;
31 using syncable::ModelTypeSetToString;
32 using syncable::ModelTypePayloadMap;
33 using sync_pb::GetUpdatesCallerInfo;
34
35 namespace {
36 bool ShouldRequestEarlyExit(
37 const browser_sync::SyncProtocolError& error) {
38 switch (error.error_type) {
39 case browser_sync::SYNC_SUCCESS:
40 case browser_sync::MIGRATION_DONE:
41 case browser_sync::THROTTLED:
42 case browser_sync::TRANSIENT_ERROR:
43 return false;
44 case browser_sync::NOT_MY_BIRTHDAY:
45 case browser_sync::CLEAR_PENDING:
46 // If we send terminate sync early then |sync_cycle_ended| notification
47 // would not be sent. If there were no actions then |ACTIONABLE_ERROR|
48 // notification wouldnt be sent either. Then the UI layer would be left
49 // waiting forever. So assert we would send something.
50 DCHECK(error.action != browser_sync::UNKNOWN_ACTION);
51 return true;
52 case browser_sync::INVALID_CREDENTIAL:
53 // The notification for this is handled by PostAndProcessHeaders|.
54 // Server does no have to send any action for this.
55 return true;
56 // Make the default a NOTREACHED. So if a new error is introduced we
57 // think about its expected functionality.
58 default:
59 NOTREACHED();
60 return false;
61 }
62 }
63
64 bool IsActionableError(
65 const browser_sync::SyncProtocolError& error) {
66 return (error.action != browser_sync::UNKNOWN_ACTION);
67 }
68 } // namespace
69
70 SyncScheduler::DelayProvider::DelayProvider() {}
71 SyncScheduler::DelayProvider::~DelayProvider() {}
72
73 SyncScheduler::WaitInterval::WaitInterval()
74 : mode(UNKNOWN),
75 had_nudge(false) {
76 }
77
78 SyncScheduler::WaitInterval::~WaitInterval() {}
79
80 #define ENUM_CASE(x) case x: return #x; break;
81
82 const char* SyncScheduler::WaitInterval::GetModeString(Mode mode) {
83 switch (mode) {
84 ENUM_CASE(UNKNOWN);
85 ENUM_CASE(EXPONENTIAL_BACKOFF);
86 ENUM_CASE(THROTTLED);
87 }
88 NOTREACHED();
89 return "";
90 }
91
92 SyncScheduler::SyncSessionJob::SyncSessionJob()
93 : purpose(UNKNOWN),
94 is_canary_job(false) {
95 }
96
97 SyncScheduler::SyncSessionJob::~SyncSessionJob() {}
98
99 SyncScheduler::SyncSessionJob::SyncSessionJob(SyncSessionJobPurpose purpose,
100 base::TimeTicks start,
101 linked_ptr<sessions::SyncSession> session, bool is_canary_job,
102 const tracked_objects::Location& from_here) : purpose(purpose),
103 scheduled_start(start),
104 session(session),
105 is_canary_job(is_canary_job),
106 from_here(from_here) {
107 }
108
109 const char* SyncScheduler::SyncSessionJob::GetPurposeString(
110 SyncScheduler::SyncSessionJob::SyncSessionJobPurpose purpose) {
111 switch (purpose) {
112 ENUM_CASE(UNKNOWN);
113 ENUM_CASE(POLL);
114 ENUM_CASE(NUDGE);
115 ENUM_CASE(CLEAR_USER_DATA);
116 ENUM_CASE(CONFIGURATION);
117 ENUM_CASE(CLEANUP_DISABLED_TYPES);
118 }
119 NOTREACHED();
120 return "";
121 }
122
123 TimeDelta SyncScheduler::DelayProvider::GetDelay(
124 const base::TimeDelta& last_delay) {
125 return SyncScheduler::GetRecommendedDelay(last_delay);
126 }
127
128 GetUpdatesCallerInfo::GetUpdatesSource GetUpdatesFromNudgeSource(
129 NudgeSource source) {
130 switch (source) {
131 case NUDGE_SOURCE_NOTIFICATION:
132 return GetUpdatesCallerInfo::NOTIFICATION;
133 case NUDGE_SOURCE_LOCAL:
134 return GetUpdatesCallerInfo::LOCAL;
135 case NUDGE_SOURCE_CONTINUATION:
136 return GetUpdatesCallerInfo::SYNC_CYCLE_CONTINUATION;
137 case NUDGE_SOURCE_LOCAL_REFRESH:
138 return GetUpdatesCallerInfo::DATATYPE_REFRESH;
139 case NUDGE_SOURCE_UNKNOWN:
140 return GetUpdatesCallerInfo::UNKNOWN;
141 default:
142 NOTREACHED();
143 return GetUpdatesCallerInfo::UNKNOWN;
144 }
145 }
146
147 SyncScheduler::WaitInterval::WaitInterval(Mode mode, TimeDelta length)
148 : mode(mode), had_nudge(false), length(length) { }
149
150 // Helper macros to log with the syncer thread name; useful when there
151 // are multiple syncer threads involved.
152
153 #define SLOG(severity) LOG(severity) << name_ << ": "
154
155 #define SDVLOG(verbose_level) DVLOG(verbose_level) << name_ << ": "
156
157 #define SDVLOG_LOC(from_here, verbose_level) \
158 DVLOG_LOC(from_here, verbose_level) << name_ << ": "
159
160 namespace {
161
162 const int kDefaultSessionsCommitDelaySeconds = 10;
163
164 bool IsConfigRelatedUpdateSourceValue(
165 GetUpdatesCallerInfo::GetUpdatesSource source) {
166 switch (source) {
167 case GetUpdatesCallerInfo::RECONFIGURATION:
168 case GetUpdatesCallerInfo::MIGRATION:
169 case GetUpdatesCallerInfo::NEW_CLIENT:
170 case GetUpdatesCallerInfo::NEWLY_SUPPORTED_DATATYPE:
171 return true;
172 default:
173 return false;
174 }
175 }
176
177 } // namespace
178
179 SyncScheduler::SyncScheduler(const std::string& name,
180 sessions::SyncSessionContext* context,
181 Syncer* syncer)
182 : weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
183 weak_ptr_factory_for_weak_handle_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
184 weak_handle_this_(MakeWeakHandle(
185 weak_ptr_factory_for_weak_handle_.GetWeakPtr())),
186 name_(name),
187 sync_loop_(MessageLoop::current()),
188 started_(false),
189 syncer_short_poll_interval_seconds_(
190 TimeDelta::FromSeconds(kDefaultShortPollIntervalSeconds)),
191 syncer_long_poll_interval_seconds_(
192 TimeDelta::FromSeconds(kDefaultLongPollIntervalSeconds)),
193 sessions_commit_delay_(
194 TimeDelta::FromSeconds(kDefaultSessionsCommitDelaySeconds)),
195 mode_(NORMAL_MODE),
196 // Start with assuming everything is fine with the connection.
197 // At the end of the sync cycle we would have the correct status.
198 server_connection_ok_(true),
199 connection_code_(HttpResponse::SERVER_CONNECTION_OK),
200 delay_provider_(new DelayProvider()),
201 syncer_(syncer),
202 session_context_(context) {
203 DCHECK(sync_loop_);
204 }
205
206 SyncScheduler::~SyncScheduler() {
207 DCHECK_EQ(MessageLoop::current(), sync_loop_);
208 StopImpl(base::Closure());
209 }
210
211 void SyncScheduler::OnCredentialsUpdated() {
212 DCHECK_EQ(MessageLoop::current(), sync_loop_);
213
214 // TODO(lipalani): crbug.com/106262. One issue here is that if after
215 // the auth error we happened to do gettime and it succeeded then
216 // the |connection_code_| would be briefly OK however it would revert
217 // back to SYNC_AUTH_ERROR at the end of the sync cycle. The
218 // referenced bug explores the option of removing gettime calls
219 // altogethere
220 if (HttpResponse::SYNC_AUTH_ERROR == connection_code_) {
221 OnServerConnectionErrorFixed();
222 }
223 }
224
225 void SyncScheduler::OnConnectionStatusChange() {
226 if (HttpResponse::CONNECTION_UNAVAILABLE == connection_code_) {
227 // Optimistically assume that the connection is fixed and try
228 // connecting.
229 OnServerConnectionErrorFixed();
230 }
231 }
232
233 void SyncScheduler::OnServerConnectionErrorFixed() {
234 DCHECK(!server_connection_ok_);
235 connection_code_ = HttpResponse::SERVER_CONNECTION_OK;
236 server_connection_ok_ = true;
237 PostTask(FROM_HERE, "DoCanaryJob",
238 base::Bind(&SyncScheduler::DoCanaryJob,
239 weak_ptr_factory_.GetWeakPtr()));
240
241 }
242
243 void SyncScheduler::UpdateServerConnectionManagerStatus(
244 HttpResponse::ServerConnectionCode code) {
245 DCHECK_EQ(MessageLoop::current(), sync_loop_);
246 SDVLOG(2) << "New server connection code: "
247 << HttpResponse::GetServerConnectionCodeString(code);
248 bool old_server_connection_ok = server_connection_ok_;
249
250 connection_code_ = code;
251
252 // Note, be careful when adding cases here because if the SyncScheduler
253 // thinks there is no valid connection as determined by this method, it
254 // will drop out of *all* forward progress sync loops (it won't poll and it
255 // will queue up Talk notifications but not actually call SyncShare) until
256 // some external action causes a ServerConnectionManager to broadcast that
257 // a valid connection has been re-established
258 if (HttpResponse::CONNECTION_UNAVAILABLE == code ||
259 HttpResponse::SYNC_AUTH_ERROR == code) {
260 server_connection_ok_ = false;
261 SDVLOG(2) << "Sync auth error or unavailable connection: "
262 << "server connection is down";
263 } else if (HttpResponse::SERVER_CONNECTION_OK == code) {
264 server_connection_ok_ = true;
265 SDVLOG(2) << "Sync server connection is ok: "
266 << "server connection is up, doing canary job";
267 }
268
269 if (old_server_connection_ok != server_connection_ok_) {
270 const char* transition =
271 server_connection_ok_ ? "down -> up" : "up -> down";
272 SDVLOG(2) << "Server connection changed: " << transition;
273 }
274 }
275
276 void SyncScheduler::Start(Mode mode, const base::Closure& callback) {
277 DCHECK_EQ(MessageLoop::current(), sync_loop_);
278 std::string thread_name = MessageLoop::current()->thread_name();
279 if (thread_name.empty())
280 thread_name = "<Main thread>";
281 SDVLOG(2) << "Start called from thread "
282 << thread_name << " with mode " << GetModeString(mode);
283 if (!started_) {
284 started_ = true;
285 PostTask(FROM_HERE, "SendInitialSnapshot",
286 base::Bind(&SyncScheduler::SendInitialSnapshot,
287 weak_ptr_factory_.GetWeakPtr()));
288 }
289 PostTask(FROM_HERE, "StartImpl",
290 base::Bind(&SyncScheduler::StartImpl,
291 weak_ptr_factory_.GetWeakPtr(), mode, callback));
292 }
293
294 void SyncScheduler::SendInitialSnapshot() {
295 DCHECK_EQ(MessageLoop::current(), sync_loop_);
296 scoped_ptr<SyncSession> dummy(new SyncSession(session_context_.get(), this,
297 SyncSourceInfo(), ModelSafeRoutingInfo(),
298 std::vector<ModelSafeWorker*>()));
299 SyncEngineEvent event(SyncEngineEvent::STATUS_CHANGED);
300 sessions::SyncSessionSnapshot snapshot(dummy->TakeSnapshot());
301 event.snapshot = &snapshot;
302 session_context_->NotifyListeners(event);
303 }
304
305 void SyncScheduler::StartImpl(Mode mode, const base::Closure& callback) {
306 DCHECK_EQ(MessageLoop::current(), sync_loop_);
307 SDVLOG(2) << "In StartImpl with mode " << GetModeString(mode);
308
309 DCHECK_EQ(MessageLoop::current(), sync_loop_);
310 DCHECK(!session_context_->account_name().empty());
311 DCHECK(syncer_.get());
312 Mode old_mode = mode_;
313 mode_ = mode;
314 AdjustPolling(NULL); // Will kick start poll timer if needed.
315 if (!callback.is_null())
316 callback.Run();
317
318 if (old_mode != mode_) {
319 // We just changed our mode. See if there are any pending jobs that we could
320 // execute in the new mode.
321 DoPendingJobIfPossible(false);
322 }
323 }
324
325 SyncScheduler::JobProcessDecision SyncScheduler::DecideWhileInWaitInterval(
326 const SyncSessionJob& job) {
327 DCHECK_EQ(MessageLoop::current(), sync_loop_);
328 DCHECK(wait_interval_.get());
329 DCHECK_NE(job.purpose, SyncSessionJob::CLEAR_USER_DATA);
330 DCHECK_NE(job.purpose, SyncSessionJob::CLEANUP_DISABLED_TYPES);
331
332 SDVLOG(2) << "DecideWhileInWaitInterval with WaitInterval mode "
333 << WaitInterval::GetModeString(wait_interval_->mode)
334 << (wait_interval_->had_nudge ? " (had nudge)" : "")
335 << (job.is_canary_job ? " (canary)" : "");
336
337 if (job.purpose == SyncSessionJob::POLL)
338 return DROP;
339
340 DCHECK(job.purpose == SyncSessionJob::NUDGE ||
341 job.purpose == SyncSessionJob::CONFIGURATION);
342 if (wait_interval_->mode == WaitInterval::THROTTLED)
343 return SAVE;
344
345 DCHECK_EQ(wait_interval_->mode, WaitInterval::EXPONENTIAL_BACKOFF);
346 if (job.purpose == SyncSessionJob::NUDGE) {
347 if (mode_ == CONFIGURATION_MODE)
348 return SAVE;
349
350 // If we already had one nudge then just drop this nudge. We will retry
351 // later when the timer runs out.
352 if (!job.is_canary_job)
353 return wait_interval_->had_nudge ? DROP : CONTINUE;
354 else // We are here because timer ran out. So retry.
355 return CONTINUE;
356 }
357 return job.is_canary_job ? CONTINUE : SAVE;
358 }
359
360 SyncScheduler::JobProcessDecision SyncScheduler::DecideOnJob(
361 const SyncSessionJob& job) {
362 DCHECK_EQ(MessageLoop::current(), sync_loop_);
363 if (job.purpose == SyncSessionJob::CLEAR_USER_DATA ||
364 job.purpose == SyncSessionJob::CLEANUP_DISABLED_TYPES)
365 return CONTINUE;
366
367 // See if our type is throttled.
368 syncable::ModelTypeSet throttled_types =
369 session_context_->GetThrottledTypes();
370 if (job.purpose == SyncSessionJob::NUDGE &&
371 job.session->source().updates_source == GetUpdatesCallerInfo::LOCAL) {
372 syncable::ModelTypeSet requested_types;
373 for (ModelTypePayloadMap::const_iterator i =
374 job.session->source().types.begin();
375 i != job.session->source().types.end();
376 ++i) {
377 requested_types.Put(i->first);
378 }
379
380 if (!requested_types.Empty() && throttled_types.HasAll(requested_types))
381 return SAVE;
382 }
383
384 if (wait_interval_.get())
385 return DecideWhileInWaitInterval(job);
386
387 if (mode_ == CONFIGURATION_MODE) {
388 if (job.purpose == SyncSessionJob::NUDGE)
389 return SAVE;
390 else if (job.purpose == SyncSessionJob::CONFIGURATION)
391 return CONTINUE;
392 else
393 return DROP;
394 }
395
396 // We are in normal mode.
397 DCHECK_EQ(mode_, NORMAL_MODE);
398 DCHECK_NE(job.purpose, SyncSessionJob::CONFIGURATION);
399
400 // Freshness condition
401 if (job.scheduled_start < last_sync_session_end_time_) {
402 SDVLOG(2) << "Dropping job because of freshness";
403 return DROP;
404 }
405
406 if (server_connection_ok_)
407 return CONTINUE;
408
409 SDVLOG(2) << "Bad server connection. Using that to decide on job.";
410 return job.purpose == SyncSessionJob::NUDGE ? SAVE : DROP;
411 }
412
413 void SyncScheduler::InitOrCoalescePendingJob(const SyncSessionJob& job) {
414 DCHECK_EQ(MessageLoop::current(), sync_loop_);
415 DCHECK(job.purpose != SyncSessionJob::CONFIGURATION);
416 if (pending_nudge_.get() == NULL) {
417 SDVLOG(2) << "Creating a pending nudge job";
418 SyncSession* s = job.session.get();
419 scoped_ptr<SyncSession> session(new SyncSession(s->context(),
420 s->delegate(), s->source(), s->routing_info(), s->workers()));
421
422 SyncSessionJob new_job(SyncSessionJob::NUDGE, job.scheduled_start,
423 make_linked_ptr(session.release()), false, job.from_here);
424 pending_nudge_.reset(new SyncSessionJob(new_job));
425
426 return;
427 }
428
429 SDVLOG(2) << "Coalescing a pending nudge";
430 pending_nudge_->session->Coalesce(*(job.session.get()));
431 pending_nudge_->scheduled_start = job.scheduled_start;
432
433 // Unfortunately the nudge location cannot be modified. So it stores the
434 // location of the first caller.
435 }
436
437 bool SyncScheduler::ShouldRunJob(const SyncSessionJob& job) {
438 DCHECK_EQ(MessageLoop::current(), sync_loop_);
439 DCHECK(started_);
440
441 JobProcessDecision decision = DecideOnJob(job);
442 SDVLOG(2) << "Should run "
443 << SyncSessionJob::GetPurposeString(job.purpose)
444 << " job in mode " << GetModeString(mode_)
445 << ": " << GetDecisionString(decision);
446 if (decision != SAVE)
447 return decision == CONTINUE;
448
449 DCHECK(job.purpose == SyncSessionJob::NUDGE || job.purpose ==
450 SyncSessionJob::CONFIGURATION);
451
452 SaveJob(job);
453 return false;
454 }
455
456 void SyncScheduler::SaveJob(const SyncSessionJob& job) {
457 DCHECK_EQ(MessageLoop::current(), sync_loop_);
458 DCHECK_NE(job.purpose, SyncSessionJob::CLEAR_USER_DATA);
459 // TODO(sync): Should we also check that job.purpose !=
460 // CLEANUP_DISABLED_TYPES? (See http://crbug.com/90868.)
461 if (job.purpose == SyncSessionJob::NUDGE) {
462 SDVLOG(2) << "Saving a nudge job";
463 InitOrCoalescePendingJob(job);
464 } else if (job.purpose == SyncSessionJob::CONFIGURATION){
465 SDVLOG(2) << "Saving a configuration job";
466 DCHECK(wait_interval_.get());
467 DCHECK(mode_ == CONFIGURATION_MODE);
468
469 SyncSession* old = job.session.get();
470 SyncSession* s(new SyncSession(session_context_.get(), this,
471 old->source(), old->routing_info(), old->workers()));
472 SyncSessionJob new_job(job.purpose, TimeTicks::Now(),
473 make_linked_ptr(s), false, job.from_here);
474 wait_interval_->pending_configure_job.reset(new SyncSessionJob(new_job));
475 } // drop the rest.
476 // TODO(sync): Is it okay to drop the rest? It's weird that
477 // SaveJob() only does what it says sometimes. (See
478 // http://crbug.com/90868.)
479 }
480
481 // Functor for std::find_if to search by ModelSafeGroup.
482 struct ModelSafeWorkerGroupIs {
483 explicit ModelSafeWorkerGroupIs(ModelSafeGroup group) : group(group) {}
484 bool operator()(ModelSafeWorker* w) {
485 return group == w->GetModelSafeGroup();
486 }
487 ModelSafeGroup group;
488 };
489
490 void SyncScheduler::ScheduleClearUserData() {
491 DCHECK_EQ(MessageLoop::current(), sync_loop_);
492 PostTask(FROM_HERE, "ScheduleClearUserDataImpl",
493 base::Bind(&SyncScheduler::ScheduleClearUserDataImpl,
494 weak_ptr_factory_.GetWeakPtr()));
495 }
496
497 // TODO(sync): Remove the *Impl methods for the other Schedule*
498 // functions, too.
499 void SyncScheduler::ScheduleCleanupDisabledTypes() {
500 DCHECK_EQ(MessageLoop::current(), sync_loop_);
501 SyncSessionJob job(SyncSessionJob::CLEANUP_DISABLED_TYPES, TimeTicks::Now(),
502 make_linked_ptr(CreateSyncSession(SyncSourceInfo())),
503 false,
504 FROM_HERE);
505 ScheduleSyncSessionJob(job);
506 }
507
508 void SyncScheduler::ScheduleNudge(
509 const TimeDelta& delay,
510 NudgeSource source, ModelTypeSet types,
511 const tracked_objects::Location& nudge_location) {
512 DCHECK_EQ(MessageLoop::current(), sync_loop_);
513 SDVLOG_LOC(nudge_location, 2)
514 << "Nudge scheduled with delay " << delay.InMilliseconds() << " ms, "
515 << "source " << GetNudgeSourceString(source) << ", "
516 << "types " << ModelTypeSetToString(types);
517
518 ModelTypePayloadMap types_with_payloads =
519 syncable::ModelTypePayloadMapFromEnumSet(types, std::string());
520 PostTask(nudge_location, "ScheduleNudgeImpl",
521 base::Bind(&SyncScheduler::ScheduleNudgeImpl,
522 weak_ptr_factory_.GetWeakPtr(),
523 delay,
524 GetUpdatesFromNudgeSource(source),
525 types_with_payloads,
526 false,
527 nudge_location));
528 }
529
530 void SyncScheduler::ScheduleNudgeWithPayloads(
531 const TimeDelta& delay,
532 NudgeSource source, const ModelTypePayloadMap& types_with_payloads,
533 const tracked_objects::Location& nudge_location) {
534 DCHECK_EQ(MessageLoop::current(), sync_loop_);
535 SDVLOG_LOC(nudge_location, 2)
536 << "Nudge scheduled with delay " << delay.InMilliseconds() << " ms, "
537 << "source " << GetNudgeSourceString(source) << ", "
538 << "payloads "
539 << syncable::ModelTypePayloadMapToString(types_with_payloads);
540
541 PostTask(nudge_location, "ScheduleNudgeImpl",
542 base::Bind(&SyncScheduler::ScheduleNudgeImpl,
543 weak_ptr_factory_.GetWeakPtr(),
544 delay,
545 GetUpdatesFromNudgeSource(source),
546 types_with_payloads,
547 false,
548 nudge_location));
549 }
550
551 void SyncScheduler::ScheduleClearUserDataImpl() {
552 DCHECK_EQ(MessageLoop::current(), sync_loop_);
553 SyncSessionJob job(SyncSessionJob::CLEAR_USER_DATA, TimeTicks::Now(),
554 make_linked_ptr(CreateSyncSession(SyncSourceInfo())),
555 false,
556 FROM_HERE);
557
558 ScheduleSyncSessionJob(job);
559 }
560
561 void SyncScheduler::ScheduleNudgeImpl(
562 const TimeDelta& delay,
563 GetUpdatesCallerInfo::GetUpdatesSource source,
564 const ModelTypePayloadMap& types_with_payloads,
565 bool is_canary_job, const tracked_objects::Location& nudge_location) {
566 DCHECK_EQ(MessageLoop::current(), sync_loop_);
567
568 SDVLOG_LOC(nudge_location, 2)
569 << "In ScheduleNudgeImpl with delay "
570 << delay.InMilliseconds() << " ms, "
571 << "source " << GetUpdatesSourceString(source) << ", "
572 << "payloads "
573 << syncable::ModelTypePayloadMapToString(types_with_payloads)
574 << (is_canary_job ? " (canary)" : "");
575
576 SyncSourceInfo info(source, types_with_payloads);
577
578 SyncSession* session(CreateSyncSession(info));
579 SyncSessionJob job(SyncSessionJob::NUDGE, TimeTicks::Now() + delay,
580 make_linked_ptr(session), is_canary_job,
581 nudge_location);
582
583 session = NULL;
584 if (!ShouldRunJob(job))
585 return;
586
587 if (pending_nudge_.get()) {
588 if (IsBackingOff() && delay > TimeDelta::FromSeconds(1)) {
589 SDVLOG(2) << "Dropping the nudge because we are in backoff";
590 return;
591 }
592
593 SDVLOG(2) << "Coalescing pending nudge";
594 pending_nudge_->session->Coalesce(*(job.session.get()));
595
596 SDVLOG(2) << "Rescheduling pending nudge";
597 SyncSession* s = pending_nudge_->session.get();
598 job.session.reset(new SyncSession(s->context(), s->delegate(),
599 s->source(), s->routing_info(), s->workers()));
600
601 // Choose the start time as the earliest of the 2.
602 job.scheduled_start = std::min(job.scheduled_start,
603 pending_nudge_->scheduled_start);
604 pending_nudge_.reset();
605 }
606
607 // TODO(zea): Consider adding separate throttling/backoff for datatype
608 // refresh requests.
609 ScheduleSyncSessionJob(job);
610 }
611
612 // Helper to extract the routing info and workers corresponding to types in
613 // |types| from |registrar|.
614 void GetModelSafeParamsForTypes(ModelTypeSet types,
615 ModelSafeWorkerRegistrar* registrar, ModelSafeRoutingInfo* routes,
616 std::vector<ModelSafeWorker*>* workers) {
617 ModelSafeRoutingInfo r_tmp;
618 std::vector<ModelSafeWorker*> w_tmp;
619 registrar->GetModelSafeRoutingInfo(&r_tmp);
620 registrar->GetWorkers(&w_tmp);
621
622 bool passive_group_added = false;
623
624 typedef std::vector<ModelSafeWorker*>::const_iterator iter;
625 for (ModelTypeSet::Iterator it = types.First();
626 it.Good(); it.Inc()) {
627 const syncable::ModelType t = it.Get();
628 DCHECK_EQ(1U, r_tmp.count(t));
629 (*routes)[t] = r_tmp[t];
630 iter w_tmp_it = std::find_if(w_tmp.begin(), w_tmp.end(),
631 ModelSafeWorkerGroupIs(r_tmp[t]));
632 if (w_tmp_it != w_tmp.end()) {
633 iter workers_it = std::find_if(workers->begin(), workers->end(),
634 ModelSafeWorkerGroupIs(r_tmp[t]));
635 if (workers_it == workers->end())
636 workers->push_back(*w_tmp_it);
637
638 if (r_tmp[t] == GROUP_PASSIVE)
639 passive_group_added = true;
640 } else {
641 NOTREACHED();
642 }
643 }
644
645 // Always add group passive.
646 if (passive_group_added == false) {
647 iter it = std::find_if(w_tmp.begin(), w_tmp.end(),
648 ModelSafeWorkerGroupIs(GROUP_PASSIVE));
649 if (it != w_tmp.end())
650 workers->push_back(*it);
651 else
652 NOTREACHED();
653 }
654 }
655
656 void SyncScheduler::ScheduleConfig(
657 ModelTypeSet types,
658 GetUpdatesCallerInfo::GetUpdatesSource source) {
659 DCHECK_EQ(MessageLoop::current(), sync_loop_);
660 DCHECK(IsConfigRelatedUpdateSourceValue(source));
661 SDVLOG(2) << "Scheduling a config";
662 ModelSafeRoutingInfo routes;
663 std::vector<ModelSafeWorker*> workers;
664 GetModelSafeParamsForTypes(types, session_context_->registrar(),
665 &routes, &workers);
666
667 PostTask(FROM_HERE, "ScheduleConfigImpl",
668 base::Bind(&SyncScheduler::ScheduleConfigImpl,
669 weak_ptr_factory_.GetWeakPtr(),
670 routes,
671 workers,
672 source));
673 }
674
675 void SyncScheduler::ScheduleConfigImpl(
676 const ModelSafeRoutingInfo& routing_info,
677 const std::vector<ModelSafeWorker*>& workers,
678 const sync_pb::GetUpdatesCallerInfo::GetUpdatesSource source) {
679 DCHECK_EQ(MessageLoop::current(), sync_loop_);
680
681 SDVLOG(2) << "In ScheduleConfigImpl";
682 // TODO(tim): config-specific GetUpdatesCallerInfo value?
683 SyncSession* session = new SyncSession(session_context_.get(), this,
684 SyncSourceInfo(source,
685 syncable::ModelTypePayloadMapFromRoutingInfo(
686 routing_info, std::string())),
687 routing_info, workers);
688 SyncSessionJob job(SyncSessionJob::CONFIGURATION, TimeTicks::Now(),
689 make_linked_ptr(session),
690 false,
691 FROM_HERE);
692 ScheduleSyncSessionJob(job);
693 }
694
695 const char* SyncScheduler::GetModeString(SyncScheduler::Mode mode) {
696 switch (mode) {
697 ENUM_CASE(CONFIGURATION_MODE);
698 ENUM_CASE(NORMAL_MODE);
699 }
700 return "";
701 }
702
703 const char* SyncScheduler::GetDecisionString(
704 SyncScheduler::JobProcessDecision mode) {
705 switch (mode) {
706 ENUM_CASE(CONTINUE);
707 ENUM_CASE(SAVE);
708 ENUM_CASE(DROP);
709 }
710 return "";
711 }
712
713 void SyncScheduler::PostTask(
714 const tracked_objects::Location& from_here,
715 const char* name, const base::Closure& task) {
716 SDVLOG_LOC(from_here, 3) << "Posting " << name << " task";
717 DCHECK_EQ(MessageLoop::current(), sync_loop_);
718 if (!started_) {
719 SDVLOG(1) << "Not posting task as scheduler is stopped.";
720 return;
721 }
722 sync_loop_->PostTask(from_here, task);
723 }
724
725 void SyncScheduler::PostDelayedTask(
726 const tracked_objects::Location& from_here,
727 const char* name, const base::Closure& task, base::TimeDelta delay) {
728 SDVLOG_LOC(from_here, 3) << "Posting " << name << " task with "
729 << delay.InMilliseconds() << " ms delay";
730 DCHECK_EQ(MessageLoop::current(), sync_loop_);
731 if (!started_) {
732 SDVLOG(1) << "Not posting task as scheduler is stopped.";
733 return;
734 }
735 sync_loop_->PostDelayedTask(from_here, task, delay);
736 }
737
738 void SyncScheduler::ScheduleSyncSessionJob(const SyncSessionJob& job) {
739 DCHECK_EQ(MessageLoop::current(), sync_loop_);
740 TimeDelta delay = job.scheduled_start - TimeTicks::Now();
741 if (delay < TimeDelta::FromMilliseconds(0))
742 delay = TimeDelta::FromMilliseconds(0);
743 SDVLOG_LOC(job.from_here, 2)
744 << "In ScheduleSyncSessionJob with "
745 << SyncSessionJob::GetPurposeString(job.purpose)
746 << " job and " << delay.InMilliseconds() << " ms delay";
747
748 if (job.purpose == SyncSessionJob::NUDGE) {
749 SDVLOG_LOC(job.from_here, 2) << "Resetting pending_nudge";
750 DCHECK(!pending_nudge_.get() || pending_nudge_->session.get() ==
751 job.session);
752 pending_nudge_.reset(new SyncSessionJob(job));
753 }
754 PostDelayedTask(job.from_here, "DoSyncSessionJob",
755 base::Bind(&SyncScheduler::DoSyncSessionJob,
756 weak_ptr_factory_.GetWeakPtr(),
757 job),
758 delay);
759 }
760
761 void SyncScheduler::SetSyncerStepsForPurpose(
762 SyncSessionJob::SyncSessionJobPurpose purpose,
763 SyncerStep* start, SyncerStep* end) {
764 DCHECK_EQ(MessageLoop::current(), sync_loop_);
765 switch (purpose) {
766 case SyncSessionJob::CONFIGURATION:
767 *start = DOWNLOAD_UPDATES;
768 *end = APPLY_UPDATES;
769 return;
770 case SyncSessionJob::CLEAR_USER_DATA:
771 *start = CLEAR_PRIVATE_DATA;
772 *end = CLEAR_PRIVATE_DATA;
773 return;
774 case SyncSessionJob::NUDGE:
775 case SyncSessionJob::POLL:
776 *start = SYNCER_BEGIN;
777 *end = SYNCER_END;
778 return;
779 case SyncSessionJob::CLEANUP_DISABLED_TYPES:
780 *start = CLEANUP_DISABLED_TYPES;
781 *end = CLEANUP_DISABLED_TYPES;
782 return;
783 default:
784 NOTREACHED();
785 *start = SYNCER_END;
786 *end = SYNCER_END;
787 return;
788 }
789 }
790
791 void SyncScheduler::DoSyncSessionJob(const SyncSessionJob& job) {
792 DCHECK_EQ(MessageLoop::current(), sync_loop_);
793 if (!ShouldRunJob(job)) {
794 SLOG(WARNING)
795 << "Not executing "
796 << SyncSessionJob::GetPurposeString(job.purpose) << " job from "
797 << GetUpdatesSourceString(job.session->source().updates_source);
798 return;
799 }
800
801 if (job.purpose == SyncSessionJob::NUDGE) {
802 if (pending_nudge_.get() == NULL ||
803 pending_nudge_->session != job.session) {
804 SDVLOG(2) << "Dropping a nudge in "
805 << "DoSyncSessionJob because another nudge was scheduled";
806 return; // Another nudge must have been scheduled in in the meantime.
807 }
808 pending_nudge_.reset();
809
810 // Create the session with the latest model safe table and use it to purge
811 // and update any disabled or modified entries in the job.
812 scoped_ptr<SyncSession> session(CreateSyncSession(job.session->source()));
813
814 job.session->RebaseRoutingInfoWithLatest(*session);
815 }
816 SDVLOG(2) << "DoSyncSessionJob with "
817 << SyncSessionJob::GetPurposeString(job.purpose) << " job";
818
819 SyncerStep begin(SYNCER_END);
820 SyncerStep end(SYNCER_END);
821 SetSyncerStepsForPurpose(job.purpose, &begin, &end);
822
823 bool has_more_to_sync = true;
824 while (ShouldRunJob(job) && has_more_to_sync) {
825 SDVLOG(2) << "Calling SyncShare.";
826 // Synchronously perform the sync session from this thread.
827 syncer_->SyncShare(job.session.get(), begin, end);
828 has_more_to_sync = job.session->HasMoreToSync();
829 if (has_more_to_sync)
830 job.session->PrepareForAnotherSyncCycle();
831 }
832 SDVLOG(2) << "Done SyncShare looping.";
833
834 FinishSyncSessionJob(job);
835 }
836
837 void SyncScheduler::UpdateCarryoverSessionState(
838 const SyncSessionJob& old_job) {
839 DCHECK_EQ(MessageLoop::current(), sync_loop_);
840 if (old_job.purpose == SyncSessionJob::CONFIGURATION) {
841 // Whatever types were part of a configuration task will have had updates
842 // downloaded. For that reason, we make sure they get recorded in the
843 // event that they get disabled at a later time.
844 ModelSafeRoutingInfo r(session_context_->previous_session_routing_info());
845 if (!r.empty()) {
846 ModelSafeRoutingInfo temp_r;
847 ModelSafeRoutingInfo old_info(old_job.session->routing_info());
848 std::set_union(r.begin(), r.end(), old_info.begin(), old_info.end(),
849 std::insert_iterator<ModelSafeRoutingInfo>(temp_r, temp_r.begin()));
850 session_context_->set_previous_session_routing_info(temp_r);
851 }
852 } else {
853 session_context_->set_previous_session_routing_info(
854 old_job.session->routing_info());
855 }
856 }
857
858 void SyncScheduler::FinishSyncSessionJob(const SyncSessionJob& job) {
859 DCHECK_EQ(MessageLoop::current(), sync_loop_);
860 // Update timing information for how often datatypes are triggering nudges.
861 base::TimeTicks now = TimeTicks::Now();
862 if (!last_sync_session_end_time_.is_null()) {
863 ModelTypePayloadMap::const_iterator iter;
864 for (iter = job.session->source().types.begin();
865 iter != job.session->source().types.end();
866 ++iter) {
867 #define PER_DATA_TYPE_MACRO(type_str) \
868 SYNC_FREQ_HISTOGRAM("Sync.Freq" type_str, \
869 now - last_sync_session_end_time_);
870 SYNC_DATA_TYPE_HISTOGRAM(iter->first);
871 #undef PER_DATA_TYPE_MACRO
872 }
873 }
874 last_sync_session_end_time_ = now;
875
876 // Now update the status of the connection from SCM. We need this
877 // to decide whether we need to save/run future jobs. The notifications
878 // from SCM are not reliable.
879 // TODO(rlarocque): crbug.com/110954
880 // We should get rid of the notifications and
881 // it is probably not needed to maintain this status variable
882 // in 2 places. We should query it directly from SCM when needed.
883 // But that would need little more refactoring(including a method to
884 // query if the auth token is invalid) from SCM side.
885 ServerConnectionManager* scm = session_context_->connection_manager();
886 UpdateServerConnectionManagerStatus(scm->server_status());
887
888 UpdateCarryoverSessionState(job);
889 if (IsSyncingCurrentlySilenced()) {
890 SDVLOG(2) << "We are currently throttled; not scheduling the next sync.";
891 // TODO(sync): Investigate whether we need to check job.purpose
892 // here; see DCHECKs in SaveJob(). (See http://crbug.com/90868.)
893 SaveJob(job);
894 return; // Nothing to do.
895 }
896
897 SDVLOG(2) << "Updating the next polling time after SyncMain";
898 ScheduleNextSync(job);
899 }
900
901 void SyncScheduler::ScheduleNextSync(const SyncSessionJob& old_job) {
902 DCHECK_EQ(MessageLoop::current(), sync_loop_);
903 DCHECK(!old_job.session->HasMoreToSync());
904
905 AdjustPolling(&old_job);
906
907 if (old_job.session->Succeeded()) {
908 // Success implies backoff relief. Note that if this was a
909 // "one-off" job (i.e. purpose ==
910 // SyncSessionJob::{CLEAR_USER_DATA,CLEANUP_DISABLED_TYPES}), if
911 // there was work to do before it ran this wont have changed, as
912 // jobs like this don't run a full sync cycle. So we don't need
913 // special code here.
914 wait_interval_.reset();
915 SDVLOG(2) << "Job succeeded so not scheduling more jobs";
916 return;
917 }
918
919 if (old_job.purpose == SyncSessionJob::POLL) {
920 return; // We don't retry POLL jobs.
921 }
922
923 // TODO(rlarocque): There's no reason why we should blindly backoff and retry
924 // if we don't succeed. Some types of errors are not likely to disappear on
925 // their own. With the return values now available in the old_job.session, we
926 // should be able to detect such errors and only retry when we detect
927 // transient errors.
928
929 if (IsBackingOff() && wait_interval_->timer.IsRunning() &&
930 mode_ == NORMAL_MODE) {
931 // When in normal mode, we allow up to one nudge per backoff interval. It
932 // appears that this was our nudge for this interval, and it failed.
933 //
934 // Note: This does not prevent us from running canary jobs. For example, an
935 // IP address change might still result in another nudge being executed
936 // during this backoff interval.
937 SDVLOG(2) << "A nudge during backoff failed";
938
939 DCHECK_EQ(SyncSessionJob::NUDGE, old_job.purpose);
940 DCHECK(!wait_interval_->had_nudge);
941
942 wait_interval_->had_nudge = true;
943 InitOrCoalescePendingJob(old_job);
944 RestartWaiting();
945 } else {
946 // Either this is the first failure or a consecutive failure after our
947 // backoff timer expired. We handle it the same way in either case.
948 SDVLOG(2) << "Non-'backoff nudge' SyncShare job failed";
949 HandleContinuationError(old_job);
950 }
951 }
952
953 void SyncScheduler::AdjustPolling(const SyncSessionJob* old_job) {
954 DCHECK_EQ(MessageLoop::current(), sync_loop_);
955
956 TimeDelta poll = (!session_context_->notifications_enabled()) ?
957 syncer_short_poll_interval_seconds_ :
958 syncer_long_poll_interval_seconds_;
959 bool rate_changed = !poll_timer_.IsRunning() ||
960 poll != poll_timer_.GetCurrentDelay();
961
962 if (old_job && old_job->purpose != SyncSessionJob::POLL && !rate_changed)
963 poll_timer_.Reset();
964
965 if (!rate_changed)
966 return;
967
968 // Adjust poll rate.
969 poll_timer_.Stop();
970 poll_timer_.Start(FROM_HERE, poll, this, &SyncScheduler::PollTimerCallback);
971 }
972
973 void SyncScheduler::RestartWaiting() {
974 CHECK(wait_interval_.get());
975 wait_interval_->timer.Stop();
976 wait_interval_->timer.Start(FROM_HERE, wait_interval_->length,
977 this, &SyncScheduler::DoCanaryJob);
978 }
979
980 void SyncScheduler::HandleContinuationError(
981 const SyncSessionJob& old_job) {
982 DCHECK_EQ(MessageLoop::current(), sync_loop_);
983 if (DCHECK_IS_ON()) {
984 if (IsBackingOff()) {
985 DCHECK(wait_interval_->timer.IsRunning() || old_job.is_canary_job);
986 }
987 }
988
989 TimeDelta length = delay_provider_->GetDelay(
990 IsBackingOff() ? wait_interval_->length : TimeDelta::FromSeconds(1));
991
992 SDVLOG(2) << "In handle continuation error with "
993 << SyncSessionJob::GetPurposeString(old_job.purpose)
994 << " job. The time delta(ms) is "
995 << length.InMilliseconds();
996
997 // This will reset the had_nudge variable as well.
998 wait_interval_.reset(new WaitInterval(WaitInterval::EXPONENTIAL_BACKOFF,
999 length));
1000 if (old_job.purpose == SyncSessionJob::CONFIGURATION) {
1001 SyncSession* old = old_job.session.get();
1002 SyncSession* s(new SyncSession(session_context_.get(), this,
1003 old->source(), old->routing_info(), old->workers()));
1004 SyncSessionJob job(old_job.purpose, TimeTicks::Now() + length,
1005 make_linked_ptr(s), false, FROM_HERE);
1006 wait_interval_->pending_configure_job.reset(new SyncSessionJob(job));
1007 } else {
1008 // We are not in configuration mode. So wait_interval's pending job
1009 // should be null.
1010 DCHECK(wait_interval_->pending_configure_job.get() == NULL);
1011
1012 // TODO(lipalani) - handle clear user data.
1013 InitOrCoalescePendingJob(old_job);
1014 }
1015 RestartWaiting();
1016 }
1017
1018 // static
1019 TimeDelta SyncScheduler::GetRecommendedDelay(const TimeDelta& last_delay) {
1020 if (last_delay.InSeconds() >= kMaxBackoffSeconds)
1021 return TimeDelta::FromSeconds(kMaxBackoffSeconds);
1022
1023 // This calculates approx. base_delay_seconds * 2 +/- base_delay_seconds / 2
1024 int64 backoff_s =
1025 std::max(static_cast<int64>(1),
1026 last_delay.InSeconds() * kBackoffRandomizationFactor);
1027
1028 // Flip a coin to randomize backoff interval by +/- 50%.
1029 int rand_sign = base::RandInt(0, 1) * 2 - 1;
1030
1031 // Truncation is adequate for rounding here.
1032 backoff_s = backoff_s +
1033 (rand_sign * (last_delay.InSeconds() / kBackoffRandomizationFactor));
1034
1035 // Cap the backoff interval.
1036 backoff_s = std::max(static_cast<int64>(1),
1037 std::min(backoff_s, kMaxBackoffSeconds));
1038
1039 return TimeDelta::FromSeconds(backoff_s);
1040 }
1041
1042 void SyncScheduler::RequestStop(const base::Closure& callback) {
1043 syncer_->RequestEarlyExit(); // Safe to call from any thread.
1044 DCHECK(weak_handle_this_.IsInitialized());
1045 SDVLOG(3) << "Posting StopImpl";
1046 weak_handle_this_.Call(FROM_HERE,
1047 &SyncScheduler::StopImpl,
1048 callback);
1049 }
1050
1051 void SyncScheduler::StopImpl(const base::Closure& callback) {
1052 DCHECK_EQ(MessageLoop::current(), sync_loop_);
1053 SDVLOG(2) << "StopImpl called";
1054
1055 // Kill any in-flight method calls.
1056 weak_ptr_factory_.InvalidateWeakPtrs();
1057 wait_interval_.reset();
1058 poll_timer_.Stop();
1059 if (started_) {
1060 started_ = false;
1061 }
1062 if (!callback.is_null())
1063 callback.Run();
1064 }
1065
1066 void SyncScheduler::DoCanaryJob() {
1067 DCHECK_EQ(MessageLoop::current(), sync_loop_);
1068 SDVLOG(2) << "Do canary job";
1069 DoPendingJobIfPossible(true);
1070 }
1071
1072 void SyncScheduler::DoPendingJobIfPossible(bool is_canary_job) {
1073 DCHECK_EQ(MessageLoop::current(), sync_loop_);
1074 SyncSessionJob* job_to_execute = NULL;
1075 if (mode_ == CONFIGURATION_MODE && wait_interval_.get()
1076 && wait_interval_->pending_configure_job.get()) {
1077 SDVLOG(2) << "Found pending configure job";
1078 job_to_execute = wait_interval_->pending_configure_job.get();
1079 } else if (mode_ == NORMAL_MODE && pending_nudge_.get()) {
1080 SDVLOG(2) << "Found pending nudge job";
1081 // Pending jobs mostly have time from the past. Reset it so this job
1082 // will get executed.
1083 if (pending_nudge_->scheduled_start < TimeTicks::Now())
1084 pending_nudge_->scheduled_start = TimeTicks::Now();
1085
1086 scoped_ptr<SyncSession> session(CreateSyncSession(
1087 pending_nudge_->session->source()));
1088
1089 // Also the routing info might have been changed since we cached the
1090 // pending nudge. Update it by coalescing to the latest.
1091 pending_nudge_->session->Coalesce(*(session.get()));
1092 // The pending nudge would be cleared in the DoSyncSessionJob function.
1093 job_to_execute = pending_nudge_.get();
1094 }
1095
1096 if (job_to_execute != NULL) {
1097 SDVLOG(2) << "Executing pending job";
1098 SyncSessionJob copy = *job_to_execute;
1099 copy.is_canary_job = is_canary_job;
1100 DoSyncSessionJob(copy);
1101 }
1102 }
1103
1104 SyncSession* SyncScheduler::CreateSyncSession(const SyncSourceInfo& source) {
1105 DCHECK_EQ(MessageLoop::current(), sync_loop_);
1106 ModelSafeRoutingInfo routes;
1107 std::vector<ModelSafeWorker*> workers;
1108 session_context_->registrar()->GetModelSafeRoutingInfo(&routes);
1109 DVLOG(2) << "Creating sync session with routes "
1110 << ModelSafeRoutingInfoToString(routes);
1111 session_context_->registrar()->GetWorkers(&workers);
1112 SyncSourceInfo info(source);
1113
1114 SyncSession* session(new SyncSession(session_context_.get(), this, info,
1115 routes, workers));
1116
1117 return session;
1118 }
1119
1120 void SyncScheduler::PollTimerCallback() {
1121 DCHECK_EQ(MessageLoop::current(), sync_loop_);
1122 ModelSafeRoutingInfo r;
1123 ModelTypePayloadMap types_with_payloads =
1124 syncable::ModelTypePayloadMapFromRoutingInfo(r, std::string());
1125 SyncSourceInfo info(GetUpdatesCallerInfo::PERIODIC, types_with_payloads);
1126 SyncSession* s = CreateSyncSession(info);
1127
1128 SyncSessionJob job(SyncSessionJob::POLL, TimeTicks::Now(),
1129 make_linked_ptr(s),
1130 false,
1131 FROM_HERE);
1132
1133 ScheduleSyncSessionJob(job);
1134 }
1135
1136 void SyncScheduler::Unthrottle() {
1137 DCHECK_EQ(MessageLoop::current(), sync_loop_);
1138 DCHECK_EQ(WaitInterval::THROTTLED, wait_interval_->mode);
1139 SDVLOG(2) << "Unthrottled.";
1140 DoCanaryJob();
1141 wait_interval_.reset();
1142 }
1143
1144 void SyncScheduler::Notify(SyncEngineEvent::EventCause cause) {
1145 DCHECK_EQ(MessageLoop::current(), sync_loop_);
1146 session_context_->NotifyListeners(SyncEngineEvent(cause));
1147 }
1148
1149 bool SyncScheduler::IsBackingOff() const {
1150 DCHECK_EQ(MessageLoop::current(), sync_loop_);
1151 return wait_interval_.get() && wait_interval_->mode ==
1152 WaitInterval::EXPONENTIAL_BACKOFF;
1153 }
1154
1155 void SyncScheduler::OnSilencedUntil(const base::TimeTicks& silenced_until) {
1156 DCHECK_EQ(MessageLoop::current(), sync_loop_);
1157 wait_interval_.reset(new WaitInterval(WaitInterval::THROTTLED,
1158 silenced_until - TimeTicks::Now()));
1159 wait_interval_->timer.Start(FROM_HERE, wait_interval_->length, this,
1160 &SyncScheduler::Unthrottle);
1161 }
1162
1163 bool SyncScheduler::IsSyncingCurrentlySilenced() {
1164 DCHECK_EQ(MessageLoop::current(), sync_loop_);
1165 return wait_interval_.get() && wait_interval_->mode ==
1166 WaitInterval::THROTTLED;
1167 }
1168
1169 void SyncScheduler::OnReceivedShortPollIntervalUpdate(
1170 const base::TimeDelta& new_interval) {
1171 DCHECK_EQ(MessageLoop::current(), sync_loop_);
1172 syncer_short_poll_interval_seconds_ = new_interval;
1173 }
1174
1175 void SyncScheduler::OnReceivedLongPollIntervalUpdate(
1176 const base::TimeDelta& new_interval) {
1177 DCHECK_EQ(MessageLoop::current(), sync_loop_);
1178 syncer_long_poll_interval_seconds_ = new_interval;
1179 }
1180
1181 void SyncScheduler::OnReceivedSessionsCommitDelay(
1182 const base::TimeDelta& new_delay) {
1183 DCHECK_EQ(MessageLoop::current(), sync_loop_);
1184 sessions_commit_delay_ = new_delay;
1185 }
1186
1187 void SyncScheduler::OnShouldStopSyncingPermanently() {
1188 DCHECK_EQ(MessageLoop::current(), sync_loop_);
1189 SDVLOG(2) << "OnShouldStopSyncingPermanently";
1190 syncer_->RequestEarlyExit(); // Thread-safe.
1191 Notify(SyncEngineEvent::STOP_SYNCING_PERMANENTLY);
1192 }
1193
1194 void SyncScheduler::OnActionableError(
1195 const sessions::SyncSessionSnapshot& snap) {
1196 DCHECK_EQ(MessageLoop::current(), sync_loop_);
1197 SDVLOG(2) << "OnActionableError";
1198 SyncEngineEvent event(SyncEngineEvent::ACTIONABLE_ERROR);
1199 sessions::SyncSessionSnapshot snapshot(snap);
1200 event.snapshot = &snapshot;
1201 session_context_->NotifyListeners(event);
1202 }
1203
1204 void SyncScheduler::OnSyncProtocolError(
1205 const sessions::SyncSessionSnapshot& snapshot) {
1206 DCHECK_EQ(MessageLoop::current(), sync_loop_);
1207 if (ShouldRequestEarlyExit(snapshot.errors.sync_protocol_error)) {
1208 SDVLOG(2) << "Sync Scheduler requesting early exit.";
1209 syncer_->RequestEarlyExit(); // Thread-safe.
1210 }
1211 if (IsActionableError(snapshot.errors.sync_protocol_error))
1212 OnActionableError(snapshot);
1213 }
1214
1215 void SyncScheduler::set_notifications_enabled(bool notifications_enabled) {
1216 DCHECK_EQ(MessageLoop::current(), sync_loop_);
1217 session_context_->set_notifications_enabled(notifications_enabled);
1218 }
1219
1220 base::TimeDelta SyncScheduler::sessions_commit_delay() const {
1221 DCHECK_EQ(MessageLoop::current(), sync_loop_);
1222 return sessions_commit_delay_;
1223 }
1224
1225 #undef SDVLOG_LOC
1226
1227 #undef SDVLOG
1228
1229 #undef SLOG
1230
1231 #undef ENUM_CASE
1232
1233 } // browser_sync
OLDNEW
« no previous file with comments | « chrome/browser/sync/engine/sync_scheduler.h ('k') | chrome/browser/sync/engine/sync_scheduler_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698