• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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