1 // Copyright 2018 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/observer_list_threadsafe.h"
6
7 #include <memory>
8 #include <optional>
9 #include <utility>
10 #include <vector>
11
12 #include "base/compiler_specific.h"
13 #include "base/functional/bind.h"
14 #include "base/location.h"
15 #include "base/logging.h"
16 #include "base/memory/raw_ptr.h"
17 #include "base/memory/weak_ptr.h"
18 #include "base/run_loop.h"
19 #include "base/synchronization/waitable_event.h"
20 #include "base/task/sequenced_task_runner.h"
21 #include "base/task/single_thread_task_runner.h"
22 #include "base/task/thread_pool.h"
23 #include "base/task/thread_pool/thread_pool_instance.h"
24 #include "base/test/bind.h"
25 #include "base/test/gtest_util.h"
26 #include "base/test/task_environment.h"
27 #include "base/test/test_waitable_event.h"
28 #include "base/threading/platform_thread.h"
29 #include "base/threading/thread_restrictions.h"
30 #include "build/build_config.h"
31 #include "testing/gtest/include/gtest/gtest.h"
32
33 namespace base {
34 namespace {
35
36 constexpr int kThreadRunTime = 1000; // ms to run the multi-threaded test.
37
38 class Foo {
39 public:
40 virtual void Observe(int x) = 0;
41 virtual ~Foo() = default;
GetValue() const42 virtual int GetValue() const { return 0; }
43 };
44
45 class Adder : public Foo {
46 public:
Adder(int scaler)47 explicit Adder(int scaler) : total(0), scaler_(scaler) {}
48 ~Adder() override = default;
49
Observe(int x)50 void Observe(int x) override { total += x * scaler_; }
GetValue() const51 int GetValue() const override { return total; }
52
53 int total;
54
55 private:
56 int scaler_;
57 };
58
59 class AddInObserve : public Foo {
60 public:
AddInObserve(ObserverListThreadSafe<Foo> * observer_list)61 explicit AddInObserve(ObserverListThreadSafe<Foo>* observer_list)
62 : observer_list(observer_list), to_add_() {}
63
SetToAdd(Foo * to_add)64 void SetToAdd(Foo* to_add) { to_add_ = to_add; }
65
Observe(int x)66 void Observe(int x) override {
67 if (to_add_) {
68 observer_list->AddObserver(to_add_.get());
69 to_add_ = nullptr;
70 }
71 }
72
73 raw_ptr<ObserverListThreadSafe<Foo>> observer_list;
74 raw_ptr<Foo> to_add_;
75 };
76
77 // A task for use in the ThreadSafeObserver test which will add and remove
78 // itself from the notification list repeatedly.
79 template <RemoveObserverPolicy RemovePolicy =
80 RemoveObserverPolicy::kAnySequence>
81 class AddRemoveThread : public Foo {
82 using Self = AddRemoveThread<RemovePolicy>;
83 using ObserverList = ObserverListThreadSafe<Foo, RemovePolicy>;
84
85 public:
AddRemoveThread(ObserverList * list,bool notify,scoped_refptr<SingleThreadTaskRunner> removal_task_runner)86 AddRemoveThread(ObserverList* list,
87 bool notify,
88 scoped_refptr<SingleThreadTaskRunner> removal_task_runner)
89 : list_(list),
90 task_runner_(ThreadPool::CreateSingleThreadTaskRunner(
91 {},
92 SingleThreadTaskRunnerThreadMode::DEDICATED)),
93 removal_task_runner_(std::move(removal_task_runner)),
94 in_list_(false),
95 start_(Time::Now()),
96 do_notifies_(notify) {
97 task_runner_->PostTask(
98 FROM_HERE, base::BindOnce(&Self::AddTask, weak_factory_.GetWeakPtr()));
99 }
100
101 ~AddRemoveThread() override = default;
102
103 // This task just keeps posting to itself in an attempt to race with the
104 // notifier.
AddTask()105 void AddTask() {
106 if ((Time::Now() - start_).InMilliseconds() > kThreadRunTime) {
107 VLOG(1) << "DONE!";
108 return;
109 }
110
111 if (!in_list_) {
112 list_->AddObserver(this);
113 in_list_ = true;
114 }
115
116 if (do_notifies_) {
117 list_->Notify(FROM_HERE, &Foo::Observe, 10);
118 }
119
120 SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
121 FROM_HERE, base::BindOnce(&Self::AddTask, weak_factory_.GetWeakPtr()));
122 }
123
RemoveTask()124 void RemoveTask() {
125 list_->RemoveObserver(this);
126 in_list_ = false;
127 }
128
Observe(int x)129 void Observe(int x) override {
130 // If we're getting called after we removed ourselves from the list, that is
131 // very bad!
132 EXPECT_TRUE(in_list_);
133
134 // This callback should fire on the appropriate thread
135 EXPECT_TRUE(task_runner_->BelongsToCurrentThread());
136
137 if (removal_task_runner_) {
138 // Remove the observer on a different thread, blocking the current thread
139 // until it's removed. Unretained is safe since the pointers are valid
140 // until the thread is unblocked.
141 base::TestWaitableEvent event;
142 removal_task_runner_->PostTask(
143 FROM_HERE, base::BindOnce(&Self::RemoveTask, base::Unretained(this))
144 .Then(base::BindOnce(&base::TestWaitableEvent::Signal,
145 base::Unretained(&event))));
146 event.Wait();
147 } else {
148 // Remove the observer on the same thread.
149 RemoveTask();
150 }
151 }
152
task_runner() const153 scoped_refptr<SingleThreadTaskRunner> task_runner() const {
154 return task_runner_;
155 }
156
157 private:
158 raw_ptr<ObserverList> list_;
159 scoped_refptr<SingleThreadTaskRunner> task_runner_;
160 // Optional task runner used to remove observers. This will be the main task
161 // runner of a different AddRemoveThread.
162 scoped_refptr<SingleThreadTaskRunner> removal_task_runner_;
163 bool in_list_; // Are we currently registered for notifications.
164 // in_list_ is only used on |this| thread.
165 Time start_; // The time we started the test.
166
167 bool do_notifies_; // Whether these threads should do notifications.
168
169 base::WeakPtrFactory<Self> weak_factory_{this};
170 };
171
172 } // namespace
173
TEST(ObserverListThreadSafeTest,BasicTest)174 TEST(ObserverListThreadSafeTest, BasicTest) {
175 using List = ObserverListThreadSafe<Foo>;
176 test::TaskEnvironment task_environment;
177
178 scoped_refptr<List> observer_list(new List);
179 Adder a(1);
180 Adder b(-1);
181 Adder c(1);
182 Adder d(-1);
183
184 List::AddObserverResult result;
185
186 result = observer_list->AddObserver(&a);
187 EXPECT_EQ(result, List::AddObserverResult::kBecameNonEmpty);
188 result = observer_list->AddObserver(&b);
189 EXPECT_EQ(result, List::AddObserverResult::kWasAlreadyNonEmpty);
190
191 observer_list->Notify(FROM_HERE, &Foo::Observe, 10);
192 RunLoop().RunUntilIdle();
193
194 result = observer_list->AddObserver(&c);
195 EXPECT_EQ(result, List::AddObserverResult::kWasAlreadyNonEmpty);
196 result = observer_list->AddObserver(&d);
197 EXPECT_EQ(result, List::AddObserverResult::kWasAlreadyNonEmpty);
198
199 observer_list->Notify(FROM_HERE, &Foo::Observe, 10);
200 observer_list->RemoveObserver(&c);
201 RunLoop().RunUntilIdle();
202
203 EXPECT_EQ(20, a.total);
204 EXPECT_EQ(-20, b.total);
205 EXPECT_EQ(0, c.total);
206 EXPECT_EQ(-10, d.total);
207 }
208
TEST(ObserverListThreadSafeTest,RemoveObserver)209 TEST(ObserverListThreadSafeTest, RemoveObserver) {
210 using List = ObserverListThreadSafe<Foo>;
211 test::TaskEnvironment task_environment;
212
213 scoped_refptr<List> observer_list(new List);
214 Adder a(1), b(1);
215
216 // A workaround for the compiler bug. See http://crbug.com/121960.
217 EXPECT_NE(&a, &b);
218
219 List::RemoveObserverResult result;
220
221 // Should do nothing.
222 result = observer_list->RemoveObserver(&a);
223 EXPECT_EQ(result, List::RemoveObserverResult::kWasOrBecameEmpty);
224 result = observer_list->RemoveObserver(&b);
225 EXPECT_EQ(result, List::RemoveObserverResult::kWasOrBecameEmpty);
226
227 observer_list->Notify(FROM_HERE, &Foo::Observe, 10);
228 RunLoop().RunUntilIdle();
229
230 EXPECT_EQ(0, a.total);
231 EXPECT_EQ(0, b.total);
232
233 observer_list->AddObserver(&a);
234
235 // Should also do nothing.
236 result = observer_list->RemoveObserver(&b);
237 EXPECT_EQ(result, List::RemoveObserverResult::kRemainsNonEmpty);
238
239 observer_list->Notify(FROM_HERE, &Foo::Observe, 10);
240 RunLoop().RunUntilIdle();
241
242 EXPECT_EQ(10, a.total);
243 EXPECT_EQ(0, b.total);
244
245 result = observer_list->RemoveObserver(&a);
246 EXPECT_EQ(result, List::RemoveObserverResult::kWasOrBecameEmpty);
247 }
248
249 class FooRemover : public Foo {
250 public:
FooRemover(ObserverListThreadSafe<Foo> * list)251 explicit FooRemover(ObserverListThreadSafe<Foo>* list) : list_(list) {}
252 ~FooRemover() override = default;
253
AddFooToRemove(Foo * foo)254 void AddFooToRemove(Foo* foo) { foos_.push_back(foo); }
255
Observe(int x)256 void Observe(int x) override {
257 std::vector<raw_ptr<Foo, VectorExperimental>> tmp;
258 tmp.swap(foos_);
259 for (Foo* it : tmp) {
260 list_->RemoveObserver(it);
261 }
262 }
263
264 private:
265 const scoped_refptr<ObserverListThreadSafe<Foo>> list_;
266 std::vector<raw_ptr<Foo, VectorExperimental>> foos_;
267 };
268
TEST(ObserverListThreadSafeTest,RemoveMultipleObservers)269 TEST(ObserverListThreadSafeTest, RemoveMultipleObservers) {
270 test::TaskEnvironment task_environment;
271 scoped_refptr<ObserverListThreadSafe<Foo>> observer_list(
272 new ObserverListThreadSafe<Foo>);
273
274 FooRemover a(observer_list.get());
275 Adder b(1);
276
277 observer_list->AddObserver(&a);
278 observer_list->AddObserver(&b);
279
280 a.AddFooToRemove(&a);
281 a.AddFooToRemove(&b);
282
283 observer_list->Notify(FROM_HERE, &Foo::Observe, 1);
284 RunLoop().RunUntilIdle();
285 }
286
287 // A test driver for a multi-threaded notification loop. Runs a number of
288 // observer threads, each of which constantly adds/removes itself from the
289 // observer list. Optionally, if `cross_thread_notifies` is set to true, the
290 // observer threads will also trigger notifications to all observers, and if
291 // `cross_thread_removes` is set to true, the observer threads will also remove
292 // observers added by other threads.
293 template <
294 RemoveObserverPolicy RemovePolicy = RemoveObserverPolicy::kAnySequence>
ThreadSafeObserverHarness(int num_threads,bool cross_thread_notifies=false,bool cross_thread_removes=false)295 static void ThreadSafeObserverHarness(int num_threads,
296 bool cross_thread_notifies = false,
297 bool cross_thread_removes = false) {
298 test::TaskEnvironment task_environment;
299
300 auto observer_list =
301 base::MakeRefCounted<ObserverListThreadSafe<Foo, RemovePolicy>>();
302
303 Adder a(1);
304 Adder b(-1);
305
306 observer_list->AddObserver(&a);
307 observer_list->AddObserver(&b);
308
309 using TestThread = AddRemoveThread<RemovePolicy>;
310 std::vector<std::unique_ptr<TestThread>> threaded_observers;
311 threaded_observers.reserve(num_threads);
312 scoped_refptr<SingleThreadTaskRunner> removal_task_runner;
313 for (int index = 0; index < num_threads; index++) {
314 auto add_remove_thread =
315 std::make_unique<TestThread>(observer_list.get(), cross_thread_notifies,
316 std::move(removal_task_runner));
317 if (cross_thread_removes) {
318 // Save the task runner to pass to the next thread.
319 removal_task_runner = add_remove_thread->task_runner();
320 }
321 threaded_observers.push_back(std::move(add_remove_thread));
322 }
323 ASSERT_EQ(static_cast<size_t>(num_threads), threaded_observers.size());
324
325 Time start = Time::Now();
326 while (true) {
327 if ((Time::Now() - start).InMilliseconds() > kThreadRunTime)
328 break;
329
330 observer_list->Notify(FROM_HERE, &Foo::Observe, 10);
331
332 RunLoop().RunUntilIdle();
333 }
334
335 task_environment.RunUntilIdle();
336 }
337
TEST(ObserverListThreadSafeTest,CrossThreadObserver)338 TEST(ObserverListThreadSafeTest, CrossThreadObserver) {
339 // Use 7 observer threads. Notifications only come from the main thread.
340 ThreadSafeObserverHarness(7);
341 }
342
TEST(ObserverListThreadSafeTest,CrossThreadNotifications)343 TEST(ObserverListThreadSafeTest, CrossThreadNotifications) {
344 // Use 3 observer threads. Notifications will fire from the main thread and
345 // all 3 observer threads.
346 ThreadSafeObserverHarness(3, /*cross_thread_notifies=*/true);
347 }
348
TEST(ObserverListThreadSafeTest,CrossThreadRemoval)349 TEST(ObserverListThreadSafeTest, CrossThreadRemoval) {
350 // Use 3 observer threads. Observers can be removed from any thread.
351 ThreadSafeObserverHarness(3, /*cross_thread_notifies=*/true,
352 /*cross_thread_removes=*/true);
353 }
354
TEST(ObserverListThreadSafeTest,CrossThreadRemovalRestricted)355 TEST(ObserverListThreadSafeTest, CrossThreadRemovalRestricted) {
356 // Use 3 observer threads. Observers must be removed from the thread that
357 // added them. This should succeed because the test doesn't break that
358 // restriction.
359 ThreadSafeObserverHarness<RemoveObserverPolicy::kAddingSequenceOnly>(
360 3, /*cross_thread_notifies=*/true, /*cross_thread_removes=*/false);
361 }
362
TEST(ObserverListThreadSafeDeathTest,CrossThreadRemovalRestricted)363 TEST(ObserverListThreadSafeDeathTest, CrossThreadRemovalRestricted) {
364 // Use 3 observer threads. Observers must be removed from the thread that
365 // added them. This should CHECK because the test breaks that restriction.
366 EXPECT_CHECK_DEATH(
367 ThreadSafeObserverHarness<RemoveObserverPolicy::kAddingSequenceOnly>(
368 3, /*cross_thread_notifies=*/true, /*cross_thread_removes=*/true));
369 }
370
TEST(ObserverListThreadSafeTest,OutlivesTaskEnvironment)371 TEST(ObserverListThreadSafeTest, OutlivesTaskEnvironment) {
372 std::optional<test::TaskEnvironment> task_environment(std::in_place);
373 auto observer_list = base::MakeRefCounted<ObserverListThreadSafe<Foo>>();
374
375 Adder a(1);
376 observer_list->AddObserver(&a);
377 task_environment.reset();
378 // Test passes if we don't crash here.
379 observer_list->Notify(FROM_HERE, &Foo::Observe, 1);
380 observer_list->RemoveObserver(&a);
381 }
382
TEST(ObserverListThreadSafeTest,OutlivesTaskEnvironmentRemovalRestricted)383 TEST(ObserverListThreadSafeTest, OutlivesTaskEnvironmentRemovalRestricted) {
384 std::optional<test::TaskEnvironment> task_environment(std::in_place);
385 auto observer_list = base::MakeRefCounted<
386 ObserverListThreadSafe<Foo, RemoveObserverPolicy::kAddingSequenceOnly>>();
387
388 Adder a(1);
389 observer_list->AddObserver(&a);
390 task_environment.reset();
391 // Test passes if we don't crash here.
392 observer_list->Notify(FROM_HERE, &Foo::Observe, 1);
393 observer_list->RemoveObserver(&a);
394 }
395
396 namespace {
397
398 class SequenceVerificationObserver : public Foo {
399 public:
SequenceVerificationObserver(scoped_refptr<SequencedTaskRunner> task_runner)400 explicit SequenceVerificationObserver(
401 scoped_refptr<SequencedTaskRunner> task_runner)
402 : task_runner_(std::move(task_runner)) {}
403 SequenceVerificationObserver(const SequenceVerificationObserver&) = delete;
404 SequenceVerificationObserver& operator=(const SequenceVerificationObserver&) =
405 delete;
406 ~SequenceVerificationObserver() override = default;
407
Observe(int x)408 void Observe(int x) override {
409 called_on_valid_sequence_ = task_runner_->RunsTasksInCurrentSequence();
410 }
411
called_on_valid_sequence() const412 bool called_on_valid_sequence() const { return called_on_valid_sequence_; }
413
414 private:
415 const scoped_refptr<SequencedTaskRunner> task_runner_;
416 bool called_on_valid_sequence_ = false;
417 };
418
419 } // namespace
420
421 // Verify that observers are notified on the correct sequence.
TEST(ObserverListThreadSafeTest,NotificationOnValidSequence)422 TEST(ObserverListThreadSafeTest, NotificationOnValidSequence) {
423 test::TaskEnvironment task_environment;
424
425 auto task_runner_1 = ThreadPool::CreateSequencedTaskRunner({});
426 auto task_runner_2 = ThreadPool::ThreadPool::CreateSequencedTaskRunner({});
427
428 auto observer_list = MakeRefCounted<ObserverListThreadSafe<Foo>>();
429
430 SequenceVerificationObserver observer_1(task_runner_1);
431 SequenceVerificationObserver observer_2(task_runner_2);
432
433 task_runner_1->PostTask(
434 FROM_HERE,
435 BindOnce(base::IgnoreResult(&ObserverListThreadSafe<Foo>::AddObserver),
436 observer_list, Unretained(&observer_1)));
437 task_runner_2->PostTask(
438 FROM_HERE,
439 BindOnce(base::IgnoreResult(&ObserverListThreadSafe<Foo>::AddObserver),
440 observer_list, Unretained(&observer_2)));
441
442 ThreadPoolInstance::Get()->FlushForTesting();
443
444 observer_list->Notify(FROM_HERE, &Foo::Observe, 1);
445
446 ThreadPoolInstance::Get()->FlushForTesting();
447
448 EXPECT_TRUE(observer_1.called_on_valid_sequence());
449 EXPECT_TRUE(observer_2.called_on_valid_sequence());
450 }
451
452 // Verify that when an observer is added to a NOTIFY_ALL ObserverListThreadSafe
453 // from a notification, it is itself notified.
TEST(ObserverListThreadSafeTest,AddObserverFromNotificationNotifyAll)454 TEST(ObserverListThreadSafeTest, AddObserverFromNotificationNotifyAll) {
455 test::TaskEnvironment task_environment;
456 auto observer_list = MakeRefCounted<ObserverListThreadSafe<Foo>>();
457
458 Adder observer_added_from_notification(1);
459
460 AddInObserve initial_observer(observer_list.get());
461 initial_observer.SetToAdd(&observer_added_from_notification);
462 observer_list->AddObserver(&initial_observer);
463
464 observer_list->Notify(FROM_HERE, &Foo::Observe, 1);
465
466 base::RunLoop().RunUntilIdle();
467
468 EXPECT_EQ(1, observer_added_from_notification.GetValue());
469 }
470
471 namespace {
472
473 class RemoveWhileNotificationIsRunningObserver : public Foo {
474 public:
RemoveWhileNotificationIsRunningObserver()475 RemoveWhileNotificationIsRunningObserver()
476 : notification_running_(WaitableEvent::ResetPolicy::AUTOMATIC,
477 WaitableEvent::InitialState::NOT_SIGNALED),
478 barrier_(WaitableEvent::ResetPolicy::AUTOMATIC,
479 WaitableEvent::InitialState::NOT_SIGNALED) {}
480 RemoveWhileNotificationIsRunningObserver(
481 const RemoveWhileNotificationIsRunningObserver&) = delete;
482 RemoveWhileNotificationIsRunningObserver& operator=(
483 const RemoveWhileNotificationIsRunningObserver&) = delete;
484 ~RemoveWhileNotificationIsRunningObserver() override = default;
485
Observe(int x)486 void Observe(int x) override {
487 notification_running_.Signal();
488 ScopedAllowBaseSyncPrimitivesForTesting allow_base_sync_primitives;
489 barrier_.Wait();
490 }
491
WaitForNotificationRunning()492 void WaitForNotificationRunning() { notification_running_.Wait(); }
Unblock()493 void Unblock() { barrier_.Signal(); }
494
495 private:
496 WaitableEvent notification_running_;
497 WaitableEvent barrier_;
498 };
499
500 } // namespace
501
502 // Verify that there is no crash when an observer is removed while it is being
503 // notified.
TEST(ObserverListThreadSafeTest,RemoveWhileNotificationIsRunning)504 TEST(ObserverListThreadSafeTest, RemoveWhileNotificationIsRunning) {
505 auto observer_list = MakeRefCounted<ObserverListThreadSafe<Foo>>();
506 RemoveWhileNotificationIsRunningObserver observer;
507
508 WaitableEvent task_running(WaitableEvent::ResetPolicy::AUTOMATIC,
509 WaitableEvent::InitialState::NOT_SIGNALED);
510 WaitableEvent barrier(WaitableEvent::ResetPolicy::AUTOMATIC,
511 WaitableEvent::InitialState::NOT_SIGNALED);
512
513 // This must be after the declaration of |barrier| so that tasks posted to
514 // ThreadPool can safely use |barrier|.
515 test::TaskEnvironment task_environment;
516
517 ThreadPool::CreateSequencedTaskRunner({MayBlock()})
518 ->PostTask(FROM_HERE,
519 base::BindOnce(base::IgnoreResult(
520 &ObserverListThreadSafe<Foo>::AddObserver),
521 observer_list, Unretained(&observer)));
522 ThreadPoolInstance::Get()->FlushForTesting();
523
524 observer_list->Notify(FROM_HERE, &Foo::Observe, 1);
525 observer.WaitForNotificationRunning();
526 observer_list->RemoveObserver(&observer);
527
528 observer.Unblock();
529 }
530
TEST(ObserverListThreadSafeTest,AddRemoveWithPendingNotifications)531 TEST(ObserverListThreadSafeTest, AddRemoveWithPendingNotifications) {
532 test::TaskEnvironment task_environment;
533
534 scoped_refptr<ObserverListThreadSafe<Foo>> observer_list(
535 new ObserverListThreadSafe<Foo>);
536 Adder a(1);
537 Adder b(1);
538
539 observer_list->AddObserver(&a);
540 observer_list->AddObserver(&b);
541
542 // Remove observer `a` while there is a pending notification for observer `a`.
543 observer_list->Notify(FROM_HERE, &Foo::Observe, 10);
544 observer_list->RemoveObserver(&a);
545 RunLoop().RunUntilIdle();
546 observer_list->AddObserver(&a);
547
548 EXPECT_EQ(0, a.total);
549 EXPECT_EQ(10, b.total);
550
551 // Remove and re-adding observer `a` while there is a pending notification for
552 // observer `a`. The notification to `a` must not be executed since it was
553 // sent before the removal of `a`.
554 observer_list->Notify(FROM_HERE, &Foo::Observe, 10);
555 observer_list->RemoveObserver(&a);
556 observer_list->AddObserver(&a);
557 RunLoop().RunUntilIdle();
558
559 EXPECT_EQ(0, a.total);
560 EXPECT_EQ(20, b.total);
561
562 // Observer `a` and `b` are present and should both receive a notification.
563 observer_list->RemoveObserver(&a);
564 observer_list->AddObserver(&a);
565 observer_list->Notify(FROM_HERE, &Foo::Observe, 10);
566 RunLoop().RunUntilIdle();
567
568 EXPECT_EQ(10, a.total);
569 EXPECT_EQ(30, b.total);
570 }
571
572 // Same as ObserverListTest.Existing, but for ObserverListThreadSafe
TEST(ObserverListThreadSafeTest,Existing)573 TEST(ObserverListThreadSafeTest, Existing) {
574 test::TaskEnvironment task_environment;
575 scoped_refptr<ObserverListThreadSafe<Foo>> observer_list(
576 new ObserverListThreadSafe<Foo>(ObserverListPolicy::EXISTING_ONLY));
577 Adder a(1);
578 AddInObserve b(observer_list.get());
579 Adder c(1);
580 b.SetToAdd(&c);
581
582 observer_list->AddObserver(&a);
583 observer_list->AddObserver(&b);
584
585 observer_list->Notify(FROM_HERE, &Foo::Observe, 1);
586 RunLoop().RunUntilIdle();
587
588 EXPECT_FALSE(b.to_add_);
589 // B's adder should not have been notified because it was added during
590 // notification.
591 EXPECT_EQ(0, c.total);
592
593 // Notify again to make sure b's adder is notified.
594 observer_list->Notify(FROM_HERE, &Foo::Observe, 1);
595 RunLoop().RunUntilIdle();
596 EXPECT_EQ(1, c.total);
597 }
598
599 } // namespace base
600