OLD | NEW |
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 } |
OLD | NEW |