blob: 87d1ebe8dd6a340474e0fb8967fb32fa031c1345 [file] [log] [blame]
Trent Apted4d207362018-08-15 23:48:151// Copyright 2018 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.
4
5#include "base/observer_list_threadsafe.h"
6
7#include <memory>
8#include <vector>
9
10#include "base/bind.h"
11#include "base/compiler_specific.h"
12#include "base/location.h"
13#include "base/memory/weak_ptr.h"
Alexander Timin4f9c35c2018-11-01 20:15:2014#include "base/message_loop/message_loop.h"
Trent Apted4d207362018-08-15 23:48:1515#include "base/run_loop.h"
16#include "base/sequenced_task_runner.h"
17#include "base/single_thread_task_runner.h"
18#include "base/synchronization/waitable_event.h"
19#include "base/task/post_task.h"
20#include "base/task/task_scheduler/task_scheduler.h"
21#include "base/test/scoped_task_environment.h"
22#include "base/threading/platform_thread.h"
23#include "base/threading/thread_restrictions.h"
Lei Zhang52637ed2019-02-20 01:38:3724#include "base/threading/thread_task_runner_handle.h"
Trent Apted4d207362018-08-15 23:48:1525#include "build/build_config.h"
26#include "testing/gtest/include/gtest/gtest.h"
27
28namespace base {
29namespace {
30
31constexpr int kThreadRunTime = 2000; // ms to run the multi-threaded test.
32
33class Foo {
34 public:
35 virtual void Observe(int x) = 0;
36 virtual ~Foo() = default;
37 virtual int GetValue() const { return 0; }
38};
39
40class Adder : public Foo {
41 public:
42 explicit Adder(int scaler) : total(0), scaler_(scaler) {}
43 ~Adder() override = default;
44
45 void Observe(int x) override { total += x * scaler_; }
46 int GetValue() const override { return total; }
47
48 int total;
49
50 private:
51 int scaler_;
52};
53
54class AddInObserve : public Foo {
55 public:
56 explicit AddInObserve(ObserverListThreadSafe<Foo>* observer_list)
57 : observer_list(observer_list), to_add_() {}
58
59 void SetToAdd(Foo* to_add) { to_add_ = to_add; }
60
61 void Observe(int x) override {
62 if (to_add_) {
63 observer_list->AddObserver(to_add_);
64 to_add_ = nullptr;
65 }
66 }
67
68 ObserverListThreadSafe<Foo>* observer_list;
69 Foo* to_add_;
70};
71
Sami Kyostila21f4d4c2018-11-29 11:11:3672// A task for use in the ThreadSafeObserver test which will add and remove
Trent Apted4d207362018-08-15 23:48:1573// itself from the notification list repeatedly.
Sami Kyostila21f4d4c2018-11-29 11:11:3674class AddRemoveThread : public Foo {
Trent Apted4d207362018-08-15 23:48:1575 public:
Sami Kyostila21f4d4c2018-11-29 11:11:3676 AddRemoveThread(ObserverListThreadSafe<Foo>* list, bool notify)
Trent Apted4d207362018-08-15 23:48:1577 : list_(list),
Sami Kyostila21f4d4c2018-11-29 11:11:3678 task_runner_(CreateSingleThreadTaskRunnerWithTraits(
79 TaskTraits(),
80 SingleThreadTaskRunnerThreadMode::DEDICATED)),
Trent Apted4d207362018-08-15 23:48:1581 in_list_(false),
82 start_(Time::Now()),
Trent Apted4d207362018-08-15 23:48:1583 do_notifies_(notify),
Sami Kyostila21f4d4c2018-11-29 11:11:3684 weak_factory_(this) {
85 task_runner_->PostTask(
Trent Apted4d207362018-08-15 23:48:1586 FROM_HERE,
87 base::BindOnce(&AddRemoveThread::AddTask, weak_factory_.GetWeakPtr()));
Trent Apted4d207362018-08-15 23:48:1588 }
89
Sami Kyostila21f4d4c2018-11-29 11:11:3690 ~AddRemoveThread() override = default;
91
Trent Apted4d207362018-08-15 23:48:1592 // This task just keeps posting to itself in an attempt to race with the
93 // notifier.
94 void AddTask() {
Trent Apted4d207362018-08-15 23:48:1595 if ((Time::Now() - start_).InMilliseconds() > kThreadRunTime) {
96 VLOG(1) << "DONE!";
97 return;
98 }
99
100 if (!in_list_) {
101 list_->AddObserver(this);
102 in_list_ = true;
103 }
104
105 if (do_notifies_) {
106 list_->Notify(FROM_HERE, &Foo::Observe, 10);
107 }
108
Sami Kyostila21f4d4c2018-11-29 11:11:36109 ThreadTaskRunnerHandle::Get()->PostTask(
Trent Apted4d207362018-08-15 23:48:15110 FROM_HERE,
111 base::BindOnce(&AddRemoveThread::AddTask, weak_factory_.GetWeakPtr()));
112 }
113
Trent Apted4d207362018-08-15 23:48:15114 void Observe(int x) override {
Trent Apted4d207362018-08-15 23:48:15115 // If we're getting called after we removed ourselves from the list, that is
116 // very bad!
Alexander Timind1e773cb2018-10-26 14:19:03117 EXPECT_TRUE(in_list_);
Trent Apted4d207362018-08-15 23:48:15118
119 // This callback should fire on the appropriate thread
Sami Kyostila21f4d4c2018-11-29 11:11:36120 EXPECT_TRUE(task_runner_->BelongsToCurrentThread());
Trent Apted4d207362018-08-15 23:48:15121
122 list_->RemoveObserver(this);
123 in_list_ = false;
124 }
125
126 private:
127 ObserverListThreadSafe<Foo>* list_;
Sami Kyostila21f4d4c2018-11-29 11:11:36128 scoped_refptr<SingleThreadTaskRunner> task_runner_;
Trent Apted4d207362018-08-15 23:48:15129 bool in_list_; // Are we currently registered for notifications.
130 // in_list_ is only used on |this| thread.
131 Time start_; // The time we started the test.
132
Trent Apted4d207362018-08-15 23:48:15133 bool do_notifies_; // Whether these threads should do notifications.
Trent Apted4d207362018-08-15 23:48:15134
135 base::WeakPtrFactory<AddRemoveThread> weak_factory_;
136};
137
138} // namespace
139
140TEST(ObserverListThreadSafeTest, BasicTest) {
Sami Kyostila3f49cb572018-11-19 13:01:09141 test::ScopedTaskEnvironment scoped_task_environment;
Trent Apted4d207362018-08-15 23:48:15142
143 scoped_refptr<ObserverListThreadSafe<Foo>> observer_list(
144 new ObserverListThreadSafe<Foo>);
145 Adder a(1);
146 Adder b(-1);
147 Adder c(1);
148 Adder d(-1);
149
150 observer_list->AddObserver(&a);
151 observer_list->AddObserver(&b);
152
153 observer_list->Notify(FROM_HERE, &Foo::Observe, 10);
154 RunLoop().RunUntilIdle();
155
156 observer_list->AddObserver(&c);
157 observer_list->AddObserver(&d);
158
159 observer_list->Notify(FROM_HERE, &Foo::Observe, 10);
160 observer_list->RemoveObserver(&c);
161 RunLoop().RunUntilIdle();
162
163 EXPECT_EQ(20, a.total);
164 EXPECT_EQ(-20, b.total);
165 EXPECT_EQ(0, c.total);
166 EXPECT_EQ(-10, d.total);
167}
168
169TEST(ObserverListThreadSafeTest, RemoveObserver) {
Sami Kyostila3f49cb572018-11-19 13:01:09170 test::ScopedTaskEnvironment scoped_task_environment;
Trent Apted4d207362018-08-15 23:48:15171
172 scoped_refptr<ObserverListThreadSafe<Foo>> observer_list(
173 new ObserverListThreadSafe<Foo>);
174 Adder a(1), b(1);
175
176 // A workaround for the compiler bug. See http://crbug.com/121960.
177 EXPECT_NE(&a, &b);
178
179 // Should do nothing.
180 observer_list->RemoveObserver(&a);
181 observer_list->RemoveObserver(&b);
182
183 observer_list->Notify(FROM_HERE, &Foo::Observe, 10);
184 RunLoop().RunUntilIdle();
185
186 EXPECT_EQ(0, a.total);
187 EXPECT_EQ(0, b.total);
188
189 observer_list->AddObserver(&a);
190
191 // Should also do nothing.
192 observer_list->RemoveObserver(&b);
193
194 observer_list->Notify(FROM_HERE, &Foo::Observe, 10);
195 RunLoop().RunUntilIdle();
196
197 EXPECT_EQ(10, a.total);
198 EXPECT_EQ(0, b.total);
199}
200
201TEST(ObserverListThreadSafeTest, WithoutSequence) {
202 scoped_refptr<ObserverListThreadSafe<Foo>> observer_list(
203 new ObserverListThreadSafe<Foo>);
204
205 Adder a(1), b(1), c(1);
206
207 // No sequence, so these should not be added.
208 observer_list->AddObserver(&a);
209 observer_list->AddObserver(&b);
210
211 {
212 // Add c when there's a sequence.
Sami Kyostila3f49cb572018-11-19 13:01:09213 test::ScopedTaskEnvironment scoped_task_environment;
Trent Apted4d207362018-08-15 23:48:15214 observer_list->AddObserver(&c);
215
216 observer_list->Notify(FROM_HERE, &Foo::Observe, 10);
217 RunLoop().RunUntilIdle();
218
219 EXPECT_EQ(0, a.total);
220 EXPECT_EQ(0, b.total);
221 EXPECT_EQ(10, c.total);
222
223 // Now add a when there's a sequence.
224 observer_list->AddObserver(&a);
225
226 // Remove c when there's a sequence.
227 observer_list->RemoveObserver(&c);
228
229 // Notify again.
230 observer_list->Notify(FROM_HERE, &Foo::Observe, 20);
231 RunLoop().RunUntilIdle();
232
233 EXPECT_EQ(20, a.total);
234 EXPECT_EQ(0, b.total);
235 EXPECT_EQ(10, c.total);
236 }
237
238 // Removing should always succeed with or without a sequence.
239 observer_list->RemoveObserver(&a);
240
241 // Notifying should not fail but should also be a no-op.
Sami Kyostila3f49cb572018-11-19 13:01:09242 test::ScopedTaskEnvironment scoped_task_environment;
Trent Apted4d207362018-08-15 23:48:15243 observer_list->AddObserver(&b);
244 observer_list->Notify(FROM_HERE, &Foo::Observe, 30);
245 RunLoop().RunUntilIdle();
246
247 EXPECT_EQ(20, a.total);
248 EXPECT_EQ(30, b.total);
249 EXPECT_EQ(10, c.total);
250}
251
252class FooRemover : public Foo {
253 public:
254 explicit FooRemover(ObserverListThreadSafe<Foo>* list) : list_(list) {}
255 ~FooRemover() override = default;
256
257 void AddFooToRemove(Foo* foo) { foos_.push_back(foo); }
258
259 void Observe(int x) override {
260 std::vector<Foo*> tmp;
261 tmp.swap(foos_);
jdoerrie6c6229352018-10-22 15:55:43262 for (auto* it : tmp) {
263 list_->RemoveObserver(it);
Trent Apted4d207362018-08-15 23:48:15264 }
265 }
266
267 private:
268 const scoped_refptr<ObserverListThreadSafe<Foo>> list_;
269 std::vector<Foo*> foos_;
270};
271
272TEST(ObserverListThreadSafeTest, RemoveMultipleObservers) {
Sami Kyostila3f49cb572018-11-19 13:01:09273 test::ScopedTaskEnvironment scoped_task_environment;
Trent Apted4d207362018-08-15 23:48:15274 scoped_refptr<ObserverListThreadSafe<Foo>> observer_list(
275 new ObserverListThreadSafe<Foo>);
276
277 FooRemover a(observer_list.get());
278 Adder b(1);
279
280 observer_list->AddObserver(&a);
281 observer_list->AddObserver(&b);
282
283 a.AddFooToRemove(&a);
284 a.AddFooToRemove(&b);
285
286 observer_list->Notify(FROM_HERE, &Foo::Observe, 1);
287 RunLoop().RunUntilIdle();
288}
289
290// A test driver for a multi-threaded notification loop. Runs a number of
291// observer threads, each of which constantly adds/removes itself from the
292// observer list. Optionally, if cross_thread_notifies is set to true, the
293// observer threads will also trigger notifications to all observers.
294static void ThreadSafeObserverHarness(int num_threads,
295 bool cross_thread_notifies) {
Sami Kyostila3f49cb572018-11-19 13:01:09296 test::ScopedTaskEnvironment scoped_task_environment;
Trent Apted4d207362018-08-15 23:48:15297
298 scoped_refptr<ObserverListThreadSafe<Foo>> observer_list(
299 new ObserverListThreadSafe<Foo>);
300 Adder a(1);
301 Adder b(-1);
302
303 observer_list->AddObserver(&a);
304 observer_list->AddObserver(&b);
305
Sami Kyostila21f4d4c2018-11-29 11:11:36306 std::vector<std::unique_ptr<AddRemoveThread>> threaded_observer;
Trent Apted4d207362018-08-15 23:48:15307 threaded_observer.reserve(num_threads);
Trent Apted4d207362018-08-15 23:48:15308 for (int index = 0; index < num_threads; index++) {
Sami Kyostila21f4d4c2018-11-29 11:11:36309 threaded_observer.push_back(std::make_unique<AddRemoveThread>(
310 observer_list.get(), cross_thread_notifies));
Trent Apted4d207362018-08-15 23:48:15311 }
312 ASSERT_EQ(static_cast<size_t>(num_threads), threaded_observer.size());
Trent Apted4d207362018-08-15 23:48:15313
314 Time start = Time::Now();
315 while (true) {
316 if ((Time::Now() - start).InMilliseconds() > kThreadRunTime)
317 break;
318
319 observer_list->Notify(FROM_HERE, &Foo::Observe, 10);
320
321 RunLoop().RunUntilIdle();
322 }
323
Sami Kyostila21f4d4c2018-11-29 11:11:36324 scoped_task_environment.RunUntilIdle();
Trent Apted4d207362018-08-15 23:48:15325}
326
Wezc1143172019-01-21 18:05:41327TEST(ObserverListThreadSafeTest, CrossThreadObserver) {
Trent Apted4d207362018-08-15 23:48:15328 // Use 7 observer threads. Notifications only come from the main thread.
329 ThreadSafeObserverHarness(7, false);
330}
331
332TEST(ObserverListThreadSafeTest, CrossThreadNotifications) {
333 // Use 3 observer threads. Notifications will fire from the main thread and
334 // all 3 observer threads.
335 ThreadSafeObserverHarness(3, true);
336}
337
Sami Kyostila3f49cb572018-11-19 13:01:09338TEST(ObserverListThreadSafeTest, OutlivesTaskEnvironment) {
339 Optional<test::ScopedTaskEnvironment> scoped_task_environment(in_place);
Trent Apted4d207362018-08-15 23:48:15340 scoped_refptr<ObserverListThreadSafe<Foo>> observer_list(
341 new ObserverListThreadSafe<Foo>);
342
343 Adder a(1);
344 observer_list->AddObserver(&a);
Sami Kyostila3f49cb572018-11-19 13:01:09345 scoped_task_environment.reset();
Trent Apted4d207362018-08-15 23:48:15346 // Test passes if we don't crash here.
347 observer_list->Notify(FROM_HERE, &Foo::Observe, 1);
348}
349
350namespace {
351
352class SequenceVerificationObserver : public Foo {
353 public:
354 explicit SequenceVerificationObserver(
355 scoped_refptr<SequencedTaskRunner> task_runner)
356 : task_runner_(std::move(task_runner)) {}
357 ~SequenceVerificationObserver() override = default;
358
359 void Observe(int x) override {
360 called_on_valid_sequence_ = task_runner_->RunsTasksInCurrentSequence();
361 }
362
363 bool called_on_valid_sequence() const { return called_on_valid_sequence_; }
364
365 private:
366 const scoped_refptr<SequencedTaskRunner> task_runner_;
367 bool called_on_valid_sequence_ = false;
368
369 DISALLOW_COPY_AND_ASSIGN(SequenceVerificationObserver);
370};
371
372} // namespace
373
374// Verify that observers are notified on the correct sequence.
375TEST(ObserverListThreadSafeTest, NotificationOnValidSequence) {
376 test::ScopedTaskEnvironment scoped_task_environment;
377
378 auto task_runner_1 = CreateSequencedTaskRunnerWithTraits(TaskTraits());
379 auto task_runner_2 = CreateSequencedTaskRunnerWithTraits(TaskTraits());
380
381 auto observer_list = MakeRefCounted<ObserverListThreadSafe<Foo>>();
382
383 SequenceVerificationObserver observer_1(task_runner_1);
384 SequenceVerificationObserver observer_2(task_runner_2);
385
386 task_runner_1->PostTask(FROM_HERE,
387 BindOnce(&ObserverListThreadSafe<Foo>::AddObserver,
388 observer_list, Unretained(&observer_1)));
389 task_runner_2->PostTask(FROM_HERE,
390 BindOnce(&ObserverListThreadSafe<Foo>::AddObserver,
391 observer_list, Unretained(&observer_2)));
392
393 TaskScheduler::GetInstance()->FlushForTesting();
394
395 observer_list->Notify(FROM_HERE, &Foo::Observe, 1);
396
397 TaskScheduler::GetInstance()->FlushForTesting();
398
399 EXPECT_TRUE(observer_1.called_on_valid_sequence());
400 EXPECT_TRUE(observer_2.called_on_valid_sequence());
401}
402
403// Verify that when an observer is added to a NOTIFY_ALL ObserverListThreadSafe
404// from a notification, it is itself notified.
405TEST(ObserverListThreadSafeTest, AddObserverFromNotificationNotifyAll) {
406 test::ScopedTaskEnvironment scoped_task_environment;
407 auto observer_list = MakeRefCounted<ObserverListThreadSafe<Foo>>();
408
409 Adder observer_added_from_notification(1);
410
411 AddInObserve initial_observer(observer_list.get());
412 initial_observer.SetToAdd(&observer_added_from_notification);
413 observer_list->AddObserver(&initial_observer);
414
415 observer_list->Notify(FROM_HERE, &Foo::Observe, 1);
416
417 base::RunLoop().RunUntilIdle();
418
419 EXPECT_EQ(1, observer_added_from_notification.GetValue());
420}
421
422namespace {
423
424class RemoveWhileNotificationIsRunningObserver : public Foo {
425 public:
426 RemoveWhileNotificationIsRunningObserver()
427 : notification_running_(WaitableEvent::ResetPolicy::AUTOMATIC,
428 WaitableEvent::InitialState::NOT_SIGNALED),
429 barrier_(WaitableEvent::ResetPolicy::AUTOMATIC,
430 WaitableEvent::InitialState::NOT_SIGNALED) {}
431 ~RemoveWhileNotificationIsRunningObserver() override = default;
432
433 void Observe(int x) override {
434 notification_running_.Signal();
435 ScopedAllowBaseSyncPrimitivesForTesting allow_base_sync_primitives;
436 barrier_.Wait();
437 }
438
439 void WaitForNotificationRunning() { notification_running_.Wait(); }
440 void Unblock() { barrier_.Signal(); }
441
442 private:
443 WaitableEvent notification_running_;
444 WaitableEvent barrier_;
445
446 DISALLOW_COPY_AND_ASSIGN(RemoveWhileNotificationIsRunningObserver);
447};
448
449} // namespace
450
451// Verify that there is no crash when an observer is removed while it is being
452// notified.
453TEST(ObserverListThreadSafeTest, RemoveWhileNotificationIsRunning) {
454 auto observer_list = MakeRefCounted<ObserverListThreadSafe<Foo>>();
455 RemoveWhileNotificationIsRunningObserver observer;
456
457 WaitableEvent task_running(WaitableEvent::ResetPolicy::AUTOMATIC,
458 WaitableEvent::InitialState::NOT_SIGNALED);
459 WaitableEvent barrier(WaitableEvent::ResetPolicy::AUTOMATIC,
460 WaitableEvent::InitialState::NOT_SIGNALED);
461
462 // This must be after the declaration of |barrier| so that tasks posted to
463 // TaskScheduler can safely use |barrier|.
464 test::ScopedTaskEnvironment scoped_task_environment;
465
Etienne Pierre-Doray3879b052018-09-17 14:17:22466 CreateSequencedTaskRunnerWithTraits({MayBlock()})
467 ->PostTask(FROM_HERE,
468 base::BindOnce(&ObserverListThreadSafe<Foo>::AddObserver,
Trent Apted4d207362018-08-15 23:48:15469 observer_list, Unretained(&observer)));
470 TaskScheduler::GetInstance()->FlushForTesting();
471
472 observer_list->Notify(FROM_HERE, &Foo::Observe, 1);
473 observer.WaitForNotificationRunning();
474 observer_list->RemoveObserver(&observer);
475
476 observer.Unblock();
477}
478
479// Same as ObserverListTest.Existing, but for ObserverListThreadSafe
480TEST(ObserverListThreadSafeTest, Existing) {
Sami Kyostila3f49cb572018-11-19 13:01:09481 test::ScopedTaskEnvironment scoped_task_environment;
Trent Apted4d207362018-08-15 23:48:15482 scoped_refptr<ObserverListThreadSafe<Foo>> observer_list(
483 new ObserverListThreadSafe<Foo>(ObserverListPolicy::EXISTING_ONLY));
484 Adder a(1);
485 AddInObserve b(observer_list.get());
486 Adder c(1);
487 b.SetToAdd(&c);
488
489 observer_list->AddObserver(&a);
490 observer_list->AddObserver(&b);
491
492 observer_list->Notify(FROM_HERE, &Foo::Observe, 1);
493 RunLoop().RunUntilIdle();
494
495 EXPECT_FALSE(b.to_add_);
496 // B's adder should not have been notified because it was added during
497 // notification.
498 EXPECT_EQ(0, c.total);
499
500 // Notify again to make sure b's adder is notified.
501 observer_list->Notify(FROM_HERE, &Foo::Observe, 1);
502 RunLoop().RunUntilIdle();
503 EXPECT_EQ(1, c.total);
504}
505
506} // namespace base