blob: 941356190abc818cde6b2321f4914b26a323534c [file] [log] [blame]
license.botbf09a502008-08-24 00:55:551// Copyright (c) 2006-2008 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.
initial.commitd7cae122008-07-26 21:49:384
[email protected]503631c2008-10-22 23:09:215#include "base/message_loop.h"
initial.commitd7cae122008-07-26 21:49:386#include "base/observer_list.h"
[email protected]503631c2008-10-22 23:09:217#include "base/observer_list_threadsafe.h"
8#include "base/platform_thread.h"
9#include "base/ref_counted.h"
initial.commitd7cae122008-07-26 21:49:3810#include "testing/gtest/include/gtest/gtest.h"
11
[email protected]e1acf6f2008-10-27 20:43:3312using base::Time;
13
initial.commitd7cae122008-07-26 21:49:3814namespace {
15
16class ObserverListTest : public testing::Test {
17};
18
19class Foo {
20 public:
21 virtual void Observe(int x) = 0;
[email protected]bf92cfbe2008-08-08 16:37:4322 virtual ~Foo() {}
initial.commitd7cae122008-07-26 21:49:3823};
24
25class Adder : public Foo {
26 public:
[email protected]503631c2008-10-22 23:09:2127 explicit Adder(int scaler) : total(0), scaler_(scaler) {}
initial.commitd7cae122008-07-26 21:49:3828 virtual void Observe(int x) {
29 total += x * scaler_;
30 }
[email protected]bf92cfbe2008-08-08 16:37:4331 virtual ~Adder() { }
initial.commitd7cae122008-07-26 21:49:3832 int total;
33 private:
34 int scaler_;
35};
36
37class Disrupter : public Foo {
38 public:
[email protected]503631c2008-10-22 23:09:2139 Disrupter(ObserverList<Foo>* list, Foo* doomed)
40 : list_(list), doomed_(doomed) { }
[email protected]bf92cfbe2008-08-08 16:37:4341 virtual ~Disrupter() { }
initial.commitd7cae122008-07-26 21:49:3842 virtual void Observe(int x) {
[email protected]503631c2008-10-22 23:09:2143 list_->RemoveObserver(doomed_);
initial.commitd7cae122008-07-26 21:49:3844 }
45 private:
[email protected]503631c2008-10-22 23:09:2146 ObserverList<Foo>* list_;
initial.commitd7cae122008-07-26 21:49:3847 Foo* doomed_;
48};
49
[email protected]503631c2008-10-22 23:09:2150class ThreadSafeDisrupter : public Foo {
51 public:
52 ThreadSafeDisrupter(ObserverListThreadSafe<Foo>* list, Foo* doomed)
53 : list_(list), doomed_(doomed) { }
54 virtual ~ThreadSafeDisrupter() { }
55 virtual void Observe(int x) {
56 list_->RemoveObserver(doomed_);
57 }
58 private:
59 ObserverListThreadSafe<Foo>* list_;
60 Foo* doomed_;
61};
62
[email protected]b3e2fad02008-10-31 03:32:0663class AddInObserve : public Foo {
64 public:
65 AddInObserve(ObserverList<Foo>* observer_list)
66 : added(false),
67 observer_list(observer_list),
68 adder(1) {
69 }
70 virtual void Observe(int x) {
71 if (!added) {
72 added = true;
73 observer_list->AddObserver(&adder);
74 }
75 }
76
77 bool added;
78 ObserverList<Foo>* observer_list;
79 Adder adder;
80};
81
82
[email protected]503631c2008-10-22 23:09:2183class ObserverListThreadSafeTest : public testing::Test {
84};
85
86static const int kThreadRunTime = 10000; // ms to run the multi-threaded test.
87
88// A thread for use in the ThreadSafeObserver test
89// which will add and remove itself from the notification
90// list repeatedly.
91class AddRemoveThread : public PlatformThread::Delegate,
92 public Foo {
93 public:
94 AddRemoveThread(ObserverListThreadSafe<Foo>* list, bool notify)
95 : list_(list),
96 in_list_(false),
97 start_(Time::Now()),
98 count_observes_(0),
99 count_addtask_(0),
100 do_notifies_(notify) {
101 factory_ = new ScopedRunnableMethodFactory<AddRemoveThread>(this);
102 }
103
[email protected]bd60d732009-01-05 19:40:53104 virtual ~AddRemoveThread() {
[email protected]503631c2008-10-22 23:09:21105 delete factory_;
106 }
107
108 void ThreadMain() {
109 loop_ = new MessageLoop(); // Fire up a message loop.
110 loop_->PostTask(FROM_HERE,
111 factory_->NewRunnableMethod(&AddRemoveThread::AddTask));
112 loop_->Run();
113 //LOG(ERROR) << "Loop 0x" << std::hex << loop_ << " done. " << count_observes_ << ", " << count_addtask_;
114 delete loop_;
115 loop_ = reinterpret_cast<MessageLoop*>(0xdeadbeef);
[email protected]bd60d732009-01-05 19:40:53116 delete this;
[email protected]503631c2008-10-22 23:09:21117 }
118
119 // This task just keeps posting to itself in an attempt
120 // to race with the notifier.
121 void AddTask() {
122 count_addtask_++;
123
124 if ((Time::Now() - start_).InMilliseconds() > kThreadRunTime) {
125 LOG(INFO) << "DONE!";
126 return;
127 }
128
129 if (!in_list_) {
130 list_->AddObserver(this);
131 in_list_ = true;
132 }
133
134 if (do_notifies_) {
135 list_->Notify(&Foo::Observe, 10);
136 }
137
138 loop_->PostDelayedTask(FROM_HERE,
139 factory_->NewRunnableMethod(&AddRemoveThread::AddTask), 0);
140 }
141
142 void Quit() {
143 loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask());
144 }
145
146 virtual void Observe(int x) {
147 count_observes_++;
148
149 // If we're getting called after we removed ourselves from
150 // the list, that is very bad!
151 DCHECK(in_list_);
152
153 // This callback should fire on the appropriate thread
154 EXPECT_EQ(loop_, MessageLoop::current());
155
156 list_->RemoveObserver(this);
157 in_list_ = false;
158 }
159
160 private:
161 ObserverListThreadSafe<Foo>* list_;
162 MessageLoop* loop_;
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 int count_observes_; // Number of times we observed.
168 int count_addtask_; // Number of times thread AddTask was called
169 bool do_notifies_; // Whether these threads should do notifications.
170
171 ScopedRunnableMethodFactory<AddRemoveThread>* factory_;
172};
173
initial.commitd7cae122008-07-26 21:49:38174} // namespace
175
176TEST(ObserverListTest, BasicTest) {
177 ObserverList<Foo> observer_list;
178 Adder a(1), b(-1), c(1), d(-1);
[email protected]503631c2008-10-22 23:09:21179 Disrupter evil(&observer_list, &c);
initial.commitd7cae122008-07-26 21:49:38180
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}
license.botbf09a502008-08-24 00:55:55197
[email protected]503631c2008-10-22 23:09:21198TEST(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
228
229// A test driver for a multi-threaded notification loop. Runs a number
230// of observer threads, each of which constantly adds/removes itself
231// from the observer list. Optionally, if cross_thread_notifies is set
232// to true, the observer threads will also trigger notifications to
233// all observers.
234static void ThreadSafeObserverHarness(int num_threads,
235 bool cross_thread_notifies) {
236 MessageLoop loop;
237
238 const int kMaxThreads = 15;
239 num_threads = num_threads > kMaxThreads ? kMaxThreads : num_threads;
240
241 scoped_refptr<ObserverListThreadSafe<Foo> > observer_list(
242 new ObserverListThreadSafe<Foo>);
243 Adder a(1);
244 Adder b(-1);
245 Adder c(1);
246 Adder d(-1);
247
248 observer_list->AddObserver(&a);
249 observer_list->AddObserver(&b);
250
251 AddRemoveThread* threaded_observer[kMaxThreads];
252 PlatformThreadHandle threads[kMaxThreads];
253 for (int index = 0; index < num_threads; index++) {
254 threaded_observer[index] = new AddRemoveThread(observer_list.get(), false);
255 EXPECT_TRUE(PlatformThread::Create(0,
256 threaded_observer[index], &threads[index]));
257 }
258
259 Time start = Time::Now();
260 while (true) {
261 if ((Time::Now() - start).InMilliseconds() > kThreadRunTime)
262 break;
263
264 observer_list->Notify(&Foo::Observe, 10);
265
266 loop.RunAllPending();
267 }
268
269 for (int index = 0; index < num_threads; index++) {
270 threaded_observer[index]->Quit();
271 PlatformThread::Join(threads[index]);
272 }
273}
274
275TEST(ObserverListThreadSafeTest, CrossThreadObserver) {
276 // Use 7 observer threads. Notifications only come from
277 // the main thread.
278 ThreadSafeObserverHarness(7, false);
279}
280
281TEST(ObserverListThreadSafeTest, CrossThreadNotifications) {
282 // Use 3 observer threads. Notifications will fire from
283 // the main thread and all 3 observer threads.
284 ThreadSafeObserverHarness(3, true);
285}
[email protected]b3e2fad02008-10-31 03:32:06286
287TEST(ObserverListTest, Existing) {
288 ObserverList<Foo> observer_list(ObserverList<Foo>::NOTIFY_EXISTING_ONLY);
289 Adder a(1);
290 AddInObserve b(&observer_list);
291
292 observer_list.AddObserver(&a);
293 observer_list.AddObserver(&b);
294
295 FOR_EACH_OBSERVER(Foo, observer_list, Observe(1));
296
297 EXPECT_TRUE(b.added);
298 // B's adder should not have been notified because it was added during
299 // notificaiton.
300 EXPECT_EQ(0, b.adder.total);
301
302 // Notify again to make sure b's adder is notified.
303 FOR_EACH_OBSERVER(Foo, observer_list, Observe(1));
304 EXPECT_EQ(1, b.adder.total);
305}