summaryrefslogtreecommitdiffstats
path: root/chrome/browser/sync
diff options
context:
space:
mode:
authorakalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-05-04 21:42:00 +0000
committerakalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-05-04 21:42:00 +0000
commitc11f3e61056cb914ae91428dcba3f6bc05fc7364 (patch)
tree4e563c7d04297606e565afe56e1fe80c1e015f3f /chrome/browser/sync
parentb5775171a912fec03e4098944c34e8cad4b103b2 (diff)
downloadchromium_src-c11f3e61056cb914ae91428dcba3f6bc05fc7364.zip
chromium_src-c11f3e61056cb914ae91428dcba3f6bc05fc7364.tar.gz
chromium_src-c11f3e61056cb914ae91428dcba3f6bc05fc7364.tar.bz2
Wrote NetworkChangeNotifierProxy, which lets objects that don't live on
the same thread as a NetworkChangeNotifier safely listen to notifications. Cleaned up unit tests. Moved ThreadBlocker and MockNetworkChangeObserver into their own files. BUG=42606 TEST=unittests Review URL: http://codereview.chromium.org/1909001 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@46395 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/sync')
-rw-r--r--chrome/browser/sync/net/mock_network_change_observer.h32
-rw-r--r--chrome/browser/sync/net/network_change_notifier_proxy.cc61
-rw-r--r--chrome/browser/sync/net/network_change_notifier_proxy.h72
-rw-r--r--chrome/browser/sync/net/network_change_notifier_proxy_unittest.cc140
-rw-r--r--chrome/browser/sync/net/network_change_observer_proxy.h1
-rw-r--r--chrome/browser/sync/net/network_change_observer_proxy_unittest.cc101
-rw-r--r--chrome/browser/sync/net/thread_blocker.cc51
-rw-r--r--chrome/browser/sync/net/thread_blocker.h51
-rw-r--r--chrome/browser/sync/net/thread_blocker_unittest.cc113
9 files changed, 532 insertions, 90 deletions
diff --git a/chrome/browser/sync/net/mock_network_change_observer.h b/chrome/browser/sync/net/mock_network_change_observer.h
new file mode 100644
index 0000000..91a5ef9
--- /dev/null
+++ b/chrome/browser/sync/net/mock_network_change_observer.h
@@ -0,0 +1,32 @@
+// 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.
+
+#ifndef CHROME_BROWSER_SYNC_NET_MOCK_NETWORK_CHANGE_OBSERVER_H_
+#define CHROME_BROWSER_SYNC_NET_MOCK_NETWORK_CHANGE_OBSERVER_H_
+
+#include "base/basictypes.h"
+#include "net/base/network_change_notifier.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+// This class is a mock net::NetworkChangeNotifier::Observer used in
+// unit tests.
+
+namespace browser_sync {
+
+class MockNetworkChangeObserver
+ : public net::NetworkChangeNotifier::Observer {
+ public:
+ MockNetworkChangeObserver() {}
+
+ virtual ~MockNetworkChangeObserver() {}
+
+ MOCK_METHOD0(OnIPAddressChanged, void());
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockNetworkChangeObserver);
+};
+
+} // namespace browser_sync
+
+#endif // CHROME_BROWSER_SYNC_NET_MOCK_NETWORK_CHANGE_OBSERVER_H_
diff --git a/chrome/browser/sync/net/network_change_notifier_proxy.cc b/chrome/browser/sync/net/network_change_notifier_proxy.cc
new file mode 100644
index 0000000..7176da3
--- /dev/null
+++ b/chrome/browser/sync/net/network_change_notifier_proxy.cc
@@ -0,0 +1,61 @@
+// 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.
+
+#include "chrome/browser/sync/net/network_change_notifier_proxy.h"
+
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "chrome/browser/sync/net/network_change_observer_proxy.h"
+
+namespace browser_sync {
+
+NetworkChangeNotifierProxy::NetworkChangeNotifierProxy(
+ MessageLoop* source_message_loop,
+ net::NetworkChangeNotifier* source_network_change_notifier)
+ : observer_proxy_(new NetworkChangeObserverProxy(
+ source_message_loop, source_network_change_notifier,
+ MessageLoop::current())),
+ observer_repeater_(&observers_) {
+ // TODO(akalin): We get this from NonThreadSafe, which
+ // net::NetworkChangeNotifier inherits from. Interface classes
+ // really shouldn't be inheriting from NonThreadSafe; make it so
+ // that all the implementations of net::NetworkChangeNotifier
+ // inherit from NonThreadSafe directly and
+ // net::NetworkChangeNotifier doesn't.
+ DCHECK(CalledOnValidThread());
+ DCHECK(observer_proxy_);
+ observer_proxy_->Attach(&observer_repeater_);
+}
+
+NetworkChangeNotifierProxy::~NetworkChangeNotifierProxy() {
+ DCHECK(CalledOnValidThread());
+ observer_proxy_->Detach();
+}
+
+void NetworkChangeNotifierProxy::AddObserver(
+ net::NetworkChangeNotifier::Observer* observer) {
+ DCHECK(CalledOnValidThread());
+ observers_.AddObserver(observer);
+}
+
+void NetworkChangeNotifierProxy::RemoveObserver(
+ net::NetworkChangeNotifier::Observer* observer) {
+ DCHECK(CalledOnValidThread());
+ observers_.RemoveObserver(observer);
+}
+
+NetworkChangeNotifierProxy::ObserverRepeater::ObserverRepeater(
+ NetworkObserverList* observers) : observers_(observers) {
+ DCHECK(observers_);
+}
+
+NetworkChangeNotifierProxy::ObserverRepeater::~ObserverRepeater() {}
+
+void NetworkChangeNotifierProxy::ObserverRepeater::OnIPAddressChanged() {
+ DCHECK(CalledOnValidThread());
+ FOR_EACH_OBSERVER(net::NetworkChangeNotifier::Observer,
+ *observers_, OnIPAddressChanged());
+}
+
+} // namespace browser_sync
diff --git a/chrome/browser/sync/net/network_change_notifier_proxy.h b/chrome/browser/sync/net/network_change_notifier_proxy.h
new file mode 100644
index 0000000..517d705
--- /dev/null
+++ b/chrome/browser/sync/net/network_change_notifier_proxy.h
@@ -0,0 +1,72 @@
+// 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.
+
+#ifndef CHROME_BROWSER_SYNC_NET_NETWORK_CHANGE_NOTIFIER_PROXY_H_
+#define CHROME_BROWSER_SYNC_NET_NETWORK_CHANGE_NOTIFIER_PROXY_H_
+
+// NetworkChangeNotifierProxy is a class that lets observers listen to
+// a NetworkChangeNotifier that lives on another thread.
+
+#include "base/basictypes.h"
+#include "base/non_thread_safe.h"
+#include "base/observer_list.h"
+#include "base/ref_counted.h"
+#include "net/base/network_change_notifier.h"
+
+class MessageLoop;
+
+namespace browser_sync {
+
+class NetworkChangeObserverProxy;
+
+class NetworkChangeNotifierProxy : public net::NetworkChangeNotifier {
+ public:
+ // Both source_thread and source_network_change_notifier must be
+ // guaranteed to outlive the current thread. Does not take
+ // ownership of any arguments.
+ NetworkChangeNotifierProxy(
+ MessageLoop* source_message_loop,
+ net::NetworkChangeNotifier* source_network_change_notifier);
+
+ virtual ~NetworkChangeNotifierProxy();
+
+ // net::NetworkChangeNotifier implementation.
+
+ virtual void AddObserver(net::NetworkChangeNotifier::Observer* observer);
+
+ virtual void RemoveObserver(net::NetworkChangeNotifier::Observer* observer);
+
+ private:
+ typedef ObserverList<net::NetworkChangeNotifier::Observer, true>
+ NetworkObserverList;
+
+ // Utility class that routes received notifications to a list of
+ // observers.
+ class ObserverRepeater : public NonThreadSafe,
+ public net::NetworkChangeNotifier::Observer {
+ public:
+ // Does not take ownership of the given observer list.
+ explicit ObserverRepeater(NetworkObserverList* observers);
+
+ virtual ~ObserverRepeater();
+
+ // net::NetworkChangeNotifier::Observer implementation.
+ virtual void OnIPAddressChanged();
+
+ private:
+ NetworkObserverList* observers_;
+
+ DISALLOW_COPY_AND_ASSIGN(ObserverRepeater);
+ };
+
+ scoped_refptr<NetworkChangeObserverProxy> observer_proxy_;
+ NetworkObserverList observers_;
+ ObserverRepeater observer_repeater_;
+
+ DISALLOW_COPY_AND_ASSIGN(NetworkChangeNotifierProxy);
+};
+
+} // namespace browser_sync
+
+#endif // CHROME_BROWSER_SYNC_NET_NETWORK_CHANGE_NOTIFIER_PROXY_H_
diff --git a/chrome/browser/sync/net/network_change_notifier_proxy_unittest.cc b/chrome/browser/sync/net/network_change_notifier_proxy_unittest.cc
new file mode 100644
index 0000000..5e095fd7
--- /dev/null
+++ b/chrome/browser/sync/net/network_change_notifier_proxy_unittest.cc
@@ -0,0 +1,140 @@
+// 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.
+
+#include "chrome/browser/sync/net/network_change_notifier_proxy.h"
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/ref_counted.h"
+#include "base/scoped_ptr.h"
+#include "base/task.h"
+#include "base/thread.h"
+#include "chrome/browser/sync/net/mock_network_change_observer.h"
+#include "chrome/browser/sync/net/thread_blocker.h"
+#include "net/base/mock_network_change_notifier.h"
+#include "net/base/network_change_notifier.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// We manage the lifetime of net::MockNetworkChangeNotifier ourselves.
+template <>
+struct RunnableMethodTraits<net::MockNetworkChangeNotifier> {
+ void RetainCallee(net::MockNetworkChangeNotifier*) {}
+ void ReleaseCallee(net::MockNetworkChangeNotifier*) {}
+};
+
+namespace browser_sync {
+
+namespace {
+
+class NetworkChangeNotifierProxyTest : public testing::Test {
+ protected:
+ NetworkChangeNotifierProxyTest()
+ : source_thread_("Source Thread"),
+ source_message_loop_(NULL) {}
+
+ virtual ~NetworkChangeNotifierProxyTest() {
+ CHECK(!source_thread_blocker_.get());
+ CHECK(!source_message_loop_);
+ }
+
+ virtual void SetUp() {
+ CHECK(source_thread_.Start());
+ source_message_loop_ = source_thread_.message_loop();
+ source_thread_blocker_.reset(new ThreadBlocker(&source_thread_));
+ source_thread_blocker_->Block();
+
+ notifier_proxy_.reset(new NetworkChangeNotifierProxy(
+ source_message_loop_, &source_network_change_notifier_));
+ }
+
+ virtual void TearDown() {
+ // Posts a task to the source thread.
+ notifier_proxy_.reset();
+ source_thread_blocker_->Unblock();
+ source_thread_blocker_.reset();
+ source_message_loop_ = NULL;
+ source_thread_.Stop();
+ }
+
+ // Trigger an "IP address changed" event on the source network
+ // change notifier on the source thread and propagate any generated
+ // notifications to the target thread.
+ void OnIPAddressChanged() {
+ source_message_loop_->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(
+ &source_network_change_notifier_,
+ &net::MockNetworkChangeNotifier::NotifyIPAddressChange));
+ // Pump source thread.
+ source_thread_blocker_->Unblock();
+ source_thread_blocker_->Block();
+ // Pump target thread.
+ target_message_loop_.RunAllPending();
+ }
+
+ // This lives on |source_thread_| but is created/destroyed in the
+ // main thread. As long as we make sure to not modify it from the
+ // main thread while |source_thread_| is running, we shouldn't need
+ // any special synchronization.
+ net::MockNetworkChangeNotifier source_network_change_notifier_;
+ base::Thread source_thread_;
+ MessageLoop* source_message_loop_;
+ scoped_ptr<ThreadBlocker> source_thread_blocker_;
+
+ MessageLoop target_message_loop_;
+ MockNetworkChangeObserver target_observer_;
+
+ scoped_ptr<net::NetworkChangeNotifier> notifier_proxy_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NetworkChangeNotifierProxyTest);
+};
+
+TEST_F(NetworkChangeNotifierProxyTest, Basic) {
+ EXPECT_CALL(target_observer_, OnIPAddressChanged()).Times(1);
+
+ notifier_proxy_->AddObserver(&target_observer_);
+ OnIPAddressChanged();
+ notifier_proxy_->RemoveObserver(&target_observer_);
+}
+
+TEST_F(NetworkChangeNotifierProxyTest, IgnoresEventAfterRemoveObserver) {
+ EXPECT_CALL(target_observer_, OnIPAddressChanged()).Times(0);
+
+ notifier_proxy_->AddObserver(&target_observer_);
+ notifier_proxy_->RemoveObserver(&target_observer_);
+ OnIPAddressChanged();
+}
+
+TEST_F(NetworkChangeNotifierProxyTest, IgnoresEventBeforeRemoveObserver) {
+ EXPECT_CALL(target_observer_, OnIPAddressChanged()).Times(0);
+
+ OnIPAddressChanged();
+ notifier_proxy_->AddObserver(&target_observer_);
+ notifier_proxy_->RemoveObserver(&target_observer_);
+}
+
+TEST_F(NetworkChangeNotifierProxyTest, Multiple) {
+ EXPECT_CALL(target_observer_, OnIPAddressChanged()).Times(0);
+
+ const int kNumObservers = 5;
+ MockNetworkChangeObserver extra_observers[kNumObservers];
+ for (int i = 0; i < kNumObservers; ++i) {
+ EXPECT_CALL(extra_observers[i], OnIPAddressChanged()).Times(1);
+ }
+
+ for (int i = 0; i < kNumObservers; ++i) {
+ notifier_proxy_->AddObserver(&extra_observers[i]);
+ }
+ OnIPAddressChanged();
+ for (int i = 0; i < kNumObservers; ++i) {
+ notifier_proxy_->RemoveObserver(&extra_observers[i]);
+ }
+}
+
+} // namespace
+
+} // namespace browser_sync
diff --git a/chrome/browser/sync/net/network_change_observer_proxy.h b/chrome/browser/sync/net/network_change_observer_proxy.h
index 24eff05..184f5cb 100644
--- a/chrome/browser/sync/net/network_change_observer_proxy.h
+++ b/chrome/browser/sync/net/network_change_observer_proxy.h
@@ -45,6 +45,7 @@
namespace browser_sync {
+// TODO(akalin): Remove use of private inheritance.
class NetworkChangeObserverProxy
: public base::RefCountedThreadSafe<NetworkChangeObserverProxy>,
private net::NetworkChangeNotifier::Observer {
diff --git a/chrome/browser/sync/net/network_change_observer_proxy_unittest.cc b/chrome/browser/sync/net/network_change_observer_proxy_unittest.cc
index adb0f81..ba78e89 100644
--- a/chrome/browser/sync/net/network_change_observer_proxy_unittest.cc
+++ b/chrome/browser/sync/net/network_change_observer_proxy_unittest.cc
@@ -7,28 +7,18 @@
#include "base/basictypes.h"
#include "base/logging.h"
#include "base/message_loop.h"
-#include "base/observer_list.h"
#include "base/ref_counted.h"
#include "base/scoped_ptr.h"
+#include "base/task.h"
#include "base/thread.h"
-#include "base/waitable_event.h"
+#include "chrome/browser/sync/net/mock_network_change_observer.h"
+#include "chrome/browser/sync/net/thread_blocker.h"
#include "net/base/mock_network_change_notifier.h"
#include "net/base/network_change_notifier.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
-namespace browser_sync {
-class ThreadBlocker;
-} // namespace browser_sync
-
-// We manage the lifetime of these classes ourselves.
-
-template <>
-struct RunnableMethodTraits<browser_sync::ThreadBlocker> {
- void RetainCallee(browser_sync::ThreadBlocker*) {}
- void ReleaseCallee(browser_sync::ThreadBlocker*) {}
-};
-
+// We manage the lifetime of net::MockNetworkChangeNotifier ourselves.
template <>
struct RunnableMethodTraits<net::MockNetworkChangeNotifier> {
void RetainCallee(net::MockNetworkChangeNotifier*) {}
@@ -37,50 +27,6 @@ struct RunnableMethodTraits<net::MockNetworkChangeNotifier> {
namespace browser_sync {
-// This class lets you block and unblock a thread at will.
-class ThreadBlocker {
- public:
- // The given thread must already be started and it must outlive this
- // instance.
- explicit ThreadBlocker(base::Thread* target_thread)
- : target_message_loop_(target_thread->message_loop()),
- is_blocked_(false, false), is_finished_blocking_(false, false),
- is_unblocked_(false, false) {
- DCHECK(target_message_loop_);
- }
-
- // When this function returns, the target thread will be blocked
- // until Unblock() is called. Each call to Block() must be matched
- // by a call to Unblock().
- void Block() {
- DCHECK_NE(MessageLoop::current(), target_message_loop_);
- target_message_loop_->PostTask(
- FROM_HERE,
- NewRunnableMethod(this, &ThreadBlocker::BlockOnTargetThread));
- while (!is_blocked_.Wait()) {}
- }
-
- // When this function returns, the target thread is unblocked.
- void Unblock() {
- DCHECK_NE(MessageLoop::current(), target_message_loop_);
- is_finished_blocking_.Signal();
- while (!is_unblocked_.Wait()) {}
- }
-
- private:
- void BlockOnTargetThread() {
- DCHECK_EQ(MessageLoop::current(), target_message_loop_);
- is_blocked_.Signal();
- while (!is_finished_blocking_.Wait()) {}
- is_unblocked_.Signal();
- }
-
- MessageLoop* const target_message_loop_;
- base::WaitableEvent is_blocked_, is_finished_blocking_, is_unblocked_;
-
- DISALLOW_COPY_AND_ASSIGN(ThreadBlocker);
-};
-
namespace {
// Version of NetworkChangeObserverProxy that records on what thread
@@ -113,32 +59,12 @@ class DeleteCheckingNetworkChangeObserverProxy
DISALLOW_COPY_AND_ASSIGN(DeleteCheckingNetworkChangeObserverProxy);
};
-// Mock observer used in the unit tests.
-class MockNetworkChangeObserver
- : public net::NetworkChangeNotifier::Observer {
- public:
- MockNetworkChangeObserver() {}
-
- virtual ~MockNetworkChangeObserver() {}
-
- MOCK_METHOD0(OnIPAddressChanged, void());
-
- private:
- DISALLOW_COPY_AND_ASSIGN(MockNetworkChangeObserver);
-};
-
-// Utility function used by PumpTarget() below.
-void SetTrue(bool* b) {
- *b = true;
-}
-
class NetworkChangeObserverProxyTest : public testing::Test {
protected:
NetworkChangeObserverProxyTest()
: source_thread_("Source Thread"),
source_message_loop_(NULL),
- proxy_deleting_message_loop_(NULL),
- proxy_(NULL) {}
+ proxy_deleting_message_loop_(NULL) {}
virtual ~NetworkChangeObserverProxyTest() {
CHECK(!source_thread_blocker_.get());
@@ -157,7 +83,7 @@ class NetworkChangeObserverProxyTest : public testing::Test {
&target_message_loop_, &proxy_deleting_message_loop_);
}
- // On TearDown, proxy_ must be released and both source and target
+ // On TearDown, |proxy_| must be released and both source and target
// must be pumped.
virtual void TearDown() {
EXPECT_TRUE(proxy_ == NULL);
@@ -177,12 +103,7 @@ class NetworkChangeObserverProxyTest : public testing::Test {
// Pump any events posted on the target thread (which is just the
// main test thread).
void PumpTarget() {
- bool done = false;
- target_message_loop_.PostTask(FROM_HERE,
- NewRunnableFunction(&SetTrue, &done));
- while (!done) {
- target_message_loop_.RunAllPending();
- }
+ target_message_loop_.RunAllPending();
}
// Trigger an "IP address changed" event on the source network
@@ -195,10 +116,10 @@ class NetworkChangeObserverProxyTest : public testing::Test {
&net::MockNetworkChangeNotifier::NotifyIPAddressChange));
}
- // This lives on source_thread_ but is created/destroyed in the main
- // thread. As long as we make sure to not modify it from the main
- // thread while source_thread_ is running, we shouldn't need any
- // special synchronization.
+ // This lives on |source_thread_| but is created/destroyed in the
+ // main thread. As long as we make sure to not modify it from the
+ // main thread while |source_thread_| is running, we shouldn't need
+ // any special synchronization.
net::MockNetworkChangeNotifier source_network_change_notifier_;
base::Thread source_thread_;
MessageLoop* source_message_loop_;
diff --git a/chrome/browser/sync/net/thread_blocker.cc b/chrome/browser/sync/net/thread_blocker.cc
new file mode 100644
index 0000000..df3fafc
--- /dev/null
+++ b/chrome/browser/sync/net/thread_blocker.cc
@@ -0,0 +1,51 @@
+// 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.
+
+#include "chrome/browser/sync/net/thread_blocker.h"
+
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/task.h"
+#include "base/thread.h"
+#include "base/waitable_event.h"
+
+// Since a ThreadBlocker is outlived by its target thread, we don't
+// have to ref-count it.
+template <>
+struct RunnableMethodTraits<browser_sync::ThreadBlocker> {
+ void RetainCallee(browser_sync::ThreadBlocker*) {}
+ void ReleaseCallee(browser_sync::ThreadBlocker*) {}
+};
+
+namespace browser_sync {
+
+ThreadBlocker::ThreadBlocker(base::Thread* target_thread)
+ : target_message_loop_(target_thread->message_loop()),
+ is_blocked_(false, false), is_finished_blocking_(false, false),
+ is_unblocked_(false, false) {
+ DCHECK(target_message_loop_);
+}
+
+void ThreadBlocker::Block() {
+ DCHECK_NE(MessageLoop::current(), target_message_loop_);
+ target_message_loop_->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this, &ThreadBlocker::BlockOnTargetThread));
+ while (!is_blocked_.Wait()) {}
+}
+
+void ThreadBlocker::Unblock() {
+ DCHECK_NE(MessageLoop::current(), target_message_loop_);
+ is_finished_blocking_.Signal();
+ while (!is_unblocked_.Wait()) {}
+}
+
+void ThreadBlocker::BlockOnTargetThread() {
+ DCHECK_EQ(MessageLoop::current(), target_message_loop_);
+ is_blocked_.Signal();
+ while (!is_finished_blocking_.Wait()) {}
+ is_unblocked_.Signal();
+}
+
+} // namespace browser_sync
diff --git a/chrome/browser/sync/net/thread_blocker.h b/chrome/browser/sync/net/thread_blocker.h
new file mode 100644
index 0000000..3548c5d
--- /dev/null
+++ b/chrome/browser/sync/net/thread_blocker.h
@@ -0,0 +1,51 @@
+// 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.
+
+#ifndef CHROME_BROWSER_SYNC_NET_THREAD_BLOCKER_H_
+#define CHROME_BROWSER_SYNC_NET_THREAD_BLOCKER_H_
+
+// This class (mainly used for testing) lets you block and unblock a
+// thread at will.
+//
+// TODO(akalin): Consider moving this to base/ as this class is not
+// sync-specific (ask darin about it).
+
+#include "base/basictypes.h"
+#include "base/waitable_event.h"
+
+class MessageLoop;
+
+namespace base {
+class Thread;
+} // namespace base
+
+namespace browser_sync {
+
+class ThreadBlocker {
+ public:
+ // The given thread must already be started and it must outlive this
+ // instance.
+ explicit ThreadBlocker(base::Thread* target_thread);
+
+ // When this function returns, the target thread will be blocked
+ // until Unblock() is called. Each call to Block() must be matched
+ // by a call to Unblock().
+ void Block();
+
+ // When this function returns, the target thread is unblocked.
+ void Unblock();
+
+ private:
+ // On the target thread, blocks until Unblock() is called.
+ void BlockOnTargetThread();
+
+ MessageLoop* const target_message_loop_;
+ base::WaitableEvent is_blocked_, is_finished_blocking_, is_unblocked_;
+
+ DISALLOW_COPY_AND_ASSIGN(ThreadBlocker);
+};
+
+} // namespace browser_sync
+
+#endif // CHROME_BROWSER_SYNC_NET_THREAD_BLOCKER_H_
diff --git a/chrome/browser/sync/net/thread_blocker_unittest.cc b/chrome/browser/sync/net/thread_blocker_unittest.cc
new file mode 100644
index 0000000..4587e30
--- /dev/null
+++ b/chrome/browser/sync/net/thread_blocker_unittest.cc
@@ -0,0 +1,113 @@
+// 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.
+
+#include "chrome/browser/sync/net/thread_blocker.h"
+
+#include "base/basictypes.h"
+#include "base/lock.h"
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/scoped_ptr.h"
+#include "base/task.h"
+#include "base/thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace browser_sync {
+class Flag;
+}; // namespace browser_sync
+
+// We manage the lifetime of browser_sync::Flag ourselves.
+template <>
+struct RunnableMethodTraits<browser_sync::Flag> {
+ void RetainCallee(browser_sync::Flag*) {}
+ void ReleaseCallee(browser_sync::Flag*) {}
+};
+
+namespace browser_sync {
+
+// Utility class that is basically just a thread-safe boolean.
+class Flag {
+ public:
+ Flag() : flag_(false) {}
+
+ bool IsSet() const {
+ AutoLock auto_lock(lock_);
+ return flag_;
+ }
+
+ void Set() {
+ AutoLock auto_lock(lock_);
+ flag_ = true;
+ }
+
+ void Unset() {
+ AutoLock auto_lock(lock_);
+ flag_ = false;
+ }
+
+ private:
+ mutable Lock lock_;
+ bool flag_;
+
+ DISALLOW_COPY_AND_ASSIGN(Flag);
+};
+
+namespace {
+
+class ThreadBlockerTest : public testing::Test {
+ protected:
+ ThreadBlockerTest() : target_thread_("Target Thread") {}
+
+ virtual ~ThreadBlockerTest() {
+ CHECK(!thread_blocker_.get());
+ }
+
+ virtual void SetUp() {
+ CHECK(target_thread_.Start());
+ thread_blocker_.reset(new ThreadBlocker(&target_thread_));
+ }
+
+ virtual void TearDown() {
+ target_thread_.Stop();
+ thread_blocker_.reset();
+ }
+
+ base::Thread target_thread_;
+ scoped_ptr<ThreadBlocker> thread_blocker_;
+ Flag flag_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ThreadBlockerTest);
+};
+
+TEST_F(ThreadBlockerTest, Basic) {
+ thread_blocker_->Block();
+ target_thread_.message_loop()->PostTask(
+ FROM_HERE, NewRunnableMethod(&flag_, &Flag::Set));
+ EXPECT_FALSE(flag_.IsSet());
+ thread_blocker_->Unblock();
+ // Need to block again to make sure this thread waits for the posted
+ // method to run.
+ thread_blocker_->Block();
+ EXPECT_TRUE(flag_.IsSet());
+ thread_blocker_->Unblock();
+}
+
+TEST_F(ThreadBlockerTest, SetUnset) {
+ thread_blocker_->Block();
+ target_thread_.message_loop()->PostTask(
+ FROM_HERE, NewRunnableMethod(&flag_, &Flag::Set));
+ target_thread_.message_loop()->PostTask(
+ FROM_HERE, NewRunnableMethod(&flag_, &Flag::Unset));
+ EXPECT_FALSE(flag_.IsSet());
+ thread_blocker_->Unblock();
+ // Need to block again here too.
+ thread_blocker_->Block();
+ EXPECT_FALSE(flag_.IsSet());
+ thread_blocker_->Unblock();
+}
+
+} // namespace
+
+} // namespace browser_sync