1 // Copyright (c) 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.
4
5 #include "base/compiler_specific.h"
6 #include "base/lock.h"
7 #include "base/platform_thread.h"
8 #include "base/scoped_ptr.h"
9 #include "base/simple_thread.h"
10 #include "base/thread_collision_warner.h"
11 #include "testing/gtest/include/gtest/gtest.h"
12
13 // '' : local class member function does not have a body
14 MSVC_PUSH_DISABLE_WARNING(4822)
15
16
17 #if defined(NDEBUG)
18
19 // Would cause a memory leak otherwise.
20 #undef DFAKE_MUTEX
21 #define DFAKE_MUTEX(obj) scoped_ptr<base::AsserterBase> obj
22
23 // In Release, we expect the AsserterBase::warn() to not happen.
24 #define EXPECT_NDEBUG_FALSE_DEBUG_TRUE EXPECT_FALSE
25
26 #else
27
28 // In Debug, we expect the AsserterBase::warn() to happen.
29 #define EXPECT_NDEBUG_FALSE_DEBUG_TRUE EXPECT_TRUE
30
31 #endif
32
33
34 namespace {
35
36 // This is the asserter used with ThreadCollisionWarner instead of the default
37 // DCheckAsserter. The method fail_state is used to know if a collision took
38 // place.
39 class AssertReporter : public base::AsserterBase {
40 public:
AssertReporter()41 AssertReporter()
42 : failed_(false) {}
43
warn()44 virtual void warn() {
45 failed_ = true;
46 }
47
~AssertReporter()48 virtual ~AssertReporter() {}
49
fail_state() const50 bool fail_state() const { return failed_; }
reset()51 void reset() { failed_ = false; }
52
53 private:
54 bool failed_;
55 };
56
57 } // namespace
58
TEST(ThreadCollisionTest,BookCriticalSection)59 TEST(ThreadCollisionTest, BookCriticalSection) {
60 AssertReporter* local_reporter = new AssertReporter();
61
62 base::ThreadCollisionWarner warner(local_reporter);
63 EXPECT_FALSE(local_reporter->fail_state());
64
65 { // Pin section.
66 DFAKE_SCOPED_LOCK_THREAD_LOCKED(warner);
67 EXPECT_FALSE(local_reporter->fail_state());
68 { // Pin section.
69 DFAKE_SCOPED_LOCK_THREAD_LOCKED(warner);
70 EXPECT_FALSE(local_reporter->fail_state());
71 }
72 }
73 }
74
TEST(ThreadCollisionTest,ScopedRecursiveBookCriticalSection)75 TEST(ThreadCollisionTest, ScopedRecursiveBookCriticalSection) {
76 AssertReporter* local_reporter = new AssertReporter();
77
78 base::ThreadCollisionWarner warner(local_reporter);
79 EXPECT_FALSE(local_reporter->fail_state());
80
81 { // Pin section.
82 DFAKE_SCOPED_RECURSIVE_LOCK(warner);
83 EXPECT_FALSE(local_reporter->fail_state());
84 { // Pin section again (allowed by DFAKE_SCOPED_RECURSIVE_LOCK)
85 DFAKE_SCOPED_RECURSIVE_LOCK(warner);
86 EXPECT_FALSE(local_reporter->fail_state());
87 } // Unpin section.
88 } // Unpin section.
89
90 // Check that section is not pinned
91 { // Pin section.
92 DFAKE_SCOPED_LOCK(warner);
93 EXPECT_FALSE(local_reporter->fail_state());
94 } // Unpin section.
95 }
96
TEST(ThreadCollisionTest,ScopedBookCriticalSection)97 TEST(ThreadCollisionTest, ScopedBookCriticalSection) {
98 AssertReporter* local_reporter = new AssertReporter();
99
100 base::ThreadCollisionWarner warner(local_reporter);
101 EXPECT_FALSE(local_reporter->fail_state());
102
103 { // Pin section.
104 DFAKE_SCOPED_LOCK(warner);
105 EXPECT_FALSE(local_reporter->fail_state());
106 } // Unpin section.
107
108 { // Pin section.
109 DFAKE_SCOPED_LOCK(warner);
110 EXPECT_FALSE(local_reporter->fail_state());
111 {
112 // Pin section again (not allowed by DFAKE_SCOPED_LOCK)
113 DFAKE_SCOPED_LOCK(warner);
114 EXPECT_NDEBUG_FALSE_DEBUG_TRUE(local_reporter->fail_state());
115 // Reset the status of warner for further tests.
116 local_reporter->reset();
117 } // Unpin section.
118 } // Unpin section.
119
120 {
121 // Pin section.
122 DFAKE_SCOPED_LOCK(warner);
123 EXPECT_FALSE(local_reporter->fail_state());
124 } // Unpin section.
125 }
126
TEST(ThreadCollisionTest,MTBookCriticalSectionTest)127 TEST(ThreadCollisionTest, MTBookCriticalSectionTest) {
128 class NonThreadSafeQueue {
129 public:
130 explicit NonThreadSafeQueue(base::AsserterBase* asserter)
131 : push_pop_(asserter) {
132 }
133
134 void push(int value) {
135 DFAKE_SCOPED_LOCK_THREAD_LOCKED(push_pop_);
136 }
137
138 int pop() {
139 DFAKE_SCOPED_LOCK_THREAD_LOCKED(push_pop_);
140 return 0;
141 }
142
143 private:
144 DFAKE_MUTEX(push_pop_);
145
146 DISALLOW_COPY_AND_ASSIGN(NonThreadSafeQueue);
147 };
148
149 class QueueUser : public base::DelegateSimpleThread::Delegate {
150 public:
151 explicit QueueUser(NonThreadSafeQueue& queue)
152 : queue_(queue) {}
153
154 virtual void Run() {
155 queue_.push(0);
156 queue_.pop();
157 }
158
159 private:
160 NonThreadSafeQueue& queue_;
161 };
162
163 AssertReporter* local_reporter = new AssertReporter();
164
165 NonThreadSafeQueue queue(local_reporter);
166
167 QueueUser queue_user_a(queue);
168 QueueUser queue_user_b(queue);
169
170 base::DelegateSimpleThread thread_a(&queue_user_a, "queue_user_thread_a");
171 base::DelegateSimpleThread thread_b(&queue_user_b, "queue_user_thread_b");
172
173 thread_a.Start();
174 thread_b.Start();
175
176 thread_a.Join();
177 thread_b.Join();
178
179 EXPECT_NDEBUG_FALSE_DEBUG_TRUE(local_reporter->fail_state());
180 }
181
TEST(ThreadCollisionTest,MTScopedBookCriticalSectionTest)182 TEST(ThreadCollisionTest, MTScopedBookCriticalSectionTest) {
183 // Queue with a 5 seconds push execution time, hopefuly the two used threads
184 // in the test will enter the push at same time.
185 class NonThreadSafeQueue {
186 public:
187 explicit NonThreadSafeQueue(base::AsserterBase* asserter)
188 : push_pop_(asserter) {
189 }
190
191 void push(int value) {
192 DFAKE_SCOPED_LOCK(push_pop_);
193 PlatformThread::Sleep(5000);
194 }
195
196 int pop() {
197 DFAKE_SCOPED_LOCK(push_pop_);
198 return 0;
199 }
200
201 private:
202 DFAKE_MUTEX(push_pop_);
203
204 DISALLOW_COPY_AND_ASSIGN(NonThreadSafeQueue);
205 };
206
207 class QueueUser : public base::DelegateSimpleThread::Delegate {
208 public:
209 explicit QueueUser(NonThreadSafeQueue& queue)
210 : queue_(queue) {}
211
212 virtual void Run() {
213 queue_.push(0);
214 queue_.pop();
215 }
216
217 private:
218 NonThreadSafeQueue& queue_;
219 };
220
221 AssertReporter* local_reporter = new AssertReporter();
222
223 NonThreadSafeQueue queue(local_reporter);
224
225 QueueUser queue_user_a(queue);
226 QueueUser queue_user_b(queue);
227
228 base::DelegateSimpleThread thread_a(&queue_user_a, "queue_user_thread_a");
229 base::DelegateSimpleThread thread_b(&queue_user_b, "queue_user_thread_b");
230
231 thread_a.Start();
232 thread_b.Start();
233
234 thread_a.Join();
235 thread_b.Join();
236
237 EXPECT_NDEBUG_FALSE_DEBUG_TRUE(local_reporter->fail_state());
238 }
239
TEST(ThreadCollisionTest,MTSynchedScopedBookCriticalSectionTest)240 TEST(ThreadCollisionTest, MTSynchedScopedBookCriticalSectionTest) {
241 // Queue with a 2 seconds push execution time, hopefuly the two used threads
242 // in the test will enter the push at same time.
243 class NonThreadSafeQueue {
244 public:
245 explicit NonThreadSafeQueue(base::AsserterBase* asserter)
246 : push_pop_(asserter) {
247 }
248
249 void push(int value) {
250 DFAKE_SCOPED_LOCK(push_pop_);
251 PlatformThread::Sleep(2000);
252 }
253
254 int pop() {
255 DFAKE_SCOPED_LOCK(push_pop_);
256 return 0;
257 }
258
259 private:
260 DFAKE_MUTEX(push_pop_);
261
262 DISALLOW_COPY_AND_ASSIGN(NonThreadSafeQueue);
263 };
264
265 // This time the QueueUser class protects the non thread safe queue with
266 // a lock.
267 class QueueUser : public base::DelegateSimpleThread::Delegate {
268 public:
269 QueueUser(NonThreadSafeQueue& queue, Lock& lock)
270 : queue_(queue),
271 lock_(lock) {}
272
273 virtual void Run() {
274 {
275 AutoLock auto_lock(lock_);
276 queue_.push(0);
277 }
278 {
279 AutoLock auto_lock(lock_);
280 queue_.pop();
281 }
282 }
283 private:
284 NonThreadSafeQueue& queue_;
285 Lock& lock_;
286 };
287
288 AssertReporter* local_reporter = new AssertReporter();
289
290 NonThreadSafeQueue queue(local_reporter);
291
292 Lock lock;
293
294 QueueUser queue_user_a(queue, lock);
295 QueueUser queue_user_b(queue, lock);
296
297 base::DelegateSimpleThread thread_a(&queue_user_a, "queue_user_thread_a");
298 base::DelegateSimpleThread thread_b(&queue_user_b, "queue_user_thread_b");
299
300 thread_a.Start();
301 thread_b.Start();
302
303 thread_a.Join();
304 thread_b.Join();
305
306 EXPECT_FALSE(local_reporter->fail_state());
307 }
308
TEST(ThreadCollisionTest,MTSynchedScopedRecursiveBookCriticalSectionTest)309 TEST(ThreadCollisionTest, MTSynchedScopedRecursiveBookCriticalSectionTest) {
310 // Queue with a 2 seconds push execution time, hopefuly the two used threads
311 // in the test will enter the push at same time.
312 class NonThreadSafeQueue {
313 public:
314 explicit NonThreadSafeQueue(base::AsserterBase* asserter)
315 : push_pop_(asserter) {
316 }
317
318 void push(int) {
319 DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_);
320 bar();
321 PlatformThread::Sleep(2000);
322 }
323
324 int pop() {
325 DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_);
326 return 0;
327 }
328
329 void bar() {
330 DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_);
331 }
332
333 private:
334 DFAKE_MUTEX(push_pop_);
335
336 DISALLOW_COPY_AND_ASSIGN(NonThreadSafeQueue);
337 };
338
339 // This time the QueueUser class protects the non thread safe queue with
340 // a lock.
341 class QueueUser : public base::DelegateSimpleThread::Delegate {
342 public:
343 QueueUser(NonThreadSafeQueue& queue, Lock& lock)
344 : queue_(queue),
345 lock_(lock) {}
346
347 virtual void Run() {
348 {
349 AutoLock auto_lock(lock_);
350 queue_.push(0);
351 }
352 {
353 AutoLock auto_lock(lock_);
354 queue_.bar();
355 }
356 {
357 AutoLock auto_lock(lock_);
358 queue_.pop();
359 }
360 }
361 private:
362 NonThreadSafeQueue& queue_;
363 Lock& lock_;
364 };
365
366 AssertReporter* local_reporter = new AssertReporter();
367
368 NonThreadSafeQueue queue(local_reporter);
369
370 Lock lock;
371
372 QueueUser queue_user_a(queue, lock);
373 QueueUser queue_user_b(queue, lock);
374
375 base::DelegateSimpleThread thread_a(&queue_user_a, "queue_user_thread_a");
376 base::DelegateSimpleThread thread_b(&queue_user_b, "queue_user_thread_b");
377
378 thread_a.Start();
379 thread_b.Start();
380
381 thread_a.Join();
382 thread_b.Join();
383
384 EXPECT_FALSE(local_reporter->fail_state());
385 }
386