Index: chrome/browser/sync/engine/syncer_thread.h |
=================================================================== |
--- chrome/browser/sync/engine/syncer_thread.h (revision 26372) |
+++ chrome/browser/sync/engine/syncer_thread.h (working copy) |
@@ -3,7 +3,8 @@ |
// 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_ |
@@ -13,11 +14,15 @@ |
#include <vector> |
#include "base/basictypes.h" |
+#include "base/condition_variable.h" |
+#include "base/ref_counted.h" |
#include "base/scoped_ptr.h" |
+#include "base/thread.h" |
+#include "base/time.h" |
+#include "base/waitable_event.h" |
#include "chrome/browser/sync/engine/all_status.h" |
#include "chrome/browser/sync/engine/client_command_channel.h" |
#include "chrome/browser/sync/util/event_sys-inl.h" |
-#include "chrome/browser/sync/util/pthread_helpers.h" |
#include "testing/gtest/include/gtest/gtest_prod.h" // For FRIEND_TEST |
class EventListenerHookup; |
@@ -39,20 +44,42 @@ |
struct SyncerShutdownEvent; |
struct TalkMediatorEvent; |
-class SyncerThread { |
+class SyncerThreadFactory { |
+ public: |
+ // Creates a SyncerThread based on the default (or user-overridden) |
+ // implementation. The thread does not start running until you call Start(), |
+ // which will cause it to check-and-wait for certain conditions to be met |
+ // (such as valid connection with Server established, syncable::Directory has |
+ // been opened) before performing an intial sync with a server. It uses |
+ // |connection_manager| to detect valid connections, and |mgr| to detect the |
+ // opening of a Directory, which will cause it to create a Syncer object for |
+ // said Directory, and assign |model_safe_worker| to it. |connection_manager| |
+ // and |mgr| should outlive the SyncerThread. You must stop the thread by |
+ // calling Stop before destroying the object. Stopping will first tear down |
+ // the Syncer object, allowing it to finish work in progress, before joining |
+ // the Stop-calling thread with the internal thread. |
+ static SyncerThread* Create(ClientCommandChannel* command_channel, |
+ syncable::DirectoryManager* mgr, |
+ ServerConnectionManager* connection_manager, AllStatus* all_status, |
+ ModelSafeWorker* model_safe_worker); |
+ private: |
+ DISALLOW_IMPLICIT_CONSTRUCTORS(SyncerThreadFactory); |
+}; |
+ |
+class SyncerThread : public base::RefCountedThreadSafe<SyncerThread> { |
FRIEND_TEST(SyncerThreadTest, CalculateSyncWaitTime); |
FRIEND_TEST(SyncerThreadTest, CalculatePollingWaitTime); |
- |
- public: |
- friend class SyncerThreadTest; |
- |
+ FRIEND_TEST(SyncerThreadWithSyncerTest, Polling); |
+ FRIEND_TEST(SyncerThreadWithSyncerTest, Nudge); |
+ friend class SyncerThreadWithSyncerTest; |
+ friend class SyncerThreadFactory; |
+public: |
enum NudgeSource { |
kUnknown = 0, |
kNotification, |
kLocal, |
kContinuation |
}; |
- |
// Server can overwrite these values via client commands. |
// Standard short poll. This is used when XMPP is off. |
static const int kDefaultShortPollIntervalSeconds = 60; |
@@ -62,55 +89,107 @@ |
// longest possible poll interval. |
static const int kDefaultMaxPollIntervalMs = 30 * 60 * 1000; |
- SyncerThread(ClientCommandChannel* command_channel, |
- syncable::DirectoryManager* mgr, |
- ServerConnectionManager* connection_manager, AllStatus* all_status, |
- ModelSafeWorker* model_safe_worker); |
- ~SyncerThread(); |
+ virtual ~SyncerThread(); |
- void WatchConnectionManager(ServerConnectionManager* conn_mgr); |
- // Creates and starts a syncer thread. |
+ 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. |
- bool Start(); |
+ virtual bool Start(); |
- // Stop processing. A max wait of at least 2*server RTT time is recommended. |
- // returns true if we stopped, false otherwise. |
- bool Stop(int max_wait); |
+ // Stop processing. |max_wait| doesn't do anything in this version. |
+ virtual bool Stop(int max_wait); |
- // Nudges the syncer to sync with a delay specified. This API is for access |
+ // 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. |
- bool NudgeSyncer(int milliseconds_from_now, NudgeSource source); |
+ virtual bool NudgeSyncer(int milliseconds_from_now, NudgeSource source); |
// Registers this thread to watch talk mediator events. |
- void WatchTalkMediator(TalkMediator* talk_mediator); |
+ virtual void WatchTalkMediator(TalkMediator* talk_mediator); |
- void WatchClientCommands(ClientCommandChannel* channel); |
+ virtual void WatchClientCommands(ClientCommandChannel* channel); |
- SyncerEventChannel* channel(); |
+ virtual SyncerEventChannel* channel(); |
- private: |
- // A few members to gate the rate at which we nudge the syncer. |
- enum { |
- kNudgeRateLimitCount = 6, |
- kNudgeRateLimitTime = 180, |
- }; |
+ protected: |
+ SyncerThread(); // Necessary for temporary pthreads-based PIMPL impl. |
+ SyncerThread(ClientCommandChannel* command_channel, |
+ syncable::DirectoryManager* mgr, |
+ ServerConnectionManager* connection_manager, AllStatus* all_status, |
+ ModelSafeWorker* model_safe_worker); |
+ virtual void ThreadMain(); |
+ void ThreadMainLoop(); |
- // A queue of all scheduled nudges. One insertion for every call to |
- // NudgeQueue(). |
- typedef std::pair<timespec, NudgeSource> NudgeObject; |
+ virtual void SetConnected(bool connected) { |
+ DCHECK(!thread_.IsRunning()); |
+ vault_.connected_ = connected; |
+ } |
- struct IsTimeSpecGreater { |
+ virtual void SetSyncerPollingInterval(base::TimeDelta interval) { |
+ // TODO(timsteele): Use TimeDelta internally. |
+ syncer_polling_interval_ = static_cast<int>(interval.InSeconds()); |
+ } |
+ virtual void SetSyncerShortPollInterval(base::TimeDelta interval) { |
+ // TODO(timsteele): Use TimeDelta internally. |
+ syncer_short_poll_interval_seconds_ = |
+ static_cast<int>(interval.InSeconds()); |
+ } |
+ |
+ // 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_; |
+ |
+ typedef std::pair<base::TimeTicks, NudgeSource> NudgeObject; |
+ |
+ struct IsTimeTicksGreater { |
inline bool operator() (const NudgeObject& lhs, const NudgeObject& rhs) { |
- return lhs.first.tv_sec == rhs.first.tv_sec ? |
- lhs.first.tv_nsec > rhs.first.tv_nsec : |
- lhs.first.tv_sec > rhs.first.tv_sec; |
+ return lhs.first > rhs.first; |
} |
}; |
typedef std::priority_queue<NudgeObject, std::vector<NudgeObject>, |
- IsTimeSpecGreater> NudgeQueue; |
+ IsTimeTicksGreater> NudgeQueue; |
+ // 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_; |
+ |
+ Syncer* syncer_; |
+ |
+ // State of the server connection. |
+ bool connected_; |
+ |
+ // A queue of all scheduled nudges. One insertion for every call to |
+ // NudgeQueue(). |
+ NudgeQueue nudge_queue_; |
+ |
+ ProtectedFields() |
+ : stop_syncer_thread_(false), syncer_(NULL), connected_(false) {} |
+ } 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: |
+ // A few members to gate the rate at which we nudge the syncer. |
+ enum { |
+ kNudgeRateLimitCount = 6, |
+ kNudgeRateLimitTime = 180, |
+ }; |
+ |
// Threshold multipler for how long before user should be considered idle. |
static const int kPollBackoffThresholdMultiplier = 10; |
@@ -125,9 +204,6 @@ |
void HandleTalkMediatorEvent(const TalkMediatorEvent& event); |
- void* ThreadMain(); |
- void ThreadMainLoop(); |
- |
void SyncMain(Syncer* syncer); |
// Calculates the next sync wait time in seconds. last_poll_wait is the time |
@@ -137,43 +213,25 @@ |
// 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. |
- int CalculatePollingWaitTime( |
+ virtual int CalculatePollingWaitTime( |
const AllStatus::Status& status, |
int last_poll_wait, // in s |
int* user_idle_milliseconds, |
bool* continue_sync_cycle); |
// Helper to above function, considers effect of user idle time. |
- int CalculateSyncWaitTime(int last_wait, int user_idle_ms); |
+ virtual int CalculateSyncWaitTime(int last_wait, int user_idle_ms); |
// Sets the source value of the controlled syncer's updates_source value. |
// The initial sync boolean is updated if read as a sentinel. The following |
// two methods work in concert to achieve this goal. |
- void UpdateNudgeSource(const timespec& now, bool* continue_sync_cycle, |
+ void UpdateNudgeSource(bool* continue_sync_cycle, |
bool* initial_sync); |
void SetUpdatesSource(bool nudged, NudgeSource nudge_source, |
bool* initial_sync); |
// For unit tests only. |
- void DisableIdleDetection() { disable_idle_detection_ = true; } |
+ virtual void DisableIdleDetection() { disable_idle_detection_ = true; } |
- // False when we want to stop the thread. |
- bool stop_syncer_thread_; |
- |
- // We use one mutex for all members except the channel. |
- PThreadMutex mutex_; |
- typedef PThreadScopedLock<PThreadMutex> MutexLock; |
- |
- // Handle of the running thread. |
- pthread_t thread_; |
- bool thread_running_; |
- |
- // Gets signaled whenever a thread outside of the syncer thread changes a |
- // member variable. |
- PThreadCondVar changed_; |
- |
- // State of the server connection. |
- bool connected_; |
- |
// State of the notification framework is tracked by these values. |
bool p2p_authenticated_; |
bool p2p_subscribed_; |
@@ -182,8 +240,6 @@ |
scoped_ptr<EventListenerHookup> conn_mgr_hookup_; |
const AllStatus* allstatus_; |
- Syncer* syncer_; |
- |
syncable::DirectoryManager* dirman_; |
ServerConnectionManager* scm_; |
@@ -208,8 +264,6 @@ |
// this is called. |
void NudgeSyncImpl(int milliseconds_from_now, NudgeSource source); |
- NudgeQueue nudge_queue_; |
- |
scoped_ptr<EventListenerHookup> talk_mediator_hookup_; |
ClientCommandChannel* const command_channel_; |
scoped_ptr<EventListenerHookup> directory_manager_hookup_; |
@@ -228,4 +282,4 @@ |
} // namespace browser_sync |
-#endif // CHROME_BROWSER_SYNC_ENGINE_SYNCER_THREAD_H_ |
+#endif // CHROME_BROWSER_SYNC_ENGINE_SYNCER_THREAD_H_ |