| Index: base/observer_list_unittest.cc
|
| diff --git a/base/observer_list_unittest.cc b/base/observer_list_unittest.cc
|
| index c5e556bd9da4e76f65ad9965fb256d02932e672b..cc7889f0488706ce669819d6acbd211d2d135371 100644
|
| --- a/base/observer_list_unittest.cc
|
| +++ b/base/observer_list_unittest.cc
|
| @@ -5,13 +5,21 @@
|
| #include "base/observer_list.h"
|
| #include "base/observer_list_threadsafe.h"
|
|
|
| +#include <utility>
|
| #include <vector>
|
|
|
| +#include "base/bind.h"
|
| #include "base/compiler_specific.h"
|
| #include "base/location.h"
|
| #include "base/memory/weak_ptr.h"
|
| #include "base/run_loop.h"
|
| +#include "base/sequenced_task_runner.h"
|
| #include "base/single_thread_task_runner.h"
|
| +#include "base/synchronization/waitable_event.h"
|
| +#include "base/task_scheduler/post_task.h"
|
| +#include "base/task_scheduler/task_scheduler.h"
|
| +#include "base/test/scoped_task_environment.h"
|
| +#include "base/test/scoped_task_scheduler.h"
|
| #include "base/threading/platform_thread.h"
|
| #include "testing/gtest/include/gtest/gtest.h"
|
|
|
| @@ -65,20 +73,6 @@ class Disrupter : public Foo {
|
| bool remove_self_;
|
| };
|
|
|
| -class ThreadSafeDisrupter : public Foo {
|
| - public:
|
| - ThreadSafeDisrupter(ObserverListThreadSafe<Foo>* list, Foo* doomed)
|
| - : list_(list),
|
| - doomed_(doomed) {
|
| - }
|
| - ~ThreadSafeDisrupter() override {}
|
| - void Observe(int x) override { list_->RemoveObserver(doomed_); }
|
| -
|
| - private:
|
| - ObserverListThreadSafe<Foo>* list_;
|
| - Foo* doomed_;
|
| -};
|
| -
|
| template <typename ObserverListType>
|
| class AddInObserve : public Foo {
|
| public:
|
| @@ -276,7 +270,6 @@ TEST(ObserverListThreadSafeTest, BasicTest) {
|
| Adder b(-1);
|
| Adder c(1);
|
| Adder d(-1);
|
| - ThreadSafeDisrupter evil(observer_list.get(), &c);
|
|
|
| observer_list->AddObserver(&a);
|
| observer_list->AddObserver(&b);
|
| @@ -284,11 +277,11 @@ TEST(ObserverListThreadSafeTest, BasicTest) {
|
| observer_list->Notify(FROM_HERE, &Foo::Observe, 10);
|
| RunLoop().RunUntilIdle();
|
|
|
| - observer_list->AddObserver(&evil);
|
| observer_list->AddObserver(&c);
|
| observer_list->AddObserver(&d);
|
|
|
| observer_list->Notify(FROM_HERE, &Foo::Observe, 10);
|
| + observer_list->RemoveObserver(&c);
|
| RunLoop().RunUntilIdle();
|
|
|
| EXPECT_EQ(20, a.total);
|
| @@ -329,18 +322,18 @@ TEST(ObserverListThreadSafeTest, RemoveObserver) {
|
| EXPECT_EQ(0, b.total);
|
| }
|
|
|
| -TEST(ObserverListThreadSafeTest, WithoutMessageLoop) {
|
| +TEST(ObserverListThreadSafeTest, WithoutSequence) {
|
| scoped_refptr<ObserverListThreadSafe<Foo> > observer_list(
|
| new ObserverListThreadSafe<Foo>);
|
|
|
| Adder a(1), b(1), c(1);
|
|
|
| - // No MessageLoop, so these should not be added.
|
| + // No sequence, so these should not be added.
|
| observer_list->AddObserver(&a);
|
| observer_list->AddObserver(&b);
|
|
|
| {
|
| - // Add c when there's a loop.
|
| + // Add c when there's a sequence.
|
| MessageLoop loop;
|
| observer_list->AddObserver(&c);
|
|
|
| @@ -351,10 +344,10 @@ TEST(ObserverListThreadSafeTest, WithoutMessageLoop) {
|
| EXPECT_EQ(0, b.total);
|
| EXPECT_EQ(10, c.total);
|
|
|
| - // Now add a when there's a loop.
|
| + // Now add a when there's a sequence.
|
| observer_list->AddObserver(&a);
|
|
|
| - // Remove c when there's a loop.
|
| + // Remove c when there's a sequence.
|
| observer_list->RemoveObserver(&c);
|
|
|
| // Notify again.
|
| @@ -366,7 +359,7 @@ TEST(ObserverListThreadSafeTest, WithoutMessageLoop) {
|
| EXPECT_EQ(10, c.total);
|
| }
|
|
|
| - // Removing should always succeed with or without a loop.
|
| + // Removing should always succeed with or without a sequence.
|
| observer_list->RemoveObserver(&a);
|
|
|
| // Notifying should not fail but should also be a no-op.
|
| @@ -491,6 +484,135 @@ TEST(ObserverListThreadSafeTest, OutlivesMessageLoop) {
|
| observer_list->Notify(FROM_HERE, &Foo::Observe, 1);
|
| }
|
|
|
| +namespace {
|
| +
|
| +class SequenceVerificationObserver : public Foo {
|
| + public:
|
| + explicit SequenceVerificationObserver(
|
| + scoped_refptr<SequencedTaskRunner> task_runner)
|
| + : task_runner_(std::move(task_runner)) {}
|
| + ~SequenceVerificationObserver() override = default;
|
| +
|
| + void Observe(int x) override {
|
| + called_on_valid_sequence_ = task_runner_->RunsTasksOnCurrentThread();
|
| + }
|
| +
|
| + bool called_on_valid_sequence() const { return called_on_valid_sequence_; }
|
| +
|
| + private:
|
| + const scoped_refptr<SequencedTaskRunner> task_runner_;
|
| + bool called_on_valid_sequence_ = false;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(SequenceVerificationObserver);
|
| +};
|
| +
|
| +} // namespace
|
| +
|
| +// Verify that observers are notified on the correct sequence.
|
| +TEST(ObserverListThreadSafeTest, NotificationOnValidSequence) {
|
| + test::ScopedTaskEnvironment scoped_task_environment;
|
| +
|
| + auto task_runner_1 = CreateSequencedTaskRunnerWithTraits(TaskTraits());
|
| + auto task_runner_2 = CreateSequencedTaskRunnerWithTraits(TaskTraits());
|
| +
|
| + auto observer_list = make_scoped_refptr(new ObserverListThreadSafe<Foo>());
|
| +
|
| + SequenceVerificationObserver observer_1(task_runner_1);
|
| + SequenceVerificationObserver observer_2(task_runner_2);
|
| +
|
| + task_runner_1->PostTask(
|
| + FROM_HERE, Bind(&ObserverListThreadSafe<Foo>::AddObserver, observer_list,
|
| + Unretained(&observer_1)));
|
| + task_runner_2->PostTask(
|
| + FROM_HERE, Bind(&ObserverListThreadSafe<Foo>::AddObserver, observer_list,
|
| + Unretained(&observer_2)));
|
| +
|
| + TaskScheduler::GetInstance()->FlushForTesting();
|
| +
|
| + observer_list->Notify(FROM_HERE, &Foo::Observe, 1);
|
| +
|
| + TaskScheduler::GetInstance()->FlushForTesting();
|
| +
|
| + EXPECT_TRUE(observer_1.called_on_valid_sequence());
|
| + EXPECT_TRUE(observer_2.called_on_valid_sequence());
|
| +}
|
| +
|
| +// Verify that when an observer is added to a NOTIFY_ALL ObserverListThreadSafe
|
| +// from a notification, it is itself notified.
|
| +TEST(ObserverListThreadSafeTest, AddObserverFromNotificationNotifyAll) {
|
| + test::ScopedTaskEnvironment scoped_task_environment;
|
| + auto observer_list = make_scoped_refptr(new ObserverListThreadSafe<Foo>());
|
| +
|
| + Adder observer_added_from_notification(1);
|
| +
|
| + AddInObserve<ObserverListThreadSafe<Foo>> initial_observer(
|
| + observer_list.get());
|
| + initial_observer.SetToAdd(&observer_added_from_notification);
|
| + observer_list->AddObserver(&initial_observer);
|
| +
|
| + observer_list->Notify(FROM_HERE, &Foo::Observe, 1);
|
| +
|
| + base::RunLoop().RunUntilIdle();
|
| +
|
| + EXPECT_EQ(1, observer_added_from_notification.GetValue());
|
| +}
|
| +
|
| +namespace {
|
| +
|
| +class RemoveWhileNotificationIsRunningObserver : public Foo {
|
| + public:
|
| + RemoveWhileNotificationIsRunningObserver()
|
| + : notification_running_(WaitableEvent::ResetPolicy::AUTOMATIC,
|
| + WaitableEvent::InitialState::NOT_SIGNALED),
|
| + barrier_(WaitableEvent::ResetPolicy::AUTOMATIC,
|
| + WaitableEvent::InitialState::NOT_SIGNALED) {}
|
| + ~RemoveWhileNotificationIsRunningObserver() override = default;
|
| +
|
| + void Observe(int x) override {
|
| + notification_running_.Signal();
|
| + barrier_.Wait();
|
| + }
|
| +
|
| + void WaitForNotificationRunning() { notification_running_.Wait(); }
|
| + void Unblock() { barrier_.Signal(); }
|
| +
|
| + private:
|
| + WaitableEvent notification_running_;
|
| + WaitableEvent barrier_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(RemoveWhileNotificationIsRunningObserver);
|
| +};
|
| +
|
| +} // namespace
|
| +
|
| +// Verify that there is no crash when an observer is removed while it is being
|
| +// notified.
|
| +TEST(ObserverListThreadSafeTest, RemoveWhileNotificationIsRunning) {
|
| + auto observer_list = make_scoped_refptr(new ObserverListThreadSafe<Foo>());
|
| + RemoveWhileNotificationIsRunningObserver observer;
|
| +
|
| + WaitableEvent task_running(WaitableEvent::ResetPolicy::AUTOMATIC,
|
| + WaitableEvent::InitialState::NOT_SIGNALED);
|
| + WaitableEvent barrier(WaitableEvent::ResetPolicy::AUTOMATIC,
|
| + WaitableEvent::InitialState::NOT_SIGNALED);
|
| +
|
| + // This must be after the declaration of |barrier| so that tasks posted to
|
| + // TaskScheduler can safely use |barrier|.
|
| + test::ScopedTaskEnvironment scoped_task_environment;
|
| +
|
| + CreateSequencedTaskRunnerWithTraits(TaskTraits().WithBaseSyncPrimitives())
|
| + ->PostTask(FROM_HERE,
|
| + base::Bind(&ObserverListThreadSafe<Foo>::AddObserver,
|
| + observer_list, Unretained(&observer)));
|
| + TaskScheduler::GetInstance()->FlushForTesting();
|
| +
|
| + observer_list->Notify(FROM_HERE, &Foo::Observe, 1);
|
| + observer.WaitForNotificationRunning();
|
| + observer_list->RemoveObserver(&observer);
|
| +
|
| + observer.Unblock();
|
| +}
|
| +
|
| TEST(ObserverListTest, Existing) {
|
| ObserverList<Foo> observer_list(ObserverList<Foo>::NOTIFY_EXISTING_ONLY);
|
| Adder a(1);
|
|
|