observer_list_unittest.cc revision 3f50c38dc070f4bb515c1b64450dae14f316474e
1// Copyright (c) 2010 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.h"
6#include "base/observer_list_threadsafe.h"
7
8#include <vector>
9
10#include "base/message_loop.h"
11#include "base/ref_counted.h"
12#include "base/threading/platform_thread.h"
13#include "testing/gtest/include/gtest/gtest.h"
14
15using base::PlatformThread;
16using base::Time;
17
18namespace {
19
20class Foo {
21 public:
22  virtual void Observe(int x) = 0;
23  virtual ~Foo() {}
24};
25
26class Adder : public Foo {
27 public:
28  explicit Adder(int scaler) : total(0), scaler_(scaler) {}
29  virtual void Observe(int x) {
30    total += x * scaler_;
31  }
32  virtual ~Adder() { }
33  int total;
34 private:
35  int scaler_;
36};
37
38class Disrupter : public Foo {
39 public:
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_);
45  }
46 private:
47  ObserverList<Foo>* list_;
48  Foo* doomed_;
49};
50
51class ThreadSafeDisrupter : public Foo {
52 public:
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_);
58  }
59 private:
60  ObserverListThreadSafe<Foo>* list_;
61  Foo* doomed_;
62};
63
64class AddInObserve : public Foo {
65 public:
66  explicit AddInObserve(ObserverList<Foo>* observer_list)
67      : added(false),
68        observer_list(observer_list),
69        adder(1) {
70  }
71  virtual void Observe(int x) {
72    if (!added) {
73      added = true;
74      observer_list->AddObserver(&adder);
75    }
76  }
77
78  bool added;
79  ObserverList<Foo>* observer_list;
80  Adder adder;
81};
82
83
84class ObserverListThreadSafeTest : public testing::Test {
85};
86
87static const int kThreadRunTime = 2000;  // ms to run the multi-threaded test.
88
89// A thread for use in the ThreadSafeObserver test
90// which will add and remove itself from the notification
91// list repeatedly.
92class AddRemoveThread : public PlatformThread::Delegate,
93                        public Foo {
94 public:
95  AddRemoveThread(ObserverListThreadSafe<Foo>* list, bool notify)
96      : list_(list),
97        in_list_(false),
98        start_(Time::Now()),
99        count_observes_(0),
100        count_addtask_(0),
101        do_notifies_(notify) {
102    factory_ = new ScopedRunnableMethodFactory<AddRemoveThread>(this);
103  }
104
105  virtual ~AddRemoveThread() {
106    delete factory_;
107  }
108
109  void ThreadMain() {
110    loop_ = new MessageLoop();  // Fire up a message loop.
111    loop_->PostTask(
112        FROM_HERE, factory_->NewRunnableMethod(&AddRemoveThread::AddTask));
113    loop_->Run();
114    //LOG(ERROR) << "Loop 0x" << std::hex << loop_ << " done. " <<
115    //    count_observes_ << ", " << count_addtask_;
116    delete loop_;
117    loop_ = reinterpret_cast<MessageLoop*>(0xdeadbeef);
118    delete this;
119  }
120
121  // This task just keeps posting to itself in an attempt
122  // to race with the notifier.
123  void AddTask() {
124    count_addtask_++;
125
126    if ((Time::Now() - start_).InMilliseconds() > kThreadRunTime) {
127      VLOG(1) << "DONE!";
128      return;
129    }
130
131    if (!in_list_) {
132      list_->AddObserver(this);
133      in_list_ = true;
134    }
135
136    if (do_notifies_) {
137      list_->Notify(&Foo::Observe, 10);
138    }
139
140    loop_->PostDelayedTask(FROM_HERE,
141      factory_->NewRunnableMethod(&AddRemoveThread::AddTask), 0);
142  }
143
144  void Quit() {
145    loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask());
146  }
147
148  virtual void Observe(int x) {
149    count_observes_++;
150
151    // If we're getting called after we removed ourselves from
152    // the list, that is very bad!
153    DCHECK(in_list_);
154
155    // This callback should fire on the appropriate thread
156    EXPECT_EQ(loop_, MessageLoop::current());
157
158    list_->RemoveObserver(this);
159    in_list_ = false;
160  }
161
162 private:
163  ObserverListThreadSafe<Foo>* list_;
164  MessageLoop* loop_;
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.
168
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.
172
173  ScopedRunnableMethodFactory<AddRemoveThread>* factory_;
174};
175
176TEST(ObserverListTest, BasicTest) {
177  ObserverList<Foo> observer_list;
178  Adder a(1), b(-1), c(1), d(-1);
179  Disrupter evil(&observer_list, &c);
180
181  observer_list.AddObserver(&a);
182  observer_list.AddObserver(&b);
183
184  FOR_EACH_OBSERVER(Foo, observer_list, Observe(10));
185
186  observer_list.AddObserver(&evil);
187  observer_list.AddObserver(&c);
188  observer_list.AddObserver(&d);
189
190  FOR_EACH_OBSERVER(Foo, observer_list, Observe(10));
191
192  EXPECT_EQ(a.total, 20);
193  EXPECT_EQ(b.total, -20);
194  EXPECT_EQ(c.total, 0);
195  EXPECT_EQ(d.total, -10);
196}
197
198TEST(ObserverListThreadSafeTest, BasicTest) {
199  MessageLoop loop;
200
201  scoped_refptr<ObserverListThreadSafe<Foo> > observer_list(
202      new ObserverListThreadSafe<Foo>);
203  Adder a(1);
204  Adder b(-1);
205  Adder c(1);
206  Adder d(-1);
207  ThreadSafeDisrupter evil(observer_list.get(), &c);
208
209  observer_list->AddObserver(&a);
210  observer_list->AddObserver(&b);
211
212  observer_list->Notify(&Foo::Observe, 10);
213  loop.RunAllPending();
214
215  observer_list->AddObserver(&evil);
216  observer_list->AddObserver(&c);
217  observer_list->AddObserver(&d);
218
219  observer_list->Notify(&Foo::Observe, 10);
220  loop.RunAllPending();
221
222  EXPECT_EQ(a.total, 20);
223  EXPECT_EQ(b.total, -20);
224  EXPECT_EQ(c.total, 0);
225  EXPECT_EQ(d.total, -10);
226}
227
228class FooRemover : public Foo {
229 public:
230  explicit FooRemover(ObserverListThreadSafe<Foo>* list) : list_(list) {}
231  virtual ~FooRemover() {}
232
233  void AddFooToRemove(Foo* foo) {
234    foos_.push_back(foo);
235  }
236
237  virtual void Observe(int x) {
238    std::vector<Foo*> tmp;
239    tmp.swap(foos_);
240    for (std::vector<Foo*>::iterator it = tmp.begin();
241         it != tmp.end(); ++it) {
242      list_->RemoveObserver(*it);
243    }
244  }
245
246 private:
247  const scoped_refptr<ObserverListThreadSafe<Foo> > list_;
248  std::vector<Foo*> foos_;
249};
250
251TEST(ObserverListThreadSafeTest, RemoveMultipleObservers) {
252  MessageLoop loop;
253  scoped_refptr<ObserverListThreadSafe<Foo> > observer_list(
254      new ObserverListThreadSafe<Foo>);
255
256  FooRemover a(observer_list);
257  Adder b(1);
258
259  observer_list->AddObserver(&a);
260  observer_list->AddObserver(&b);
261
262  a.AddFooToRemove(&a);
263  a.AddFooToRemove(&b);
264
265  observer_list->Notify(&Foo::Observe, 1);
266  loop.RunAllPending();
267}
268
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
273// all observers.
274static void ThreadSafeObserverHarness(int num_threads,
275                                      bool cross_thread_notifies) {
276  MessageLoop loop;
277
278  const int kMaxThreads = 15;
279  num_threads = num_threads > kMaxThreads ? kMaxThreads : num_threads;
280
281  scoped_refptr<ObserverListThreadSafe<Foo> > observer_list(
282      new ObserverListThreadSafe<Foo>);
283  Adder a(1);
284  Adder b(-1);
285  Adder c(1);
286  Adder d(-1);
287
288  observer_list->AddObserver(&a);
289  observer_list->AddObserver(&b);
290
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]));
297  }
298
299  Time start = Time::Now();
300  while (true) {
301    if ((Time::Now() - start).InMilliseconds() > kThreadRunTime)
302      break;
303
304    observer_list->Notify(&Foo::Observe, 10);
305
306    loop.RunAllPending();
307  }
308
309  for (int index = 0; index < num_threads; index++) {
310    threaded_observer[index]->Quit();
311    PlatformThread::Join(threads[index]);
312  }
313}
314
315TEST(ObserverListThreadSafeTest, CrossThreadObserver) {
316  // Use 7 observer threads.  Notifications only come from
317  // the main thread.
318  ThreadSafeObserverHarness(7, false);
319}
320
321TEST(ObserverListThreadSafeTest, CrossThreadNotifications) {
322  // Use 3 observer threads.  Notifications will fire from
323  // the main thread and all 3 observer threads.
324  ThreadSafeObserverHarness(3, true);
325}
326
327TEST(ObserverListTest, Existing) {
328  ObserverList<Foo> observer_list(ObserverList<Foo>::NOTIFY_EXISTING_ONLY);
329  Adder a(1);
330  AddInObserve b(&observer_list);
331
332  observer_list.AddObserver(&a);
333  observer_list.AddObserver(&b);
334
335  FOR_EACH_OBSERVER(Foo, observer_list, Observe(1));
336
337  EXPECT_TRUE(b.added);
338  // B's adder should not have been notified because it was added during
339  // notificaiton.
340  EXPECT_EQ(0, b.adder.total);
341
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);
345}
346
347class AddInClearObserve : public Foo {
348 public:
349  explicit AddInClearObserve(ObserverList<Foo>* list)
350      : list_(list), added_(false), adder_(1) {}
351
352  virtual void Observe(int /* x */) {
353    list_->Clear();
354    list_->AddObserver(&adder_);
355    added_ = true;
356  }
357
358  bool added() const { return added_; }
359  const Adder& adder() const { return adder_; }
360
361 private:
362  ObserverList<Foo>* const list_;
363
364  bool added_;
365  Adder adder_;
366};
367
368TEST(ObserverListTest, ClearNotifyAll) {
369  ObserverList<Foo> observer_list;
370  AddInClearObserve a(&observer_list);
371
372  observer_list.AddObserver(&a);
373
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.";
378}
379
380TEST(ObserverListTest, ClearNotifyExistingOnly) {
381  ObserverList<Foo> observer_list(ObserverList<Foo>::NOTIFY_EXISTING_ONLY);
382  AddInClearObserve a(&observer_list);
383
384  observer_list.AddObserver(&a);
385
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.";
390}
391
392}  // namespace
393