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
26namespace browser_sync {
27
28struct ServerConnectionEvent;
29
30class 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.
339DISABLE_RUNNABLE_METHOD_REFCOUNT(browser_sync::SyncerThread);
340
341#endif  // CHROME_BROWSER_SYNC_ENGINE_SYNCER_THREAD_H_
342