OLD | NEW |
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 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 #include "chrome/browser/sync/engine/syncer_thread.h" | 4 #include "chrome/browser/sync/engine/syncer_thread.h" |
5 | 5 |
6 #include "build/build_config.h" | 6 #include "build/build_config.h" |
7 | 7 |
8 #if defined(OS_MACOSX) | 8 #if defined(OS_MACOSX) |
9 #include <CoreFoundation/CFNumber.h> | 9 #include <CoreFoundation/CFNumber.h> |
10 #include <IOKit/IOTypes.h> | 10 #include <IOKit/IOTypes.h> |
11 #include <IOKit/IOKitLib.h> | 11 #include <IOKit/IOKitLib.h> |
12 #endif | 12 #endif |
13 | 13 |
14 #include <algorithm> | 14 #include <algorithm> |
15 #include <map> | 15 #include <map> |
16 #include <queue> | 16 #include <queue> |
17 | 17 |
| 18 #include "base/rand_util.h" |
18 #include "base/third_party/dynamic_annotations/dynamic_annotations.h" | 19 #include "base/third_party/dynamic_annotations/dynamic_annotations.h" |
19 #include "chrome/browser/sync/engine/auth_watcher.h" | 20 #include "chrome/browser/sync/engine/auth_watcher.h" |
20 #include "chrome/browser/sync/engine/model_safe_worker.h" | 21 #include "chrome/browser/sync/engine/model_safe_worker.h" |
21 #include "chrome/browser/sync/engine/net/server_connection_manager.h" | 22 #include "chrome/browser/sync/engine/net/server_connection_manager.h" |
22 #include "chrome/browser/sync/engine/syncer.h" | 23 #include "chrome/browser/sync/engine/syncer.h" |
| 24 #include "chrome/browser/sync/sessions/session_state.h" |
23 #include "chrome/browser/sync/syncable/directory_manager.h" | 25 #include "chrome/browser/sync/syncable/directory_manager.h" |
24 #include "chrome/common/chrome_switches.h" | 26 #include "chrome/common/chrome_switches.h" |
25 #include "jingle/notifier/listener/notification_constants.h" | 27 #include "jingle/notifier/listener/notification_constants.h" |
26 | 28 |
27 using std::priority_queue; | 29 using std::priority_queue; |
28 using std::min; | 30 using std::min; |
29 using base::Time; | 31 using base::Time; |
30 using base::TimeDelta; | 32 using base::TimeDelta; |
31 using base::TimeTicks; | 33 using base::TimeTicks; |
32 | 34 |
| 35 namespace browser_sync { |
33 | 36 |
34 namespace browser_sync { | 37 using sessions::SyncSessionSnapshot; |
35 | 38 |
36 // We use high values here to ensure that failure to receive poll updates from | 39 // We use high values here to ensure that failure to receive poll updates from |
37 // the server doesn't result in rapid-fire polling from the client due to low | 40 // the server doesn't result in rapid-fire polling from the client due to low |
38 // local limits. | 41 // local limits. |
39 const int SyncerThread::kDefaultShortPollIntervalSeconds = 3600 * 8; | 42 const int SyncerThread::kDefaultShortPollIntervalSeconds = 3600 * 8; |
40 const int SyncerThread::kDefaultLongPollIntervalSeconds = 3600 * 12; | 43 const int SyncerThread::kDefaultLongPollIntervalSeconds = 3600 * 12; |
41 | 44 |
42 // TODO(tim): This is used to regulate the short poll (when notifications are | 45 // TODO(tim): This is used to regulate the short poll (when notifications are |
43 // disabled) based on user idle time. If it is set to a smaller value than | 46 // disabled) based on user idle time. If it is set to a smaller value than |
44 // the short poll interval, it basically does nothing; for now, this is what | 47 // the short poll interval, it basically does nothing; for now, this is what |
45 // we want and allows stronger control over the poll rate from the server. We | 48 // we want and allows stronger control over the poll rate from the server. We |
46 // should probably re-visit this code later and figure out if user idle time | 49 // should probably re-visit this code later and figure out if user idle time |
47 // is really something we want and make sure it works, if it is. | 50 // is really something we want and make sure it works, if it is. |
48 const int SyncerThread::kDefaultMaxPollIntervalMs = 30 * 60 * 1000; | 51 const int SyncerThread::kDefaultMaxPollIntervalMs = 30 * 60 * 1000; |
49 | 52 |
| 53 // Backoff interval randomization factor. |
| 54 static const int kBackoffRandomizationFactor = 2; |
| 55 |
| 56 const int SyncerThread::kMaxBackoffSeconds = 60 * 60 * 4; // 4 hours. |
| 57 |
50 void SyncerThread::NudgeSyncer(int milliseconds_from_now, NudgeSource source) { | 58 void SyncerThread::NudgeSyncer(int milliseconds_from_now, NudgeSource source) { |
51 AutoLock lock(lock_); | 59 AutoLock lock(lock_); |
52 if (vault_.syncer_ == NULL) { | 60 if (vault_.syncer_ == NULL) { |
53 return; | 61 return; |
54 } | 62 } |
55 | 63 |
56 NudgeSyncImpl(milliseconds_from_now, source); | 64 NudgeSyncImpl(milliseconds_from_now, source); |
57 } | 65 } |
58 | 66 |
59 SyncerThread::SyncerThread(sessions::SyncSessionContext* context, | 67 SyncerThread::SyncerThread(sessions::SyncSessionContext* context) |
60 AllStatus* all_status) | |
61 : thread_main_started_(false, false), | 68 : thread_main_started_(false, false), |
62 thread_("SyncEngine_SyncerThread"), | 69 thread_("SyncEngine_SyncerThread"), |
63 vault_field_changed_(&lock_), | 70 vault_field_changed_(&lock_), |
64 p2p_authenticated_(false), | 71 p2p_authenticated_(false), |
65 p2p_subscribed_(false), | 72 p2p_subscribed_(false), |
66 conn_mgr_hookup_(NULL), | 73 conn_mgr_hookup_(NULL), |
67 allstatus_(all_status), | |
68 syncer_short_poll_interval_seconds_(kDefaultShortPollIntervalSeconds), | 74 syncer_short_poll_interval_seconds_(kDefaultShortPollIntervalSeconds), |
69 syncer_long_poll_interval_seconds_(kDefaultLongPollIntervalSeconds), | 75 syncer_long_poll_interval_seconds_(kDefaultLongPollIntervalSeconds), |
70 syncer_polling_interval_(kDefaultShortPollIntervalSeconds), | 76 syncer_polling_interval_(kDefaultShortPollIntervalSeconds), |
71 syncer_max_interval_(kDefaultMaxPollIntervalMs), | 77 syncer_max_interval_(kDefaultMaxPollIntervalMs), |
72 directory_manager_hookup_(NULL), | 78 directory_manager_hookup_(NULL), |
73 syncer_events_(NULL), | 79 syncer_events_(NULL), |
74 session_context_(context), | 80 session_context_(context), |
75 disable_idle_detection_(false) { | 81 disable_idle_detection_(false) { |
76 DCHECK(context); | 82 DCHECK(context); |
77 syncer_event_relay_channel_.reset(new SyncerEventChannel()); | 83 syncer_event_relay_channel_.reset(new SyncerEventChannel()); |
(...skipping 251 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
329 // event. This will also update the source of the following SyncMain call. | 335 // event. This will also update the source of the following SyncMain call. |
330 bool nudged = UpdateNudgeSource(throttled, continue_sync_cycle, | 336 bool nudged = UpdateNudgeSource(throttled, continue_sync_cycle, |
331 &initial_sync_for_thread); | 337 &initial_sync_for_thread); |
332 | 338 |
333 LOG(INFO) << "Calling Sync Main at time " << Time::Now().ToInternalValue(); | 339 LOG(INFO) << "Calling Sync Main at time " << Time::Now().ToInternalValue(); |
334 SyncMain(vault_.syncer_); | 340 SyncMain(vault_.syncer_); |
335 last_sync_time = TimeTicks::Now(); | 341 last_sync_time = TimeTicks::Now(); |
336 | 342 |
337 LOG(INFO) << "Updating the next polling time after SyncMain"; | 343 LOG(INFO) << "Updating the next polling time after SyncMain"; |
338 vault_.current_wait_interval_ = CalculatePollingWaitTime( | 344 vault_.current_wait_interval_ = CalculatePollingWaitTime( |
339 allstatus_->status(), | |
340 static_cast<int>(vault_.current_wait_interval_.poll_delta.InSeconds()), | 345 static_cast<int>(vault_.current_wait_interval_.poll_delta.InSeconds()), |
341 &user_idle_milliseconds, &continue_sync_cycle, nudged); | 346 &user_idle_milliseconds, &continue_sync_cycle, nudged); |
342 } | 347 } |
343 #if defined(OS_LINUX) | 348 #if defined(OS_LINUX) |
344 idle_query_.reset(); | 349 idle_query_.reset(); |
345 #endif | 350 #endif |
346 } | 351 } |
347 | 352 |
348 void SyncerThread::WaitUntilConnectedOrQuit() { | 353 void SyncerThread::WaitUntilConnectedOrQuit() { |
349 LOG(INFO) << "Syncer thread waiting for connection."; | 354 LOG(INFO) << "Syncer thread waiting for connection."; |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
408 void SyncerThread::ExitPausedState() { | 413 void SyncerThread::ExitPausedState() { |
409 lock_.AssertAcquired(); | 414 lock_.AssertAcquired(); |
410 vault_.paused_ = false; | 415 vault_.paused_ = false; |
411 vault_field_changed_.Broadcast(); | 416 vault_field_changed_.Broadcast(); |
412 SyncerEvent event(SyncerEvent::RESUMED); | 417 SyncerEvent event(SyncerEvent::RESUMED); |
413 relay_channel()->Notify(event); | 418 relay_channel()->Notify(event); |
414 } | 419 } |
415 | 420 |
416 // We check how long the user's been idle and sync less often if the machine is | 421 // We check how long the user's been idle and sync less often if the machine is |
417 // not in use. The aim is to reduce server load. | 422 // not in use. The aim is to reduce server load. |
418 // TODO(timsteele): Should use Time(Delta). | |
419 SyncerThread::WaitInterval SyncerThread::CalculatePollingWaitTime( | 423 SyncerThread::WaitInterval SyncerThread::CalculatePollingWaitTime( |
420 const AllStatus::Status& status, | |
421 int last_poll_wait, // Time in seconds. | 424 int last_poll_wait, // Time in seconds. |
422 int* user_idle_milliseconds, | 425 int* user_idle_milliseconds, |
423 bool* continue_sync_cycle, | 426 bool* continue_sync_cycle, |
424 bool was_nudged) { | 427 bool was_nudged) { |
425 lock_.AssertAcquired(); // We access 'vault' in here, so we need the lock. | 428 lock_.AssertAcquired(); // We access 'vault' in here, so we need the lock. |
426 WaitInterval return_interval; | 429 WaitInterval return_interval; |
427 | 430 |
428 // Server initiated throttling trumps everything. | 431 // Server initiated throttling trumps everything. |
429 if (!silenced_until_.is_null()) { | 432 if (!silenced_until_.is_null()) { |
430 // We don't need to reset other state, it can continue where it left off. | 433 // We don't need to reset other state, it can continue where it left off. |
431 return_interval.mode = WaitInterval::THROTTLED; | 434 return_interval.mode = WaitInterval::THROTTLED; |
432 return_interval.poll_delta = silenced_until_ - TimeTicks::Now(); | 435 return_interval.poll_delta = silenced_until_ - TimeTicks::Now(); |
433 return return_interval; | 436 return return_interval; |
434 } | 437 } |
435 | 438 |
436 bool is_continuing_sync_cyle = *continue_sync_cycle; | 439 bool is_continuing_sync_cyle = *continue_sync_cycle; |
437 *continue_sync_cycle = false; | 440 *continue_sync_cycle = false; |
438 | 441 |
439 // Determine if the syncer has unfinished work to do from allstatus_. | 442 // Determine if the syncer has unfinished work to do. |
440 const bool syncer_has_work_to_do = | 443 SyncSessionSnapshot* snapshot = session_context_->previous_session_snapshot(); |
441 status.updates_available > status.updates_received | 444 const bool syncer_has_work_to_do = snapshot && |
442 || status.unsynced_count > 0; | 445 (snapshot->num_server_changes_remaining > snapshot->max_local_timestamp |
| 446 || snapshot->unsynced_count > 0); |
443 LOG(INFO) << "syncer_has_work_to_do is " << syncer_has_work_to_do; | 447 LOG(INFO) << "syncer_has_work_to_do is " << syncer_has_work_to_do; |
444 | 448 |
445 // First calculate the expected wait time, figuring in any backoff because of | 449 // First calculate the expected wait time, figuring in any backoff because of |
446 // user idle time. next_wait is in seconds | 450 // user idle time. next_wait is in seconds |
447 syncer_polling_interval_ = (!status.notifications_enabled) ? | 451 syncer_polling_interval_ = (!session_context_->notifications_enabled()) ? |
448 syncer_short_poll_interval_seconds_ : | 452 syncer_short_poll_interval_seconds_ : |
449 syncer_long_poll_interval_seconds_; | 453 syncer_long_poll_interval_seconds_; |
450 int default_next_wait = syncer_polling_interval_; | 454 int default_next_wait = syncer_polling_interval_; |
451 return_interval.poll_delta = TimeDelta::FromSeconds(default_next_wait); | 455 return_interval.poll_delta = TimeDelta::FromSeconds(default_next_wait); |
452 | 456 |
453 if (syncer_has_work_to_do) { | 457 if (syncer_has_work_to_do) { |
454 // Provide exponential backoff due to consecutive errors, else attempt to | 458 // Provide exponential backoff due to consecutive errors, else attempt to |
455 // complete the work as soon as possible. | 459 // complete the work as soon as possible. |
456 if (is_continuing_sync_cyle) { | 460 if (is_continuing_sync_cyle) { |
457 return_interval.mode = WaitInterval::EXPONENTIAL_BACKOFF; | 461 return_interval.mode = WaitInterval::EXPONENTIAL_BACKOFF; |
458 if (was_nudged && vault_.current_wait_interval_.mode == | 462 if (was_nudged && vault_.current_wait_interval_.mode == |
459 WaitInterval::EXPONENTIAL_BACKOFF) { | 463 WaitInterval::EXPONENTIAL_BACKOFF) { |
460 // We were nudged, it failed, and we were already in backoff. | 464 // We were nudged, it failed, and we were already in backoff. |
461 return_interval.had_nudge_during_backoff = true; | 465 return_interval.had_nudge_during_backoff = true; |
462 // Keep exponent for exponential backoff the same in this case. | 466 // Keep exponent for exponential backoff the same in this case. |
463 return_interval.poll_delta = vault_.current_wait_interval_.poll_delta; | 467 return_interval.poll_delta = vault_.current_wait_interval_.poll_delta; |
464 } else { | 468 } else { |
465 // We weren't nudged, or we were in a NORMAL wait interval until now. | 469 // We weren't nudged, or we were in a NORMAL wait interval until now. |
466 return_interval.poll_delta = TimeDelta::FromSeconds( | 470 return_interval.poll_delta = TimeDelta::FromSeconds( |
467 AllStatus::GetRecommendedDelaySeconds(last_poll_wait)); | 471 GetRecommendedDelaySeconds(last_poll_wait)); |
468 } | 472 } |
469 } else { | 473 } else { |
470 // No consecutive error. | 474 // No consecutive error. |
471 return_interval.poll_delta = TimeDelta::FromSeconds( | 475 return_interval.poll_delta = TimeDelta::FromSeconds( |
472 AllStatus::GetRecommendedDelaySeconds(0)); | 476 GetRecommendedDelaySeconds(0)); |
473 } | 477 } |
474 *continue_sync_cycle = true; | 478 *continue_sync_cycle = true; |
475 } else if (!status.notifications_enabled) { | 479 } else if (!session_context_->notifications_enabled()) { |
476 // Ensure that we start exponential backoff from our base polling | 480 // Ensure that we start exponential backoff from our base polling |
477 // interval when we are not continuing a sync cycle. | 481 // interval when we are not continuing a sync cycle. |
478 last_poll_wait = std::max(last_poll_wait, syncer_polling_interval_); | 482 last_poll_wait = std::max(last_poll_wait, syncer_polling_interval_); |
479 | 483 |
480 // Did the user start interacting with the computer again? | 484 // Did the user start interacting with the computer again? |
481 // If so, revise our idle time (and probably next_sync_time) downwards | 485 // If so, revise our idle time (and probably next_sync_time) downwards |
482 int new_idle_time = disable_idle_detection_ ? 0 : UserIdleTime(); | 486 int new_idle_time = disable_idle_detection_ ? 0 : UserIdleTime(); |
483 if (new_idle_time < *user_idle_milliseconds) { | 487 if (new_idle_time < *user_idle_milliseconds) { |
484 *user_idle_milliseconds = new_idle_time; | 488 *user_idle_milliseconds = new_idle_time; |
485 } | 489 } |
(...skipping 157 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
643 AutoLock lock(lock_); | 647 AutoLock lock(lock_); |
644 CheckConnected(&vault_.connected_, event.connection_code, | 648 CheckConnected(&vault_.connected_, event.connection_code, |
645 &vault_field_changed_); | 649 &vault_field_changed_); |
646 } | 650 } |
647 } | 651 } |
648 | 652 |
649 SyncerEventChannel* SyncerThread::relay_channel() { | 653 SyncerEventChannel* SyncerThread::relay_channel() { |
650 return syncer_event_relay_channel_.get(); | 654 return syncer_event_relay_channel_.get(); |
651 } | 655 } |
652 | 656 |
| 657 int SyncerThread::GetRecommendedDelaySeconds(int base_delay_seconds) { |
| 658 if (base_delay_seconds >= kMaxBackoffSeconds) |
| 659 return kMaxBackoffSeconds; |
| 660 |
| 661 // This calculates approx. base_delay_seconds * 2 +/- base_delay_seconds / 2 |
| 662 int backoff_s = |
| 663 std::max(1, base_delay_seconds * kBackoffRandomizationFactor); |
| 664 |
| 665 // Flip a coin to randomize backoff interval by +/- 50%. |
| 666 int rand_sign = base::RandInt(0, 1) * 2 - 1; |
| 667 |
| 668 // Truncation is adequate for rounding here. |
| 669 backoff_s = backoff_s + |
| 670 (rand_sign * (base_delay_seconds / kBackoffRandomizationFactor)); |
| 671 |
| 672 // Cap the backoff interval. |
| 673 backoff_s = std::max(1, std::min(backoff_s, kMaxBackoffSeconds)); |
| 674 |
| 675 return backoff_s; |
| 676 } |
| 677 |
653 // Inputs and return value in milliseconds. | 678 // Inputs and return value in milliseconds. |
654 int SyncerThread::CalculateSyncWaitTime(int last_interval, int user_idle_ms) { | 679 int SyncerThread::CalculateSyncWaitTime(int last_interval, int user_idle_ms) { |
655 // syncer_polling_interval_ is in seconds | 680 // syncer_polling_interval_ is in seconds |
656 int syncer_polling_interval_ms = syncer_polling_interval_ * 1000; | 681 int syncer_polling_interval_ms = syncer_polling_interval_ * 1000; |
657 | 682 |
658 // This is our default and lower bound. | 683 // This is our default and lower bound. |
659 int next_wait = syncer_polling_interval_ms; | 684 int next_wait = syncer_polling_interval_ms; |
660 | 685 |
661 // Get idle time, bounded by max wait. | 686 // Get idle time, bounded by max wait. |
662 int idle = min(user_idle_ms, syncer_max_interval_); | 687 int idle = min(user_idle_ms, syncer_max_interval_); |
663 | 688 |
664 // If the user has been idle for a while, we'll start decreasing the poll | 689 // If the user has been idle for a while, we'll start decreasing the poll |
665 // rate. | 690 // rate. |
666 if (idle >= kPollBackoffThresholdMultiplier * syncer_polling_interval_ms) { | 691 if (idle >= kPollBackoffThresholdMultiplier * syncer_polling_interval_ms) { |
667 next_wait = std::min(AllStatus::GetRecommendedDelaySeconds( | 692 next_wait = std::min(GetRecommendedDelaySeconds( |
668 last_interval / 1000), syncer_max_interval_ / 1000) * 1000; | 693 last_interval / 1000), syncer_max_interval_ / 1000) * 1000; |
669 } | 694 } |
670 | 695 |
671 return next_wait; | 696 return next_wait; |
672 } | 697 } |
673 | 698 |
674 // Called with mutex_ already locked. | 699 // Called with mutex_ already locked. |
675 void SyncerThread::NudgeSyncImpl(int milliseconds_from_now, | 700 void SyncerThread::NudgeSyncImpl(int milliseconds_from_now, |
676 NudgeSource source) { | 701 NudgeSource source) { |
677 // TODO(sync): Add the option to reset the backoff state machine. | 702 // TODO(sync): Add the option to reset the backoff state machine. |
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
767 was_logged = true; | 792 was_logged = true; |
768 LOG(INFO) << "UserIdleTime unimplemented on this platform, " | 793 LOG(INFO) << "UserIdleTime unimplemented on this platform, " |
769 "synchronization will not throttle when user idle"; | 794 "synchronization will not throttle when user idle"; |
770 } | 795 } |
771 #endif | 796 #endif |
772 | 797 |
773 return 0; | 798 return 0; |
774 } | 799 } |
775 | 800 |
776 } // namespace browser_sync | 801 } // namespace browser_sync |
OLD | NEW |