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