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); |