// 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_
