Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(77)

Side by Side Diff: base/observer_list_unittest.cc

Issue 7353: Create a thread-safe observer list. Will be used (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 12 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « base/observer_list_threadsafe.h ('k') | base/task.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. 1 // 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "base/message_loop.h"
5 #include "base/observer_list.h" 6 #include "base/observer_list.h"
7 #include "base/observer_list_threadsafe.h"
8 #include "base/platform_thread.h"
9 #include "base/ref_counted.h"
6 #include "testing/gtest/include/gtest/gtest.h" 10 #include "testing/gtest/include/gtest/gtest.h"
7 11
8 namespace { 12 namespace {
9 13
10 class ObserverListTest : public testing::Test { 14 class ObserverListTest : public testing::Test {
11 }; 15 };
12 16
13 class Foo { 17 class Foo {
14 public: 18 public:
15 virtual void Observe(int x) = 0; 19 virtual void Observe(int x) = 0;
16 virtual ~Foo() {} 20 virtual ~Foo() {}
17 }; 21 };
18 22
19 class Adder : public Foo { 23 class Adder : public Foo {
20 public: 24 public:
21 Adder(int scaler) : total(0), scaler_(scaler) {} 25 explicit Adder(int scaler) : total(0), scaler_(scaler) {}
22 virtual void Observe(int x) { 26 virtual void Observe(int x) {
23 total += x * scaler_; 27 total += x * scaler_;
24 } 28 }
25 virtual ~Adder() { } 29 virtual ~Adder() { }
26 int total; 30 int total;
27 private: 31 private:
28 int scaler_; 32 int scaler_;
29 }; 33 };
30 34
31 class Disrupter : public Foo { 35 class Disrupter : public Foo {
32 public: 36 public:
33 Disrupter(ObserverList<Foo>& list, Foo* doomed) : list_(list), doomed_(doomed) { 37 Disrupter(ObserverList<Foo>* list, Foo* doomed)
34 } 38 : list_(list), doomed_(doomed) { }
35 virtual ~Disrupter() { } 39 virtual ~Disrupter() { }
36 virtual void Observe(int x) { 40 virtual void Observe(int x) {
37 list_.RemoveObserver(doomed_); 41 list_->RemoveObserver(doomed_);
38 } 42 }
39 private: 43 private:
40 ObserverList<Foo>& list_; 44 ObserverList<Foo>* list_;
41 Foo* doomed_; 45 Foo* doomed_;
42 }; 46 };
43 47
48 class ThreadSafeDisrupter : public Foo {
49 public:
50 ThreadSafeDisrupter(ObserverListThreadSafe<Foo>* list, Foo* doomed)
51 : list_(list), doomed_(doomed) { }
52 virtual ~ThreadSafeDisrupter() { }
53 virtual void Observe(int x) {
54 list_->RemoveObserver(doomed_);
55 }
56 private:
57 ObserverListThreadSafe<Foo>* list_;
58 Foo* doomed_;
59 };
60
61 class ObserverListThreadSafeTest : public testing::Test {
62 };
63
64 static const int kThreadRunTime = 10000; // ms to run the multi-threaded test.
65
66 // A thread for use in the ThreadSafeObserver test
67 // which will add and remove itself from the notification
68 // list repeatedly.
69 class AddRemoveThread : public PlatformThread::Delegate,
70 public Foo {
71 public:
72 AddRemoveThread(ObserverListThreadSafe<Foo>* list, bool notify)
73 : list_(list),
74 in_list_(false),
75 start_(Time::Now()),
76 count_observes_(0),
77 count_addtask_(0),
78 do_notifies_(notify) {
79 factory_ = new ScopedRunnableMethodFactory<AddRemoveThread>(this);
80 }
81
82 ~AddRemoveThread() {
83 delete factory_;
84 }
85
86 void ThreadMain() {
87 loop_ = new MessageLoop(); // Fire up a message loop.
88 loop_->PostTask(FROM_HERE,
89 factory_->NewRunnableMethod(&AddRemoveThread::AddTask));
90 loop_->Run();
91 //LOG(ERROR) << "Loop 0x" << std::hex << loop_ << " done. " << count_observe s_ << ", " << count_addtask_;
92 delete loop_;
93 loop_ = reinterpret_cast<MessageLoop*>(0xdeadbeef);
94 }
95
96 // This task just keeps posting to itself in an attempt
97 // to race with the notifier.
98 void AddTask() {
99 count_addtask_++;
100
101 if ((Time::Now() - start_).InMilliseconds() > kThreadRunTime) {
102 LOG(INFO) << "DONE!";
103 return;
104 }
105
106 if (!in_list_) {
107 list_->AddObserver(this);
108 in_list_ = true;
109 }
110
111 if (do_notifies_) {
112 list_->Notify(&Foo::Observe, 10);
113 }
114
115 loop_->PostDelayedTask(FROM_HERE,
116 factory_->NewRunnableMethod(&AddRemoveThread::AddTask), 0);
117 }
118
119 void Quit() {
120 loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask());
121 }
122
123 virtual void Observe(int x) {
124 count_observes_++;
125
126 // If we're getting called after we removed ourselves from
127 // the list, that is very bad!
128 DCHECK(in_list_);
129
130 // This callback should fire on the appropriate thread
131 EXPECT_EQ(loop_, MessageLoop::current());
132
133 list_->RemoveObserver(this);
134 in_list_ = false;
135 }
136
137 private:
138 ObserverListThreadSafe<Foo>* list_;
139 MessageLoop* loop_;
140 bool in_list_; // Are we currently registered for notifications.
141 // in_list_ is only used on |this| thread.
142 Time start_; // The time we started the test.
143
144 int count_observes_; // Number of times we observed.
145 int count_addtask_; // Number of times thread AddTask was called
146 bool do_notifies_; // Whether these threads should do notifications.
147
148 ScopedRunnableMethodFactory<AddRemoveThread>* factory_;
149 };
150
44 } // namespace 151 } // namespace
45 152
46 TEST(ObserverListTest, BasicTest) { 153 TEST(ObserverListTest, BasicTest) {
47 ObserverList<Foo> observer_list; 154 ObserverList<Foo> observer_list;
48 Adder a(1), b(-1), c(1), d(-1); 155 Adder a(1), b(-1), c(1), d(-1);
49 Disrupter evil(observer_list, &c); 156 Disrupter evil(&observer_list, &c);
50 157
51 observer_list.AddObserver(&a); 158 observer_list.AddObserver(&a);
52 observer_list.AddObserver(&b); 159 observer_list.AddObserver(&b);
53 160
54 FOR_EACH_OBSERVER(Foo, observer_list, Observe(10)); 161 FOR_EACH_OBSERVER(Foo, observer_list, Observe(10));
55 162
56 observer_list.AddObserver(&evil); 163 observer_list.AddObserver(&evil);
57 observer_list.AddObserver(&c); 164 observer_list.AddObserver(&c);
58 observer_list.AddObserver(&d); 165 observer_list.AddObserver(&d);
59 166
60 FOR_EACH_OBSERVER(Foo, observer_list, Observe(10)); 167 FOR_EACH_OBSERVER(Foo, observer_list, Observe(10));
61 168
62 EXPECT_EQ(a.total, 20); 169 EXPECT_EQ(a.total, 20);
63 EXPECT_EQ(b.total, -20); 170 EXPECT_EQ(b.total, -20);
64 EXPECT_EQ(c.total, 0); 171 EXPECT_EQ(c.total, 0);
65 EXPECT_EQ(d.total, -10); 172 EXPECT_EQ(d.total, -10);
66 } 173 }
67 174
175 TEST(ObserverListThreadSafeTest, BasicTest) {
176 MessageLoop loop;
177
178 scoped_refptr<ObserverListThreadSafe<Foo> > observer_list(
179 new ObserverListThreadSafe<Foo>);
180 Adder a(1);
181 Adder b(-1);
182 Adder c(1);
183 Adder d(-1);
184 ThreadSafeDisrupter evil(observer_list.get(), &c);
185
186 observer_list->AddObserver(&a);
187 observer_list->AddObserver(&b);
188
189 observer_list->Notify(&Foo::Observe, 10);
190 loop.RunAllPending();
191
192 observer_list->AddObserver(&evil);
193 observer_list->AddObserver(&c);
194 observer_list->AddObserver(&d);
195
196 observer_list->Notify(&Foo::Observe, 10);
197 loop.RunAllPending();
198
199 EXPECT_EQ(a.total, 20);
200 EXPECT_EQ(b.total, -20);
201 EXPECT_EQ(c.total, 0);
202 EXPECT_EQ(d.total, -10);
203 }
204
205
206 // A test driver for a multi-threaded notification loop. Runs a number
207 // of observer threads, each of which constantly adds/removes itself
208 // from the observer list. Optionally, if cross_thread_notifies is set
209 // to true, the observer threads will also trigger notifications to
210 // all observers.
211 static void ThreadSafeObserverHarness(int num_threads,
212 bool cross_thread_notifies) {
213 MessageLoop loop;
214
215 const int kMaxThreads = 15;
216 num_threads = num_threads > kMaxThreads ? kMaxThreads : num_threads;
217
218 scoped_refptr<ObserverListThreadSafe<Foo> > observer_list(
219 new ObserverListThreadSafe<Foo>);
220 Adder a(1);
221 Adder b(-1);
222 Adder c(1);
223 Adder d(-1);
224
225 observer_list->AddObserver(&a);
226 observer_list->AddObserver(&b);
227
228 AddRemoveThread* threaded_observer[kMaxThreads];
229 PlatformThreadHandle threads[kMaxThreads];
230 for (int index = 0; index < num_threads; index++) {
231 threaded_observer[index] = new AddRemoveThread(observer_list.get(), false);
232 EXPECT_TRUE(PlatformThread::Create(0,
233 threaded_observer[index], &threads[index]));
234 }
235
236 Time start = Time::Now();
237 while (true) {
238 if ((Time::Now() - start).InMilliseconds() > kThreadRunTime)
239 break;
240
241 observer_list->Notify(&Foo::Observe, 10);
242
243 loop.RunAllPending();
244 }
245
246 for (int index = 0; index < num_threads; index++) {
247 threaded_observer[index]->Quit();
248 PlatformThread::Join(threads[index]);
249 }
250 }
251
252 TEST(ObserverListThreadSafeTest, CrossThreadObserver) {
253 // Use 7 observer threads. Notifications only come from
254 // the main thread.
255 ThreadSafeObserverHarness(7, false);
256 }
257
258 TEST(ObserverListThreadSafeTest, CrossThreadNotifications) {
259 // Use 3 observer threads. Notifications will fire from
260 // the main thread and all 3 observer threads.
261 ThreadSafeObserverHarness(3, true);
262 }
OLDNEW
« no previous file with comments | « base/observer_list_threadsafe.h ('k') | base/task.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698