|  | // Copyright (c) 2012 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 "base/observer_list.h" | 
|  | #include "base/observer_list_threadsafe.h" | 
|  |  | 
|  | #include <vector> | 
|  |  | 
|  | #include "base/compiler_specific.h" | 
|  | #include "base/memory/weak_ptr.h" | 
|  | #include "base/message_loop.h" | 
|  | #include "base/threading/platform_thread.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  |  | 
|  | using base::PlatformThread; | 
|  | using base::Time; | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | class Foo { | 
|  | public: | 
|  | virtual void Observe(int x) = 0; | 
|  | virtual ~Foo() {} | 
|  | }; | 
|  |  | 
|  | class Adder : public Foo { | 
|  | public: | 
|  | explicit Adder(int scaler) : total(0), scaler_(scaler) {} | 
|  | virtual void Observe(int x) override { | 
|  | total += x * scaler_; | 
|  | } | 
|  | virtual ~Adder() {} | 
|  | int total; | 
|  |  | 
|  | private: | 
|  | int scaler_; | 
|  | }; | 
|  |  | 
|  | class Disrupter : public Foo { | 
|  | public: | 
|  | Disrupter(ObserverList<Foo>* list, Foo* doomed) | 
|  | : list_(list), | 
|  | doomed_(doomed) { | 
|  | } | 
|  | virtual ~Disrupter() {} | 
|  | virtual void Observe(int x) override { | 
|  | list_->RemoveObserver(doomed_); | 
|  | } | 
|  |  | 
|  | private: | 
|  | ObserverList<Foo>* list_; | 
|  | Foo* doomed_; | 
|  | }; | 
|  |  | 
|  | class ThreadSafeDisrupter : public Foo { | 
|  | public: | 
|  | ThreadSafeDisrupter(ObserverListThreadSafe<Foo>* list, Foo* doomed) | 
|  | : list_(list), | 
|  | doomed_(doomed) { | 
|  | } | 
|  | virtual ~ThreadSafeDisrupter() {} | 
|  | virtual void Observe(int x) override { | 
|  | list_->RemoveObserver(doomed_); | 
|  | } | 
|  |  | 
|  | private: | 
|  | ObserverListThreadSafe<Foo>* list_; | 
|  | Foo* doomed_; | 
|  | }; | 
|  |  | 
|  | template <typename ObserverListType> | 
|  | class AddInObserve : public Foo { | 
|  | public: | 
|  | explicit AddInObserve(ObserverListType* observer_list) | 
|  | : added(false), | 
|  | observer_list(observer_list), | 
|  | adder(1) { | 
|  | } | 
|  |  | 
|  | virtual void Observe(int x) override { | 
|  | if (!added) { | 
|  | added = true; | 
|  | observer_list->AddObserver(&adder); | 
|  | } | 
|  | } | 
|  |  | 
|  | bool added; | 
|  | ObserverListType* observer_list; | 
|  | Adder adder; | 
|  | }; | 
|  |  | 
|  |  | 
|  | static const int kThreadRunTime = 2000;  // ms to run the multi-threaded test. | 
|  |  | 
|  | // A thread for use in the ThreadSafeObserver test | 
|  | // which will add and remove itself from the notification | 
|  | // list repeatedly. | 
|  | class AddRemoveThread : public PlatformThread::Delegate, | 
|  | public Foo { | 
|  | public: | 
|  | AddRemoveThread(ObserverListThreadSafe<Foo>* list, bool notify) | 
|  | : list_(list), | 
|  | loop_(NULL), | 
|  | in_list_(false), | 
|  | start_(Time::Now()), | 
|  | count_observes_(0), | 
|  | count_addtask_(0), | 
|  | do_notifies_(notify), | 
|  | ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { | 
|  | } | 
|  |  | 
|  | virtual ~AddRemoveThread() { | 
|  | } | 
|  |  | 
|  | virtual void ThreadMain() override { | 
|  | loop_ = new MessageLoop();  // Fire up a message loop. | 
|  | loop_->PostTask( | 
|  | FROM_HERE, | 
|  | base::Bind(&AddRemoveThread::AddTask, weak_factory_.GetWeakPtr())); | 
|  | loop_->Run(); | 
|  | //LOG(ERROR) << "Loop 0x" << std::hex << loop_ << " done. " << | 
|  | //    count_observes_ << ", " << count_addtask_; | 
|  | delete loop_; | 
|  | loop_ = reinterpret_cast<MessageLoop*>(static_cast<size_t>(0xdeadbeef)); | 
|  | delete this; | 
|  | } | 
|  |  | 
|  | bool Started() { | 
|  | return loop_ != NULL; | 
|  | } | 
|  |  | 
|  | // This task just keeps posting to itself in an attempt | 
|  | // to race with the notifier. | 
|  | void AddTask() { | 
|  | count_addtask_++; | 
|  |  | 
|  | if ((Time::Now() - start_).InMilliseconds() > kThreadRunTime) { | 
|  | VLOG(1) << "DONE!"; | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (!in_list_) { | 
|  | list_->AddObserver(this); | 
|  | in_list_ = true; | 
|  | } | 
|  |  | 
|  | if (do_notifies_) { | 
|  | list_->Notify(&Foo::Observe, 10); | 
|  | } | 
|  |  | 
|  | loop_->PostTask( | 
|  | FROM_HERE, | 
|  | base::Bind(&AddRemoveThread::AddTask, weak_factory_.GetWeakPtr())); | 
|  | } | 
|  |  | 
|  | void Quit() { | 
|  | loop_->PostTask(FROM_HERE, MessageLoop::QuitClosure()); | 
|  | } | 
|  |  | 
|  | virtual void Observe(int x) override { | 
|  | count_observes_++; | 
|  |  | 
|  | // If we're getting called after we removed ourselves from | 
|  | // the list, that is very bad! | 
|  | DCHECK(in_list_); | 
|  |  | 
|  | // This callback should fire on the appropriate thread | 
|  | EXPECT_EQ(loop_, MessageLoop::current()); | 
|  |  | 
|  | list_->RemoveObserver(this); | 
|  | in_list_ = false; | 
|  | } | 
|  |  | 
|  | private: | 
|  | ObserverListThreadSafe<Foo>* list_; | 
|  | MessageLoop* loop_; | 
|  | bool in_list_;        // Are we currently registered for notifications. | 
|  | // in_list_ is only used on |this| thread. | 
|  | Time start_;          // The time we started the test. | 
|  |  | 
|  | int count_observes_;  // Number of times we observed. | 
|  | int count_addtask_;   // Number of times thread AddTask was called | 
|  | bool do_notifies_;    // Whether these threads should do notifications. | 
|  |  | 
|  | base::WeakPtrFactory<AddRemoveThread> weak_factory_; | 
|  | }; | 
|  |  | 
|  | TEST(ObserverListTest, BasicTest) { | 
|  | ObserverList<Foo> observer_list; | 
|  | Adder a(1), b(-1), c(1), d(-1), e(-1); | 
|  | Disrupter evil(&observer_list, &c); | 
|  |  | 
|  | observer_list.AddObserver(&a); | 
|  | observer_list.AddObserver(&b); | 
|  |  | 
|  | FOR_EACH_OBSERVER(Foo, observer_list, Observe(10)); | 
|  |  | 
|  | observer_list.AddObserver(&evil); | 
|  | observer_list.AddObserver(&c); | 
|  | observer_list.AddObserver(&d); | 
|  |  | 
|  | // Removing an observer not in the list should do nothing. | 
|  | observer_list.RemoveObserver(&e); | 
|  |  | 
|  | FOR_EACH_OBSERVER(Foo, observer_list, Observe(10)); | 
|  |  | 
|  | EXPECT_EQ(20, a.total); | 
|  | EXPECT_EQ(-20, b.total); | 
|  | EXPECT_EQ(0, c.total); | 
|  | EXPECT_EQ(-10, d.total); | 
|  | EXPECT_EQ(0, e.total); | 
|  | } | 
|  |  | 
|  | TEST(ObserverListThreadSafeTest, BasicTest) { | 
|  | MessageLoop loop; | 
|  |  | 
|  | scoped_refptr<ObserverListThreadSafe<Foo> > observer_list( | 
|  | new ObserverListThreadSafe<Foo>); | 
|  | Adder a(1); | 
|  | Adder b(-1); | 
|  | Adder c(1); | 
|  | Adder d(-1); | 
|  | ThreadSafeDisrupter evil(observer_list.get(), &c); | 
|  |  | 
|  | observer_list->AddObserver(&a); | 
|  | observer_list->AddObserver(&b); | 
|  |  | 
|  | observer_list->Notify(&Foo::Observe, 10); | 
|  | loop.RunUntilIdle(); | 
|  |  | 
|  | observer_list->AddObserver(&evil); | 
|  | observer_list->AddObserver(&c); | 
|  | observer_list->AddObserver(&d); | 
|  |  | 
|  | observer_list->Notify(&Foo::Observe, 10); | 
|  | loop.RunUntilIdle(); | 
|  |  | 
|  | EXPECT_EQ(20, a.total); | 
|  | EXPECT_EQ(-20, b.total); | 
|  | EXPECT_EQ(0, c.total); | 
|  | EXPECT_EQ(-10, d.total); | 
|  | } | 
|  |  | 
|  | TEST(ObserverListThreadSafeTest, RemoveObserver) { | 
|  | MessageLoop loop; | 
|  |  | 
|  | scoped_refptr<ObserverListThreadSafe<Foo> > observer_list( | 
|  | new ObserverListThreadSafe<Foo>); | 
|  | Adder a(1), b(1); | 
|  |  | 
|  | // A workaround for the compiler bug. See http://crbug.com/121960. | 
|  | EXPECT_NE(&a, &b); | 
|  |  | 
|  | // Should do nothing. | 
|  | observer_list->RemoveObserver(&a); | 
|  | observer_list->RemoveObserver(&b); | 
|  |  | 
|  | observer_list->Notify(&Foo::Observe, 10); | 
|  | loop.RunUntilIdle(); | 
|  |  | 
|  | EXPECT_EQ(0, a.total); | 
|  | EXPECT_EQ(0, b.total); | 
|  |  | 
|  | observer_list->AddObserver(&a); | 
|  |  | 
|  | // Should also do nothing. | 
|  | observer_list->RemoveObserver(&b); | 
|  |  | 
|  | observer_list->Notify(&Foo::Observe, 10); | 
|  | loop.RunUntilIdle(); | 
|  |  | 
|  | EXPECT_EQ(10, a.total); | 
|  | EXPECT_EQ(0, b.total); | 
|  | } | 
|  |  | 
|  | TEST(ObserverListThreadSafeTest, WithoutMessageLoop) { | 
|  | scoped_refptr<ObserverListThreadSafe<Foo> > observer_list( | 
|  | new ObserverListThreadSafe<Foo>); | 
|  |  | 
|  | Adder a(1), b(1), c(1); | 
|  |  | 
|  | // No MessageLoop, so these should not be added. | 
|  | observer_list->AddObserver(&a); | 
|  | observer_list->AddObserver(&b); | 
|  |  | 
|  | { | 
|  | // Add c when there's a loop. | 
|  | MessageLoop loop; | 
|  | observer_list->AddObserver(&c); | 
|  |  | 
|  | observer_list->Notify(&Foo::Observe, 10); | 
|  | loop.RunUntilIdle(); | 
|  |  | 
|  | EXPECT_EQ(0, a.total); | 
|  | EXPECT_EQ(0, b.total); | 
|  | EXPECT_EQ(10, c.total); | 
|  |  | 
|  | // Now add a when there's a loop. | 
|  | observer_list->AddObserver(&a); | 
|  |  | 
|  | // Remove c when there's a loop. | 
|  | observer_list->RemoveObserver(&c); | 
|  |  | 
|  | // Notify again. | 
|  | observer_list->Notify(&Foo::Observe, 20); | 
|  | loop.RunUntilIdle(); | 
|  |  | 
|  | EXPECT_EQ(20, a.total); | 
|  | EXPECT_EQ(0, b.total); | 
|  | EXPECT_EQ(10, c.total); | 
|  | } | 
|  |  | 
|  | // Removing should always succeed with or without a loop. | 
|  | observer_list->RemoveObserver(&a); | 
|  |  | 
|  | // Notifying should not fail but should also be a no-op. | 
|  | MessageLoop loop; | 
|  | observer_list->AddObserver(&b); | 
|  | observer_list->Notify(&Foo::Observe, 30); | 
|  | loop.RunUntilIdle(); | 
|  |  | 
|  | EXPECT_EQ(20, a.total); | 
|  | EXPECT_EQ(30, b.total); | 
|  | EXPECT_EQ(10, c.total); | 
|  | } | 
|  |  | 
|  | class FooRemover : public Foo { | 
|  | public: | 
|  | explicit FooRemover(ObserverListThreadSafe<Foo>* list) : list_(list) {} | 
|  | virtual ~FooRemover() {} | 
|  |  | 
|  | void AddFooToRemove(Foo* foo) { | 
|  | foos_.push_back(foo); | 
|  | } | 
|  |  | 
|  | virtual void Observe(int x) override { | 
|  | std::vector<Foo*> tmp; | 
|  | tmp.swap(foos_); | 
|  | for (std::vector<Foo*>::iterator it = tmp.begin(); | 
|  | it != tmp.end(); ++it) { | 
|  | list_->RemoveObserver(*it); | 
|  | } | 
|  | } | 
|  |  | 
|  | private: | 
|  | const scoped_refptr<ObserverListThreadSafe<Foo> > list_; | 
|  | std::vector<Foo*> foos_; | 
|  | }; | 
|  |  | 
|  | TEST(ObserverListThreadSafeTest, RemoveMultipleObservers) { | 
|  | MessageLoop loop; | 
|  | scoped_refptr<ObserverListThreadSafe<Foo> > observer_list( | 
|  | new ObserverListThreadSafe<Foo>); | 
|  |  | 
|  | FooRemover a(observer_list); | 
|  | Adder b(1); | 
|  |  | 
|  | observer_list->AddObserver(&a); | 
|  | observer_list->AddObserver(&b); | 
|  |  | 
|  | a.AddFooToRemove(&a); | 
|  | a.AddFooToRemove(&b); | 
|  |  | 
|  | observer_list->Notify(&Foo::Observe, 1); | 
|  | loop.RunUntilIdle(); | 
|  | } | 
|  |  | 
|  | // A test driver for a multi-threaded notification loop.  Runs a number | 
|  | // of observer threads, each of which constantly adds/removes itself | 
|  | // from the observer list.  Optionally, if cross_thread_notifies is set | 
|  | // to true, the observer threads will also trigger notifications to | 
|  | // all observers. | 
|  | static void ThreadSafeObserverHarness(int num_threads, | 
|  | bool cross_thread_notifies) { | 
|  | MessageLoop loop; | 
|  |  | 
|  | const int kMaxThreads = 15; | 
|  | num_threads = num_threads > kMaxThreads ? kMaxThreads : num_threads; | 
|  |  | 
|  | scoped_refptr<ObserverListThreadSafe<Foo> > observer_list( | 
|  | new ObserverListThreadSafe<Foo>); | 
|  | Adder a(1); | 
|  | Adder b(-1); | 
|  | Adder c(1); | 
|  | Adder d(-1); | 
|  |  | 
|  | observer_list->AddObserver(&a); | 
|  | observer_list->AddObserver(&b); | 
|  |  | 
|  | AddRemoveThread* threaded_observer[kMaxThreads]; | 
|  | base::PlatformThreadHandle threads[kMaxThreads]; | 
|  | for (int index = 0; index < num_threads; index++) { | 
|  | threaded_observer[index] = new AddRemoveThread(observer_list.get(), false); | 
|  | EXPECT_TRUE(PlatformThread::Create(0, | 
|  | threaded_observer[index], &threads[index])); | 
|  | } | 
|  |  | 
|  | // Wait for all threads to start do avoid NULL ptr dereference | 
|  | // when calling Quit() | 
|  | for (int index = 0; index < num_threads; ++index) { | 
|  | while (!threaded_observer[index]->Started()) { | 
|  | base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(50)); | 
|  | } | 
|  | } | 
|  |  | 
|  | Time start = Time::Now(); | 
|  | while (true) { | 
|  | if ((Time::Now() - start).InMilliseconds() > kThreadRunTime) | 
|  | break; | 
|  |  | 
|  | observer_list->Notify(&Foo::Observe, 10); | 
|  |  | 
|  | loop.RunUntilIdle(); | 
|  | } | 
|  |  | 
|  | for (int index = 0; index < num_threads; index++) { | 
|  | threaded_observer[index]->Quit(); | 
|  | PlatformThread::Join(threads[index]); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST(ObserverListThreadSafeTest, CrossThreadObserver) { | 
|  | // Use 7 observer threads.  Notifications only come from | 
|  | // the main thread. | 
|  | ThreadSafeObserverHarness(7, false); | 
|  | } | 
|  |  | 
|  | TEST(ObserverListThreadSafeTest, CrossThreadNotifications) { | 
|  | // Use 3 observer threads.  Notifications will fire from | 
|  | // the main thread and all 3 observer threads. | 
|  | ThreadSafeObserverHarness(3, true); | 
|  | } | 
|  |  | 
|  | TEST(ObserverListThreadSafeTest, OutlivesMessageLoop) { | 
|  | MessageLoop* loop = new MessageLoop; | 
|  | scoped_refptr<ObserverListThreadSafe<Foo> > observer_list( | 
|  | new ObserverListThreadSafe<Foo>); | 
|  |  | 
|  | Adder a(1); | 
|  | observer_list->AddObserver(&a); | 
|  | delete loop; | 
|  | // Test passes if we don't crash here. | 
|  | observer_list->Notify(&Foo::Observe, 1); | 
|  | } | 
|  |  | 
|  | TEST(ObserverListTest, Existing) { | 
|  | ObserverList<Foo> observer_list(ObserverList<Foo>::NOTIFY_EXISTING_ONLY); | 
|  | Adder a(1); | 
|  | AddInObserve<ObserverList<Foo> > b(&observer_list); | 
|  |  | 
|  | observer_list.AddObserver(&a); | 
|  | observer_list.AddObserver(&b); | 
|  |  | 
|  | FOR_EACH_OBSERVER(Foo, observer_list, Observe(1)); | 
|  |  | 
|  | EXPECT_TRUE(b.added); | 
|  | // B's adder should not have been notified because it was added during | 
|  | // notificaiton. | 
|  | EXPECT_EQ(0, b.adder.total); | 
|  |  | 
|  | // Notify again to make sure b's adder is notified. | 
|  | FOR_EACH_OBSERVER(Foo, observer_list, Observe(1)); | 
|  | EXPECT_EQ(1, b.adder.total); | 
|  | } | 
|  |  | 
|  | // Same as above, but for ObserverListThreadSafe | 
|  | TEST(ObserverListThreadSafeTest, Existing) { | 
|  | MessageLoop loop; | 
|  | scoped_refptr<ObserverListThreadSafe<Foo> > observer_list( | 
|  | new ObserverListThreadSafe<Foo>(ObserverList<Foo>::NOTIFY_EXISTING_ONLY)); | 
|  | Adder a(1); | 
|  | AddInObserve<ObserverListThreadSafe<Foo> > b(observer_list.get()); | 
|  |  | 
|  | observer_list->AddObserver(&a); | 
|  | observer_list->AddObserver(&b); | 
|  |  | 
|  | observer_list->Notify(&Foo::Observe, 1); | 
|  | loop.RunUntilIdle(); | 
|  |  | 
|  | EXPECT_TRUE(b.added); | 
|  | // B's adder should not have been notified because it was added during | 
|  | // notificaiton. | 
|  | EXPECT_EQ(0, b.adder.total); | 
|  |  | 
|  | // Notify again to make sure b's adder is notified. | 
|  | observer_list->Notify(&Foo::Observe, 1); | 
|  | loop.RunUntilIdle(); | 
|  | EXPECT_EQ(1, b.adder.total); | 
|  | } | 
|  |  | 
|  | class AddInClearObserve : public Foo { | 
|  | public: | 
|  | explicit AddInClearObserve(ObserverList<Foo>* list) | 
|  | : list_(list), added_(false), adder_(1) {} | 
|  |  | 
|  | virtual void Observe(int /* x */) override { | 
|  | list_->Clear(); | 
|  | list_->AddObserver(&adder_); | 
|  | added_ = true; | 
|  | } | 
|  |  | 
|  | bool added() const { return added_; } | 
|  | const Adder& adder() const { return adder_; } | 
|  |  | 
|  | private: | 
|  | ObserverList<Foo>* const list_; | 
|  |  | 
|  | bool added_; | 
|  | Adder adder_; | 
|  | }; | 
|  |  | 
|  | TEST(ObserverListTest, ClearNotifyAll) { | 
|  | ObserverList<Foo> observer_list; | 
|  | AddInClearObserve a(&observer_list); | 
|  |  | 
|  | observer_list.AddObserver(&a); | 
|  |  | 
|  | FOR_EACH_OBSERVER(Foo, observer_list, Observe(1)); | 
|  | EXPECT_TRUE(a.added()); | 
|  | EXPECT_EQ(1, a.adder().total) | 
|  | << "Adder should observe once and have sum of 1."; | 
|  | } | 
|  |  | 
|  | TEST(ObserverListTest, ClearNotifyExistingOnly) { | 
|  | ObserverList<Foo> observer_list(ObserverList<Foo>::NOTIFY_EXISTING_ONLY); | 
|  | AddInClearObserve a(&observer_list); | 
|  |  | 
|  | observer_list.AddObserver(&a); | 
|  |  | 
|  | FOR_EACH_OBSERVER(Foo, observer_list, Observe(1)); | 
|  | EXPECT_TRUE(a.added()); | 
|  | EXPECT_EQ(0, a.adder().total) | 
|  | << "Adder should not observe, so sum should still be 0."; | 
|  | } | 
|  |  | 
|  | class ListDestructor : public Foo { | 
|  | public: | 
|  | explicit ListDestructor(ObserverList<Foo>* list) : list_(list) {} | 
|  | virtual ~ListDestructor() {} | 
|  |  | 
|  | virtual void Observe(int x) override { | 
|  | delete list_; | 
|  | } | 
|  |  | 
|  | private: | 
|  | ObserverList<Foo>* list_; | 
|  | }; | 
|  |  | 
|  |  | 
|  | TEST(ObserverListTest, IteratorOutlivesList) { | 
|  | ObserverList<Foo>* observer_list = new ObserverList<Foo>; | 
|  | ListDestructor a(observer_list); | 
|  | observer_list->AddObserver(&a); | 
|  |  | 
|  | FOR_EACH_OBSERVER(Foo, *observer_list, Observe(0)); | 
|  | // If this test fails, there'll be Valgrind errors when this function goes out | 
|  | // of scope. | 
|  | } | 
|  |  | 
|  | }  // namespace |