| 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 |