1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/observer_list.h"
6 #include "base/observer_list_threadsafe.h"
10 #include "base/memory/ref_counted.h"
11 #include "base/message_loop.h"
12 #include "base/threading/platform_thread.h"
13 #include "testing/gtest/include/gtest/gtest.h"
15 using base::PlatformThread
;
22 virtual void Observe(int x
) = 0;
26 class Adder
: public Foo
{
28 explicit Adder(int scaler
) : total(0), scaler_(scaler
) {}
29 virtual void Observe(int x
) {
38 class Disrupter
: public Foo
{
40 Disrupter(ObserverList
<Foo
>* list
, Foo
* doomed
)
41 : list_(list
), doomed_(doomed
) { }
42 virtual ~Disrupter() { }
43 virtual void Observe(int x
) {
44 list_
->RemoveObserver(doomed_
);
47 ObserverList
<Foo
>* list_
;
51 class ThreadSafeDisrupter
: public Foo
{
53 ThreadSafeDisrupter(ObserverListThreadSafe
<Foo
>* list
, Foo
* doomed
)
54 : list_(list
), doomed_(doomed
) { }
55 virtual ~ThreadSafeDisrupter() { }
56 virtual void Observe(int x
) {
57 list_
->RemoveObserver(doomed_
);
60 ObserverListThreadSafe
<Foo
>* list_
;
64 class AddInObserve
: public Foo
{
66 explicit AddInObserve(ObserverList
<Foo
>* observer_list
)
68 observer_list(observer_list
),
71 virtual void Observe(int x
) {
74 observer_list
->AddObserver(&adder
);
79 ObserverList
<Foo
>* observer_list
;
84 class ObserverListThreadSafeTest
: public testing::Test
{
87 static const int kThreadRunTime
= 2000; // ms to run the multi-threaded test.
89 // A thread for use in the ThreadSafeObserver test
90 // which will add and remove itself from the notification
92 class AddRemoveThread
: public PlatformThread::Delegate
,
95 AddRemoveThread(ObserverListThreadSafe
<Foo
>* list
, bool notify
)
101 do_notifies_(notify
) {
102 factory_
= new ScopedRunnableMethodFactory
<AddRemoveThread
>(this);
105 virtual ~AddRemoveThread() {
110 loop_
= new MessageLoop(); // Fire up a message loop.
112 FROM_HERE
, factory_
->NewRunnableMethod(&AddRemoveThread::AddTask
));
114 //LOG(ERROR) << "Loop 0x" << std::hex << loop_ << " done. " <<
115 // count_observes_ << ", " << count_addtask_;
117 loop_
= reinterpret_cast<MessageLoop
*>(0xdeadbeef);
121 // This task just keeps posting to itself in an attempt
122 // to race with the notifier.
126 if ((Time::Now() - start_
).InMilliseconds() > kThreadRunTime
) {
132 list_
->AddObserver(this);
137 list_
->Notify(&Foo::Observe
, 10);
140 loop_
->PostDelayedTask(FROM_HERE
,
141 factory_
->NewRunnableMethod(&AddRemoveThread::AddTask
), 0);
145 loop_
->PostTask(FROM_HERE
, new MessageLoop::QuitTask());
148 virtual void Observe(int x
) {
151 // If we're getting called after we removed ourselves from
152 // the list, that is very bad!
155 // This callback should fire on the appropriate thread
156 EXPECT_EQ(loop_
, MessageLoop::current());
158 list_
->RemoveObserver(this);
163 ObserverListThreadSafe
<Foo
>* list_
;
165 bool in_list_
; // Are we currently registered for notifications.
166 // in_list_ is only used on |this| thread.
167 Time start_
; // The time we started the test.
169 int count_observes_
; // Number of times we observed.
170 int count_addtask_
; // Number of times thread AddTask was called
171 bool do_notifies_
; // Whether these threads should do notifications.
173 ScopedRunnableMethodFactory
<AddRemoveThread
>* factory_
;
176 TEST(ObserverListTest
, BasicTest
) {
177 ObserverList
<Foo
> observer_list
;
178 Adder
a(1), b(-1), c(1), d(-1);
179 Disrupter
evil(&observer_list
, &c
);
181 observer_list
.AddObserver(&a
);
182 observer_list
.AddObserver(&b
);
184 FOR_EACH_OBSERVER(Foo
, observer_list
, Observe(10));
186 observer_list
.AddObserver(&evil
);
187 observer_list
.AddObserver(&c
);
188 observer_list
.AddObserver(&d
);
190 FOR_EACH_OBSERVER(Foo
, observer_list
, Observe(10));
192 EXPECT_EQ(a
.total
, 20);
193 EXPECT_EQ(b
.total
, -20);
194 EXPECT_EQ(c
.total
, 0);
195 EXPECT_EQ(d
.total
, -10);
198 TEST(ObserverListThreadSafeTest
, BasicTest
) {
201 scoped_refptr
<ObserverListThreadSafe
<Foo
> > observer_list(
202 new ObserverListThreadSafe
<Foo
>);
207 ThreadSafeDisrupter
evil(observer_list
.get(), &c
);
209 observer_list
->AddObserver(&a
);
210 observer_list
->AddObserver(&b
);
212 observer_list
->Notify(&Foo::Observe
, 10);
213 loop
.RunAllPending();
215 observer_list
->AddObserver(&evil
);
216 observer_list
->AddObserver(&c
);
217 observer_list
->AddObserver(&d
);
219 observer_list
->Notify(&Foo::Observe
, 10);
220 loop
.RunAllPending();
222 EXPECT_EQ(a
.total
, 20);
223 EXPECT_EQ(b
.total
, -20);
224 EXPECT_EQ(c
.total
, 0);
225 EXPECT_EQ(d
.total
, -10);
228 class FooRemover
: public Foo
{
230 explicit FooRemover(ObserverListThreadSafe
<Foo
>* list
) : list_(list
) {}
231 virtual ~FooRemover() {}
233 void AddFooToRemove(Foo
* foo
) {
234 foos_
.push_back(foo
);
237 virtual void Observe(int x
) {
238 std::vector
<Foo
*> tmp
;
240 for (std::vector
<Foo
*>::iterator it
= tmp
.begin();
241 it
!= tmp
.end(); ++it
) {
242 list_
->RemoveObserver(*it
);
247 const scoped_refptr
<ObserverListThreadSafe
<Foo
> > list_
;
248 std::vector
<Foo
*> foos_
;
251 TEST(ObserverListThreadSafeTest
, RemoveMultipleObservers
) {
253 scoped_refptr
<ObserverListThreadSafe
<Foo
> > observer_list(
254 new ObserverListThreadSafe
<Foo
>);
256 FooRemover
a(observer_list
);
259 observer_list
->AddObserver(&a
);
260 observer_list
->AddObserver(&b
);
262 a
.AddFooToRemove(&a
);
263 a
.AddFooToRemove(&b
);
265 observer_list
->Notify(&Foo::Observe
, 1);
266 loop
.RunAllPending();
269 // A test driver for a multi-threaded notification loop. Runs a number
270 // of observer threads, each of which constantly adds/removes itself
271 // from the observer list. Optionally, if cross_thread_notifies is set
272 // to true, the observer threads will also trigger notifications to
274 static void ThreadSafeObserverHarness(int num_threads
,
275 bool cross_thread_notifies
) {
278 const int kMaxThreads
= 15;
279 num_threads
= num_threads
> kMaxThreads
? kMaxThreads
: num_threads
;
281 scoped_refptr
<ObserverListThreadSafe
<Foo
> > observer_list(
282 new ObserverListThreadSafe
<Foo
>);
288 observer_list
->AddObserver(&a
);
289 observer_list
->AddObserver(&b
);
291 AddRemoveThread
* threaded_observer
[kMaxThreads
];
292 base::PlatformThreadHandle threads
[kMaxThreads
];
293 for (int index
= 0; index
< num_threads
; index
++) {
294 threaded_observer
[index
] = new AddRemoveThread(observer_list
.get(), false);
295 EXPECT_TRUE(PlatformThread::Create(0,
296 threaded_observer
[index
], &threads
[index
]));
299 Time start
= Time::Now();
301 if ((Time::Now() - start
).InMilliseconds() > kThreadRunTime
)
304 observer_list
->Notify(&Foo::Observe
, 10);
306 loop
.RunAllPending();
309 for (int index
= 0; index
< num_threads
; index
++) {
310 threaded_observer
[index
]->Quit();
311 PlatformThread::Join(threads
[index
]);
315 TEST(ObserverListThreadSafeTest
, CrossThreadObserver
) {
316 // Use 7 observer threads. Notifications only come from
318 ThreadSafeObserverHarness(7, false);
321 TEST(ObserverListThreadSafeTest
, CrossThreadNotifications
) {
322 // Use 3 observer threads. Notifications will fire from
323 // the main thread and all 3 observer threads.
324 ThreadSafeObserverHarness(3, true);
327 TEST(ObserverListTest
, Existing
) {
328 ObserverList
<Foo
> observer_list(ObserverList
<Foo
>::NOTIFY_EXISTING_ONLY
);
330 AddInObserve
b(&observer_list
);
332 observer_list
.AddObserver(&a
);
333 observer_list
.AddObserver(&b
);
335 FOR_EACH_OBSERVER(Foo
, observer_list
, Observe(1));
337 EXPECT_TRUE(b
.added
);
338 // B's adder should not have been notified because it was added during
340 EXPECT_EQ(0, b
.adder
.total
);
342 // Notify again to make sure b's adder is notified.
343 FOR_EACH_OBSERVER(Foo
, observer_list
, Observe(1));
344 EXPECT_EQ(1, b
.adder
.total
);
347 class AddInClearObserve
: public Foo
{
349 explicit AddInClearObserve(ObserverList
<Foo
>* list
)
350 : list_(list
), added_(false), adder_(1) {}
352 virtual void Observe(int /* x */) {
354 list_
->AddObserver(&adder_
);
358 bool added() const { return added_
; }
359 const Adder
& adder() const { return adder_
; }
362 ObserverList
<Foo
>* const list_
;
368 TEST(ObserverListTest
, ClearNotifyAll
) {
369 ObserverList
<Foo
> observer_list
;
370 AddInClearObserve
a(&observer_list
);
372 observer_list
.AddObserver(&a
);
374 FOR_EACH_OBSERVER(Foo
, observer_list
, Observe(1));
375 EXPECT_TRUE(a
.added());
376 EXPECT_EQ(1, a
.adder().total
)
377 << "Adder should observe once and have sum of 1.";
380 TEST(ObserverListTest
, ClearNotifyExistingOnly
) {
381 ObserverList
<Foo
> observer_list(ObserverList
<Foo
>::NOTIFY_EXISTING_ONLY
);
382 AddInClearObserve
a(&observer_list
);
384 observer_list
.AddObserver(&a
);
386 FOR_EACH_OBSERVER(Foo
, observer_list
, Observe(1));
387 EXPECT_TRUE(a
.added());
388 EXPECT_EQ(0, a
.adder().total
)
389 << "Adder should not observe, so sum should still be 0.";