Home | History | Annotate | Download | only in engine
      1 // Copyright (c) 2011 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 // A class to run the syncer on a thread.
      6 #ifndef CHROME_BROWSER_SYNC_ENGINE_SYNCER_THREAD_H_
      7 #define CHROME_BROWSER_SYNC_ENGINE_SYNCER_THREAD_H_
      8 #pragma once
      9 
     10 #include "base/callback.h"
     11 #include "base/memory/linked_ptr.h"
     12 #include "base/memory/scoped_ptr.h"
     13 #include "base/observer_list.h"
     14 #include "base/task.h"
     15 #include "base/threading/thread.h"
     16 #include "base/time.h"
     17 #include "base/timer.h"
     18 #include "chrome/browser/sync/engine/nudge_source.h"
     19 #include "chrome/browser/sync/engine/polling_constants.h"
     20 #include "chrome/browser/sync/engine/syncer.h"
     21 #include "chrome/browser/sync/syncable/model_type_payload_map.h"
     22 #include "chrome/browser/sync/engine/net/server_connection_manager.h"
     23 #include "chrome/browser/sync/sessions/sync_session.h"
     24 #include "chrome/browser/sync/sessions/sync_session_context.h"
     25 
     26 namespace browser_sync {
     27 
     28 struct ServerConnectionEvent;
     29 
     30 class SyncerThread : public sessions::SyncSession::Delegate,
     31                      public ServerConnectionEventListener {
     32  public:
     33   enum Mode {
     34     // In this mode, the thread only performs configuration tasks.  This is
     35     // designed to make the case where we want to download updates for a
     36     // specific type only, and not continue syncing until we are moved into
     37     // normal mode.
     38     CONFIGURATION_MODE,
     39     // Resumes polling and allows nudges, drops configuration tasks.  Runs
     40     // through entire sync cycle.
     41     NORMAL_MODE,
     42   };
     43 
     44   // Takes ownership of both |context| and |syncer|.
     45   SyncerThread(sessions::SyncSessionContext* context, Syncer* syncer);
     46   virtual ~SyncerThread();
     47 
     48   typedef Callback0::Type ModeChangeCallback;
     49 
     50   // Change the mode of operation.
     51   // We don't use a lock when changing modes, so we won't cause currently
     52   // scheduled jobs to adhere to the new mode.  We could protect it, but it
     53   // doesn't buy very much as a) a session could already be in progress and it
     54   // will continue no matter what, b) the scheduled sessions already contain
     55   // all their required state and won't be affected by potential change at
     56   // higher levels (i.e. the registrar), and c) we service tasks FIFO, so once
     57   // the mode changes all future jobs will be run against the updated mode.
     58   // If supplied, |callback| will be invoked when the mode has been
     59   // changed to |mode| *from the SyncerThread*, and not from the caller
     60   // thread.
     61   void Start(Mode mode, ModeChangeCallback* callback);
     62 
     63   // Joins on the thread as soon as possible (currently running session
     64   // completes).
     65   void Stop();
     66 
     67   // The meat and potatoes.
     68   void ScheduleNudge(const base::TimeDelta& delay, NudgeSource source,
     69                      const syncable::ModelTypeBitSet& types,
     70                      const tracked_objects::Location& nudge_location);
     71   void ScheduleNudgeWithPayloads(
     72       const base::TimeDelta& delay, NudgeSource source,
     73       const syncable::ModelTypePayloadMap& types_with_payloads,
     74       const tracked_objects::Location& nudge_location);
     75   void ScheduleConfig(const syncable::ModelTypeBitSet& types);
     76   void ScheduleClearUserData();
     77 
     78   // Change status of notifications in the SyncSessionContext.
     79   void set_notifications_enabled(bool notifications_enabled);
     80 
     81   // DDOS avoidance function.  Calculates how long we should wait before trying
     82   // again after a failed sync attempt, where the last delay was |base_delay|.
     83   // TODO(tim): Look at URLRequestThrottlerEntryInterface.
     84   static base::TimeDelta GetRecommendedDelay(const base::TimeDelta& base_delay);
     85 
     86   // SyncSession::Delegate implementation.
     87   virtual void OnSilencedUntil(const base::TimeTicks& silenced_until);
     88   virtual bool IsSyncingCurrentlySilenced();
     89   virtual void OnReceivedShortPollIntervalUpdate(
     90       const base::TimeDelta& new_interval);
     91   virtual void OnReceivedLongPollIntervalUpdate(
     92       const base::TimeDelta& new_interval);
     93   virtual void OnShouldStopSyncingPermanently();
     94 
     95   // ServerConnectionEventListener implementation.
     96   // TODO(tim): schedule a nudge when valid connection detected? in 1 minute?
     97   virtual void OnServerConnectionEvent(const ServerConnectionEvent2& event);
     98 
     99  private:
    100   enum JobProcessDecision {
    101     // Indicates we should continue with the current job.
    102     CONTINUE,
    103     // Indicates that we should save it to be processed later.
    104     SAVE,
    105     // Indicates we should drop this job.
    106     DROP,
    107   };
    108 
    109   struct SyncSessionJob {
    110     // An enum used to describe jobs for scheduling purposes.
    111     enum SyncSessionJobPurpose {
    112       // Our poll timer schedules POLL jobs periodically based on a server
    113       // assigned poll interval.
    114       POLL,
    115       // A nudge task can come from a variety of components needing to force
    116       // a sync.  The source is inferable from |session.source()|.
    117       NUDGE,
    118       // The user invoked a function in the UI to clear their entire account
    119       // and stop syncing (globally).
    120       CLEAR_USER_DATA,
    121       // Typically used for fetching updates for a subset of the enabled types
    122       // during initial sync or reconfiguration.  We don't run all steps of
    123       // the sync cycle for these (e.g. CleanupDisabledTypes is skipped).
    124       CONFIGURATION,
    125     };
    126     SyncSessionJob();
    127     SyncSessionJob(SyncSessionJobPurpose purpose, base::TimeTicks start,
    128         linked_ptr<sessions::SyncSession> session, bool is_canary_job,
    129         const tracked_objects::Location& nudge_location);
    130     ~SyncSessionJob();
    131     SyncSessionJobPurpose purpose;
    132     base::TimeTicks scheduled_start;
    133     linked_ptr<sessions::SyncSession> session;
    134     bool is_canary_job;
    135 
    136     // This is the location the nudge came from. used for debugging purpose.
    137     // In case of multiple nudges getting coalesced this stores the first nudge
    138     // that came in.
    139     tracked_objects::Location nudge_location;
    140   };
    141   friend class SyncerThread2Test;
    142   friend class SyncerThread2WhiteboxTest;
    143 
    144   FRIEND_TEST_ALL_PREFIXES(SyncerThread2WhiteboxTest,
    145       DropNudgeWhileExponentialBackOff);
    146   FRIEND_TEST_ALL_PREFIXES(SyncerThread2WhiteboxTest, SaveNudge);
    147   FRIEND_TEST_ALL_PREFIXES(SyncerThread2WhiteboxTest, ContinueNudge);
    148   FRIEND_TEST_ALL_PREFIXES(SyncerThread2WhiteboxTest, DropPoll);
    149   FRIEND_TEST_ALL_PREFIXES(SyncerThread2WhiteboxTest, ContinuePoll);
    150   FRIEND_TEST_ALL_PREFIXES(SyncerThread2WhiteboxTest, ContinueConfiguration);
    151   FRIEND_TEST_ALL_PREFIXES(SyncerThread2WhiteboxTest,
    152                            SaveConfigurationWhileThrottled);
    153   FRIEND_TEST_ALL_PREFIXES(SyncerThread2WhiteboxTest,
    154                            SaveNudgeWhileThrottled);
    155   FRIEND_TEST_ALL_PREFIXES(SyncerThread2WhiteboxTest,
    156                            ContinueClearUserDataUnderAllCircumstances);
    157   FRIEND_TEST_ALL_PREFIXES(SyncerThread2WhiteboxTest,
    158                            ContinueCanaryJobConfig);
    159   FRIEND_TEST_ALL_PREFIXES(SyncerThread2WhiteboxTest,
    160       ContinueNudgeWhileExponentialBackOff);
    161 
    162   // A component used to get time delays associated with exponential backoff.
    163   // Encapsulated into a class to facilitate testing.
    164   class DelayProvider {
    165    public:
    166     DelayProvider();
    167     virtual base::TimeDelta GetDelay(const base::TimeDelta& last_delay);
    168     virtual ~DelayProvider();
    169    private:
    170     DISALLOW_COPY_AND_ASSIGN(DelayProvider);
    171   };
    172 
    173   struct WaitInterval {
    174     enum Mode {
    175       // A wait interval whose duration has been affected by exponential
    176       // backoff.
    177       // EXPONENTIAL_BACKOFF intervals are nudge-rate limited to 1 per interval.
    178       EXPONENTIAL_BACKOFF,
    179       // A server-initiated throttled interval.  We do not allow any syncing
    180       // during such an interval.
    181       THROTTLED,
    182     };
    183     WaitInterval();
    184     ~WaitInterval();
    185 
    186     Mode mode;
    187 
    188     // This bool is set to true if we have observed a nudge during this
    189     // interval and mode == EXPONENTIAL_BACKOFF.
    190     bool had_nudge;
    191     base::TimeDelta length;
    192     base::OneShotTimer<SyncerThread> timer;
    193 
    194     // Configure jobs are saved only when backing off or throttling. So we
    195     // expose the pointer here.
    196     scoped_ptr<SyncSessionJob> pending_configure_job;
    197     WaitInterval(Mode mode, base::TimeDelta length);
    198   };
    199 
    200   // Helper to assemble a job and post a delayed task to sync.
    201   void ScheduleSyncSessionJob(
    202       const base::TimeDelta& delay,
    203       SyncSessionJob::SyncSessionJobPurpose purpose,
    204       sessions::SyncSession* session,
    205       const tracked_objects::Location& nudge_location);
    206 
    207   // Invoke the Syncer to perform a sync.
    208   void DoSyncSessionJob(const SyncSessionJob& job);
    209 
    210   // Called after the Syncer has performed the sync represented by |job|, to
    211   // reset our state.
    212   void FinishSyncSessionJob(const SyncSessionJob& job);
    213 
    214   // Record important state that might be needed in future syncs, such as which
    215   // data types may require cleanup.
    216   void UpdateCarryoverSessionState(const SyncSessionJob& old_job);
    217 
    218   // Helper to FinishSyncSessionJob to schedule the next sync operation.
    219   void ScheduleNextSync(const SyncSessionJob& old_job);
    220 
    221   // Helper to configure polling intervals. Used by Start and ScheduleNextSync.
    222   void AdjustPolling(const SyncSessionJob* old_job);
    223 
    224   // Helper to ScheduleNextSync in case of consecutive sync errors.
    225   void HandleConsecutiveContinuationError(const SyncSessionJob& old_job);
    226 
    227   // Determines if it is legal to run |job| by checking current
    228   // operational mode, backoff or throttling, freshness
    229   // (so we don't make redundant syncs), and connection.
    230   bool ShouldRunJob(const SyncSessionJob& job);
    231 
    232   // Decide whether we should CONTINUE, SAVE or DROP the job.
    233   JobProcessDecision DecideOnJob(const SyncSessionJob& job);
    234 
    235   // Decide on whether to CONTINUE, SAVE or DROP the job when we are in
    236   // backoff mode.
    237   JobProcessDecision DecideWhileInWaitInterval(const SyncSessionJob& job);
    238 
    239   // Saves the job for future execution. Note: It drops all the poll jobs.
    240   void SaveJob(const SyncSessionJob& job);
    241 
    242   // Coalesces the current job with the pending nudge.
    243   void InitOrCoalescePendingJob(const SyncSessionJob& job);
    244 
    245   // 'Impl' here refers to real implementation of public functions, running on
    246   // |thread_|.
    247   void StartImpl(Mode mode, ModeChangeCallback* callback);
    248   void ScheduleNudgeImpl(
    249       const base::TimeDelta& delay,
    250       sync_pb::GetUpdatesCallerInfo::GetUpdatesSource source,
    251       const syncable::ModelTypePayloadMap& types_with_payloads,
    252       bool is_canary_job, const tracked_objects::Location& nudge_location);
    253   void ScheduleConfigImpl(const ModelSafeRoutingInfo& routing_info,
    254       const std::vector<ModelSafeWorker*>& workers,
    255       const sync_pb::GetUpdatesCallerInfo::GetUpdatesSource source);
    256   void ScheduleClearUserDataImpl();
    257 
    258   // Returns true if the client is currently in exponential backoff.
    259   bool IsBackingOff() const;
    260 
    261   // Helper to signal all listeners registered with |session_context_|.
    262   void Notify(SyncEngineEvent::EventCause cause);
    263 
    264   // Callback to change backoff state.
    265   void DoCanaryJob();
    266   void Unthrottle();
    267 
    268   // Executes the pending job. Called whenever an event occurs that may
    269   // change conditions permitting a job to run. Like when network connection is
    270   // re-established, mode changes etc.
    271   void DoPendingJobIfPossible(bool is_canary_job);
    272 
    273   // The pointer is owned by the caller.
    274   browser_sync::sessions::SyncSession* CreateSyncSession(
    275       const browser_sync::sessions::SyncSourceInfo& info);
    276 
    277   // Creates a session for a poll and performs the sync.
    278   void PollTimerCallback();
    279 
    280   // Assign |start| and |end| to appropriate SyncerStep values for the
    281   // specified |purpose|.
    282   void SetSyncerStepsForPurpose(SyncSessionJob::SyncSessionJobPurpose purpose,
    283                                 SyncerStep* start,
    284                                 SyncerStep* end);
    285 
    286   // Initializes the hookup between the ServerConnectionManager and us.
    287   void WatchConnectionManager();
    288 
    289   // Used to update |server_connection_ok_|, see below.
    290   void CheckServerConnectionManagerStatus(
    291       HttpResponse::ServerConnectionCode code);
    292 
    293   // Called once the first time thread_ is started to broadcast an initial
    294   // session snapshot containing data like initial_sync_ended.  Important when
    295   // the client starts up and does not need to perform an initial sync.
    296   void SendInitialSnapshot();
    297 
    298   base::Thread thread_;
    299 
    300   // Modifiable versions of kDefaultLongPollIntervalSeconds which can be
    301   // updated by the server.
    302   base::TimeDelta syncer_short_poll_interval_seconds_;
    303   base::TimeDelta syncer_long_poll_interval_seconds_;
    304 
    305   // Periodic timer for polling.  See AdjustPolling.
    306   base::RepeatingTimer<SyncerThread> poll_timer_;
    307 
    308   // The mode of operation. We don't use a lock, see Start(...) comment.
    309   Mode mode_;
    310 
    311   // TODO(tim): Bug 26339. This needs to track more than just time I think,
    312   // since the nudges could be for different types. Current impl doesn't care.
    313   base::TimeTicks last_sync_session_end_time_;
    314 
    315   // Have we observed a valid server connection?
    316   bool server_connection_ok_;
    317 
    318   // Tracks in-flight nudges so we can coalesce.
    319   scoped_ptr<SyncSessionJob> pending_nudge_;
    320 
    321   // Current wait state.  Null if we're not in backoff and not throttled.
    322   scoped_ptr<WaitInterval> wait_interval_;
    323 
    324   scoped_ptr<DelayProvider> delay_provider_;
    325 
    326   // Invoked to run through the sync cycle.
    327   scoped_ptr<Syncer> syncer_;
    328 
    329   scoped_ptr<sessions::SyncSessionContext> session_context_;
    330 
    331   DISALLOW_COPY_AND_ASSIGN(SyncerThread);
    332 };
    333 
    334 
    335 }  // namespace browser_sync
    336 
    337 // The SyncerThread manages its own internal thread and thus outlives it. We
    338 // don't need refcounting for posting tasks to this internal thread.
    339 DISABLE_RUNNABLE_METHOD_REFCOUNT(browser_sync::SyncerThread);
    340 
    341 #endif  // CHROME_BROWSER_SYNC_ENGINE_SYNCER_THREAD_H_
    342