Chromium Code Reviews| Index: base/observer_list_unittest.cc |
| diff --git a/base/observer_list_unittest.cc b/base/observer_list_unittest.cc |
| index c5e556bd9da4e76f65ad9965fb256d02932e672b..97c87d1e8bb0d5c2b93d30f7836210b24f7be984 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,133 @@ 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) { |
| + test::ScopedTaskEnvironment scoped_task_environment; |
| + 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); |
| + |
| + 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(); |
| + TaskScheduler::GetInstance()->FlushForTesting(); |
|
gab
2017/04/10 16:23:45
Why is it racy without this? Doesn't the ScopedTas
fdoray
2017/04/10 16:34:09
Yes. But without this, a task may still be waiting
gab
2017/04/10 16:36:14
Hmmm ok, then flip the order of their definition o
fdoray
2017/04/10 19:07:16
Done.
|
| +} |
| + |
| TEST(ObserverListTest, Existing) { |
| ObserverList<Foo> observer_list(ObserverList<Foo>::NOTIFY_EXISTING_ONLY); |
| Adder a(1); |