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