1// Copyright 2012 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#ifndef SYNC_ENGINE_SYNC_SCHEDULER_IMPL_H_
6#define SYNC_ENGINE_SYNC_SCHEDULER_IMPL_H_
7
8#include <map>
9#include <string>
10
11#include "base/callback.h"
12#include "base/cancelable_callback.h"
13#include "base/compiler_specific.h"
14#include "base/gtest_prod_util.h"
15#include "base/memory/linked_ptr.h"
16#include "base/memory/scoped_ptr.h"
17#include "base/memory/weak_ptr.h"
18#include "base/threading/non_thread_safe.h"
19#include "base/time/time.h"
20#include "base/timer/timer.h"
21#include "sync/base/sync_export.h"
22#include "sync/engine/net/server_connection_manager.h"
23#include "sync/engine/nudge_source.h"
24#include "sync/engine/sync_scheduler.h"
25#include "sync/engine/syncer.h"
26#include "sync/internal_api/public/engine/polling_constants.h"
27#include "sync/internal_api/public/util/weak_handle.h"
28#include "sync/sessions/nudge_tracker.h"
29#include "sync/sessions/sync_session.h"
30#include "sync/sessions/sync_session_context.h"
31
32namespace syncer {
33
34class BackoffDelayProvider;
35
36namespace sessions {
37struct ModelNeutralState;
38}
39
40class SYNC_EXPORT_PRIVATE SyncSchedulerImpl
41    : public SyncScheduler,
42      public base::NonThreadSafe {
43 public:
44  // |name| is a display string to identify the syncer thread.  Takes
45  // |ownership of |syncer| and |delay_provider|.
46  SyncSchedulerImpl(const std::string& name,
47                    BackoffDelayProvider* delay_provider,
48                    sessions::SyncSessionContext* context,
49                    Syncer* syncer);
50
51  // Calls Stop().
52  virtual ~SyncSchedulerImpl();
53
54  virtual void Start(Mode mode) OVERRIDE;
55  virtual void ScheduleConfiguration(
56      const ConfigurationParams& params) OVERRIDE;
57  virtual void Stop() OVERRIDE;
58  virtual void ScheduleLocalNudge(
59      ModelTypeSet types,
60      const tracked_objects::Location& nudge_location) OVERRIDE;
61  virtual void ScheduleLocalRefreshRequest(
62      ModelTypeSet types,
63      const tracked_objects::Location& nudge_location) OVERRIDE;
64  virtual void ScheduleInvalidationNudge(
65      syncer::ModelType type,
66      scoped_ptr<InvalidationInterface> invalidation,
67      const tracked_objects::Location& nudge_location) OVERRIDE;
68  virtual void ScheduleInitialSyncNudge(syncer::ModelType model_type) OVERRIDE;
69  virtual void SetNotificationsEnabled(bool notifications_enabled) OVERRIDE;
70
71  virtual void OnCredentialsUpdated() OVERRIDE;
72  virtual void OnConnectionStatusChange() OVERRIDE;
73
74  // SyncSession::Delegate implementation.
75  virtual void OnThrottled(const base::TimeDelta& throttle_duration) OVERRIDE;
76  virtual void OnTypesThrottled(
77      ModelTypeSet types,
78      const base::TimeDelta& throttle_duration) OVERRIDE;
79  virtual bool IsCurrentlyThrottled() OVERRIDE;
80  virtual void OnReceivedShortPollIntervalUpdate(
81      const base::TimeDelta& new_interval) OVERRIDE;
82  virtual void OnReceivedLongPollIntervalUpdate(
83      const base::TimeDelta& new_interval) OVERRIDE;
84  virtual void OnReceivedCustomNudgeDelays(
85      const std::map<ModelType, base::TimeDelta>& nudge_delays) OVERRIDE;
86  virtual void OnReceivedClientInvalidationHintBufferSize(int size) OVERRIDE;
87  virtual void OnSyncProtocolError(
88      const SyncProtocolError& sync_protocol_error) OVERRIDE;
89  virtual void OnReceivedGuRetryDelay(const base::TimeDelta& delay) OVERRIDE;
90  virtual void OnReceivedMigrationRequest(syncer::ModelTypeSet types) OVERRIDE;
91
92  // Returns true if the client is currently in exponential backoff.
93  bool IsBackingOff() const;
94
95 private:
96  enum JobPriority {
97    // Non-canary jobs respect exponential backoff.
98    NORMAL_PRIORITY,
99    // Canary jobs bypass exponential backoff, so use with extreme caution.
100    CANARY_PRIORITY
101  };
102
103  enum PollAdjustType {
104    // Restart the poll interval.
105    FORCE_RESET,
106    // Restart the poll interval only if its length has changed.
107    UPDATE_INTERVAL,
108  };
109
110  friend class SyncSchedulerTest;
111  friend class SyncSchedulerWhiteboxTest;
112  friend class SyncerTest;
113
114  FRIEND_TEST_ALL_PREFIXES(SyncSchedulerTest, TransientPollFailure);
115  FRIEND_TEST_ALL_PREFIXES(SyncSchedulerTest,
116                           ServerConnectionChangeDuringBackoff);
117  FRIEND_TEST_ALL_PREFIXES(SyncSchedulerTest,
118                           ConnectionChangeCanaryPreemptedByNudge);
119  FRIEND_TEST_ALL_PREFIXES(BackoffTriggersSyncSchedulerTest,
120                           FailGetEncryptionKey);
121  FRIEND_TEST_ALL_PREFIXES(SyncSchedulerTest, SuccessfulRetry);
122  FRIEND_TEST_ALL_PREFIXES(SyncSchedulerTest, FailedRetry);
123  FRIEND_TEST_ALL_PREFIXES(SyncSchedulerTest, ReceiveNewRetryDelay);
124
125  struct SYNC_EXPORT_PRIVATE WaitInterval {
126    enum Mode {
127      // Uninitialized state, should not be set in practice.
128      UNKNOWN = -1,
129      // We enter a series of increasingly longer WaitIntervals if we experience
130      // repeated transient failures.  We retry at the end of each interval.
131      EXPONENTIAL_BACKOFF,
132      // A server-initiated throttled interval.  We do not allow any syncing
133      // during such an interval.
134      THROTTLED,
135    };
136    WaitInterval();
137    ~WaitInterval();
138    WaitInterval(Mode mode, base::TimeDelta length);
139
140    static const char* GetModeString(Mode mode);
141
142    Mode mode;
143    base::TimeDelta length;
144  };
145
146  static const char* GetModeString(Mode mode);
147
148  void SetDefaultNudgeDelay(base::TimeDelta delay_ms);
149
150  // Invoke the syncer to perform a nudge job.
151  void DoNudgeSyncSessionJob(JobPriority priority);
152
153  // Invoke the syncer to perform a configuration job.
154  void DoConfigurationSyncSessionJob(JobPriority priority);
155
156  // Helper function for Do{Nudge,Configuration}SyncSessionJob.
157  void HandleFailure(
158      const sessions::ModelNeutralState& model_neutral_state);
159
160  // Invoke the Syncer to perform a poll job.
161  void DoPollSyncSessionJob();
162
163  // Helper function to calculate poll interval.
164  base::TimeDelta GetPollInterval();
165
166  // Adjusts the poll timer to account for new poll interval, and possibly
167  // resets the poll interval, depedning on the flag's value.
168  void AdjustPolling(PollAdjustType type);
169
170  // Helper to restart waiting with |wait_interval_|'s timer.
171  void RestartWaiting();
172
173  // Determines if we're allowed to contact the server right now.
174  bool CanRunJobNow(JobPriority priority);
175
176  // Determines if we're allowed to contact the server right now.
177  bool CanRunNudgeJobNow(JobPriority priority);
178
179  // If the scheduler's current state supports it, this will create a job based
180  // on the passed in parameters and coalesce it with any other pending jobs,
181  // then post a delayed task to run it.  It may also choose to drop the job or
182  // save it for later, depending on the scheduler's current state.
183  void ScheduleNudgeImpl(
184      const base::TimeDelta& delay,
185      const tracked_objects::Location& nudge_location);
186
187  // Helper to signal listeners about changed retry time.
188  void NotifyRetryTime(base::Time retry_time);
189
190  // Helper to signal listeners about changed throttled types.
191  void NotifyThrottledTypesChanged(ModelTypeSet types);
192
193  // Looks for pending work and, if it finds any, run this work at "canary"
194  // priority.
195  void TryCanaryJob();
196
197  // At the moment TrySyncSessionJob just posts call to TrySyncSessionJobImpl on
198  // current thread. In the future it will request access token here.
199  void TrySyncSessionJob();
200  void TrySyncSessionJobImpl();
201
202  // Transitions out of the THROTTLED WaitInterval then calls TryCanaryJob().
203  void Unthrottle();
204
205  // Called when a per-type throttling interval expires.
206  void TypeUnthrottle(base::TimeTicks unthrottle_time);
207
208  // Runs a normal nudge job when the scheduled timer expires.
209  void PerformDelayedNudge();
210
211  // Attempts to exit EXPONENTIAL_BACKOFF by calling TryCanaryJob().
212  void ExponentialBackoffRetry();
213
214  // Called when the root cause of the current connection error is fixed.
215  void OnServerConnectionErrorFixed();
216
217  // Creates a session for a poll and performs the sync.
218  void PollTimerCallback();
219
220  // Creates a session for a retry and performs the sync.
221  void RetryTimerCallback();
222
223  // Returns the set of types that are enabled and not currently throttled.
224  ModelTypeSet GetEnabledAndUnthrottledTypes();
225
226  // Called as we are started to broadcast an initial session snapshot
227  // containing data like initial_sync_ended.  Important when the client starts
228  // up and does not need to perform an initial sync.
229  void SendInitialSnapshot();
230
231  // This is used for histogramming and analysis of ScheduleNudge* APIs.
232  // SyncScheduler is the ultimate choke-point for all such invocations (with
233  // and without InvalidationState variants, all NudgeSources, etc) and as such
234  // is the most flexible place to do this bookkeeping.
235  void UpdateNudgeTimeRecords(ModelTypeSet types);
236
237  // For certain methods that need to worry about X-thread posting.
238  WeakHandle<SyncSchedulerImpl> weak_handle_this_;
239
240  // Used for logging.
241  const std::string name_;
242
243  // Set in Start(), unset in Stop().
244  bool started_;
245
246  // Modifiable versions of kDefaultLongPollIntervalSeconds which can be
247  // updated by the server.
248  base::TimeDelta syncer_short_poll_interval_seconds_;
249  base::TimeDelta syncer_long_poll_interval_seconds_;
250
251  // Periodic timer for polling.  See AdjustPolling.
252  base::RepeatingTimer<SyncSchedulerImpl> poll_timer_;
253
254  // The mode of operation.
255  Mode mode_;
256
257  // Current wait state.  Null if we're not in backoff and not throttled.
258  scoped_ptr<WaitInterval> wait_interval_;
259
260  scoped_ptr<BackoffDelayProvider> delay_provider_;
261
262  // The event that will wake us up.
263  base::OneShotTimer<SyncSchedulerImpl> pending_wakeup_timer_;
264
265  // An event that fires when data type throttling expires.
266  base::OneShotTimer<SyncSchedulerImpl> type_unthrottle_timer_;
267
268  // Storage for variables related to an in-progress configure request.  Note
269  // that (mode_ != CONFIGURATION_MODE) \implies !pending_configure_params_.
270  scoped_ptr<ConfigurationParams> pending_configure_params_;
271
272  // If we have a nudge pending to run soon, it will be listed here.
273  base::TimeTicks scheduled_nudge_time_;
274
275  // Keeps track of work that the syncer needs to handle.
276  sessions::NudgeTracker nudge_tracker_;
277
278  // Invoked to run through the sync cycle.
279  scoped_ptr<Syncer> syncer_;
280
281  sessions::SyncSessionContext* session_context_;
282
283  // A map tracking LOCAL NudgeSource invocations of ScheduleNudge* APIs,
284  // organized by datatype. Each datatype that was part of the types requested
285  // in the call will have its TimeTicks value updated.
286  typedef std::map<ModelType, base::TimeTicks> ModelTypeTimeMap;
287  ModelTypeTimeMap last_local_nudges_by_model_type_;
288
289  // Used as an "anti-reentrancy defensive assertion".
290  // While true, it is illegal for any new scheduling activity to take place.
291  // Ensures that higher layers don't break this law in response to events that
292  // take place during a sync cycle. We call this out because such violations
293  // could result in tight sync loops hitting sync servers.
294  bool no_scheduling_allowed_;
295
296  // crbug/251307. This is a workaround for M29. crbug/259913 tracks proper fix
297  // for M30.
298  // The issue is that poll job runs after few hours of inactivity and therefore
299  // will always fail with auth error because of expired access token. Once
300  // fresh access token is requested poll job is not retried.
301  // The change is to remember that poll timer just fired and retry poll job
302  // after credentials are updated.
303  bool do_poll_after_credentials_updated_;
304
305  // TryJob might get called for multiple reasons. It should only call
306  // DoPollSyncSessionJob after some time since the last attempt.
307  // last_poll_reset_ keeps track of when was last attempt.
308  base::TimeTicks last_poll_reset_;
309
310  // next_sync_session_job_priority_ defines which priority will be used next
311  // time TrySyncSessionJobImpl is called. CANARY_PRIORITY allows syncer to run
312  // even if scheduler is in exponential backoff. This is needed for events that
313  // have chance of resolving previous error (e.g. network connection change
314  // after NETWORK_UNAVAILABLE error).
315  // It is reset back to NORMAL_PRIORITY on every call to TrySyncSessionJobImpl.
316  JobPriority next_sync_session_job_priority_;
317
318  // One-shot timer for scheduling GU retry according to delay set by server.
319  base::OneShotTimer<SyncSchedulerImpl> retry_timer_;
320
321  base::WeakPtrFactory<SyncSchedulerImpl> weak_ptr_factory_;
322
323  // A second factory specially for weak_handle_this_, to allow the handle
324  // to be const and alleviate threading concerns.
325  base::WeakPtrFactory<SyncSchedulerImpl> weak_ptr_factory_for_weak_handle_;
326
327  DISALLOW_COPY_AND_ASSIGN(SyncSchedulerImpl);
328};
329
330}  // namespace syncer
331
332#endif  // SYNC_ENGINE_SYNC_SCHEDULER_IMPL_H_
333