blob: 66812257a53752224483d9b4e517802fecc3df78 [file] [log] [blame]
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// A class to run the syncer on a thread.
// This is the default implementation of SyncerThread whose Stop implementation
// does not support a timeout, but is greatly simplified.
#ifndef CHROME_BROWSER_SYNC_ENGINE_SYNCER_THREAD_H_
#define CHROME_BROWSER_SYNC_ENGINE_SYNCER_THREAD_H_
#pragma once
#include <list>
#include <string>
#include <vector>
#include "base/basictypes.h"
#include "base/condition_variable.h"
#include "base/gtest_prod_util.h"
#include "base/ref_counted.h"
#include "base/scoped_ptr.h"
#include "base/thread.h"
#include "base/time.h"
#include "base/waitable_event.h"
#if defined(OS_LINUX)
#include "chrome/browser/sync/engine/idle_query_linux.h"
#endif
#include "chrome/browser/sync/engine/syncer_types.h"
#include "chrome/browser/sync/sessions/sync_session.h"
#include "chrome/browser/sync/syncable/model_type.h"
#include "chrome/common/deprecated/event_sys-inl.h"
class EventListenerHookup;
namespace browser_sync {
class ModelSafeWorker;
class ServerConnectionManager;
class Syncer;
class URLFactory;
struct ServerConnectionEvent;
class SyncerThread : public base::RefCountedThreadSafe<SyncerThread>,
public sessions::SyncSession::Delegate {
FRIEND_TEST_ALL_PREFIXES(SyncerThreadTest, CalculateSyncWaitTime);
FRIEND_TEST_ALL_PREFIXES(SyncerThreadTest, CalculatePollingWaitTime);
FRIEND_TEST_ALL_PREFIXES(SyncerThreadWithSyncerTest, Polling);
FRIEND_TEST_ALL_PREFIXES(SyncerThreadWithSyncerTest, Nudge);
FRIEND_TEST_ALL_PREFIXES(SyncerThreadWithSyncerTest, NudgeWithDataTypes);
FRIEND_TEST_ALL_PREFIXES(SyncerThreadWithSyncerTest,
NudgeWithDataTypesCoalesced);
FRIEND_TEST_ALL_PREFIXES(SyncerThreadWithSyncerTest, Throttling);
FRIEND_TEST_ALL_PREFIXES(SyncerThreadWithSyncerTest, AuthInvalid);
FRIEND_TEST_ALL_PREFIXES(SyncerThreadWithSyncerTest, Pause);
FRIEND_TEST_ALL_PREFIXES(SyncerThreadWithSyncerTest, StartWhenNotConnected);
FRIEND_TEST_ALL_PREFIXES(SyncerThreadWithSyncerTest, PauseWhenNotConnected);
FRIEND_TEST_ALL_PREFIXES(SyncerThreadWithSyncerTest, StopSyncPermanently);
friend class SyncerThreadWithSyncerTest;
friend class SyncerThreadFactory;
public:
// Encapsulates the parameters that make up an interval on which the
// syncer thread is sleeping.
struct WaitInterval {
enum Mode {
// A wait interval whose duration has not been affected by exponential
// backoff. The base case for exponential backoff falls in to this case
// (e.g when the exponent is 1). So far, we don't need a separate case.
// NORMAL intervals are not nudge-rate limited.
NORMAL,
// A wait interval whose duration has been affected by exponential
// backoff.
// EXPONENTIAL_BACKOFF intervals are nudge-rate limited to 1 per interval.
EXPONENTIAL_BACKOFF,
// A server-initiated throttled interval. We do not allow any syncing
// during such an interval.
THROTTLED,
};
Mode mode;
// This bool is set to true if we have observed a nudge during during this
// interval and mode == EXPONENTIAL_BACKOFF.
bool had_nudge_during_backoff;
base::TimeDelta poll_delta; // The wait duration until the next poll.
WaitInterval() : mode(NORMAL), had_nudge_during_backoff(false) { }
};
enum NudgeSource {
kUnknown = 0,
kNotification,
kLocal,
kContinuation,
kClearPrivateData
};
// Server can overwrite these values via client commands.
// Standard short poll. This is used when XMPP is off.
static const int kDefaultShortPollIntervalSeconds;
// Long poll is used when XMPP is on.
static const int kDefaultLongPollIntervalSeconds;
// 30 minutes by default. If exponential backoff kicks in, this is the
// longest possible poll interval.
static const int kDefaultMaxPollIntervalMs;
// Maximum interval for exponential backoff.
static const int kMaxBackoffSeconds;
explicit SyncerThread(sessions::SyncSessionContext* context);
virtual ~SyncerThread();
virtual void WatchConnectionManager(ServerConnectionManager* conn_mgr);
// Starts a syncer thread.
// Returns true if it creates a thread or if there's currently a thread
// running and false otherwise.
virtual bool Start();
// Stop processing. |max_wait| doesn't do anything in this version.
virtual bool Stop(int max_wait);
// Request that the thread pauses. Returns false if the request can
// not be completed (e.g. the thread is not running). When the
// thread actually pauses, a SyncEngineEvent::PAUSED event notification
// will be sent to the relay channel.
virtual bool RequestPause();
// Request that the thread resumes from pause. Returns false if the
// request can not be completed (e.g. the thread is not running or
// is not currently paused). When the thread actually resumes, a
// SyncEngineEvent::RESUMED event notification will be sent to the relay
// channel.
virtual bool RequestResume();
// Nudges the syncer to sync with a delay specified. This API is for access
// from the SyncerThread's controller and will cause a mutex lock.
virtual void NudgeSyncer(int milliseconds_from_now, NudgeSource source);
// Same as |NudgeSyncer|, but supports tracking the datatypes that caused
// the nudge to occur.
virtual void NudgeSyncerWithDataTypes(
int milliseconds_from_now,
NudgeSource source,
const syncable::ModelTypeBitSet& model_type);
void SetNotificationsEnabled(bool notifications_enabled);
// Call this when a directory is opened
void CreateSyncer(const std::string& dirname);
// DDOS avoidance function. The argument and return value is in seconds
static int GetRecommendedDelaySeconds(int base_delay_seconds);
protected:
virtual void ThreadMain();
void ThreadMainLoop();
virtual void SetConnected(bool connected);
virtual void SetSyncerPollingInterval(base::TimeDelta interval);
virtual void SetSyncerShortPollInterval(base::TimeDelta interval);
// Needed to emulate the behavior of pthread_create, which synchronously
// started the thread and set the value of thread_running_ to true.
// We can't quite match that because we asynchronously post the task,
// which opens a window for Stop to get called before the task actually
// makes it. To prevent this, we block Start() until we're sure it's ok.
base::WaitableEvent thread_main_started_;
// Handle of the running thread.
base::Thread thread_;
// Fields that are modified / accessed by multiple threads go in this struct
// for clarity and explicitness.
struct ProtectedFields {
// False when we want to stop the thread.
bool stop_syncer_thread_;
// True when a pause was requested.
bool pause_requested_;
// True when the thread is paused.
bool paused_;
Syncer* syncer_;
// State of the server connection.
bool connected_;
// kUnknown if there is no pending nudge. (Theoretically, there
// could be a pending nudge of type kUnknown, so it's better to
// check pending_nudge_time_.)
NudgeSource pending_nudge_source_;
// BitSet of the datatypes that have triggered the current nudge
// (can be union of various bitsets when multiple nudges are coalesced)
syncable::ModelTypeBitSet pending_nudge_types_;
// null iff there is no pending nudge.
base::TimeTicks pending_nudge_time_;
// The wait interval for to the current iteration of our main loop. This is
// only written to by the syncer thread, and since the only reader from a
// different thread (NudgeSync) is called at totally random times, we don't
// really need to access mutually exclusively as the data races that exist
// are intrinsic, but do so anyway and avoid using 'volatile'.
WaitInterval current_wait_interval_;
ProtectedFields()
: stop_syncer_thread_(false),
pause_requested_(false),
paused_(false),
syncer_(NULL),
connected_(false),
pending_nudge_source_(kUnknown) {}
} vault_;
// Gets signaled whenever a thread outside of the syncer thread changes a
// protected field in the vault_.
ConditionVariable vault_field_changed_;
// Used to lock everything in |vault_|.
Lock lock_;
private:
// Threshold multipler for how long before user should be considered idle.
static const int kPollBackoffThresholdMultiplier = 10;
// SyncSession::Delegate implementation.
virtual void OnSilencedUntil(const base::TimeTicks& silenced_until);
virtual bool IsSyncingCurrentlySilenced();
virtual void OnReceivedShortPollIntervalUpdate(
const base::TimeDelta& new_interval);
virtual void OnReceivedLongPollIntervalUpdate(
const base::TimeDelta& new_interval);
virtual void OnShouldStopSyncingPermanently();
void HandleServerConnectionEvent(const ServerConnectionEvent& event);
// Collect all local state required for a sync and build a SyncSession out of
// it, reset state for the next time, and performs the sync cycle.
// See |GetAndResetNudgeSource| for details on what 'reset' means.
// |was_nudged| is set to true if the session returned is fulfilling a nudge.
// Returns once the session is finished (HasMoreToSync returns false). The
// caller owns the returned SyncSession.
sessions::SyncSession* SyncMain(Syncer* syncer,
bool was_throttled,
bool continue_sync_cycle,
bool* initial_sync_for_thread,
bool* was_nudged);
// Calculates the next sync wait time and exponential backoff state.
// last_poll_wait is the time duration of the previous polling timeout which
// was used. user_idle_milliseconds is updated by this method, and is a report
// of the full amount of time since the last period of activity for the user.
// The continue_sync_cycle parameter is used to determine whether or not we
// are calculating a polling wait time that is a continuation of an sync cycle
// which terminated while the syncer still had work to do. was_nudged is used
// in case of exponential backoff so we only allow one nudge per backoff
// interval.
WaitInterval CalculatePollingWaitTime(
int last_poll_wait, // in s
int* user_idle_milliseconds,
bool* continue_sync_cycle,
bool was_nudged);
// Helper to above function, considers effect of user idle time.
virtual int CalculateSyncWaitTime(int last_wait, int user_idle_ms);
// Resets the source tracking state to a clean slate and returns the current
// state in a SyncSourceInfo.
// The initial sync boolean is updated if read as a sentinel. The following
// two methods work in concert to achieve this goal.
// If |was_throttled| was true, this still discards elapsed nudges, but we
// treat the request as a periodic poll rather than a nudge from a source.
// Builds a SyncSourceInfo and returns whether a nudge occurred in the
// |was_nudged| parameter.
sessions::SyncSourceInfo GetAndResetNudgeSource(bool was_throttled,
bool continue_sync_cycle,
bool* initial_sync,
bool* was_nudged);
sessions::SyncSourceInfo MakeSyncSourceInfo(
bool nudged,
NudgeSource nudge_source,
const syncable::ModelTypeBitSet& nudge_types,
bool* initial_sync);
int UserIdleTime();
void WaitUntilConnectedOrQuit();
// The thread will remain in this method until a resume is requested
// or shutdown is started.
void PauseUntilResumedOrQuit();
void EnterPausedState();
void ExitPausedState();
// For unit tests only.
virtual void DisableIdleDetection();
// This sets all conditions for syncer thread termination but does not
// actually join threads. It is expected that Stop will be called at some
// time after to fully stop and clean up.
void RequestSyncerExitAndSetThreadStopConditions();
void Notify(SyncEngineEvent::EventCause cause);
scoped_ptr<EventListenerHookup> conn_mgr_hookup_;
// Modifiable versions of kDefaultLongPollIntervalSeconds which can be
// updated by the server.
int syncer_short_poll_interval_seconds_;
int syncer_long_poll_interval_seconds_;
// The time we wait between polls in seconds. This is used as lower bound on
// our wait time. Updated once per loop from the command line flag.
int syncer_polling_interval_;
// The upper bound on the nominal wait between polls in seconds. Note that
// this bounds the "nominal" poll interval, while the the actual interval
// also takes previous failures into account.
int syncer_max_interval_;
// This causes syncer to start syncing ASAP. If the rate of requests is too
// high the request will be silently dropped. mutex_ should be held when
// this is called.
void NudgeSyncImpl(
int milliseconds_from_now,
NudgeSource source,
const syncable::ModelTypeBitSet& model_types);
#if defined(OS_LINUX)
// On Linux, we need this information in order to query idle time.
scoped_ptr<IdleQueryLinux> idle_query_;
#endif
scoped_ptr<sessions::SyncSessionContext> session_context_;
// Set whenever the server instructs us to stop sending it requests until
// a specified time, and reset for each call to SyncShare. (Note that the
// WaitInterval::THROTTLED contract is such that we don't call SyncShare at
// all until the "silenced until" embargo expires.)
base::TimeTicks silenced_until_;
// Useful for unit tests
bool disable_idle_detection_;
DISALLOW_COPY_AND_ASSIGN(SyncerThread);
};
} // namespace browser_sync
#endif // CHROME_BROWSER_SYNC_ENGINE_SYNCER_THREAD_H_