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

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

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

Powered by Google App Engine
This is Rietveld 408576698