OLD | NEW |
| (Empty) |
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/compiler_specific.h" | |
6 #include "base/lock.h" | |
7 #include "base/scoped_ptr.h" | |
8 #include "base/threading/platform_thread.h" | |
9 #include "base/threading/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: | |
41 AssertReporter() | |
42 : failed_(false) {} | |
43 | |
44 virtual void warn() { | |
45 failed_ = true; | |
46 } | |
47 | |
48 virtual ~AssertReporter() {} | |
49 | |
50 bool fail_state() const { return failed_; } | |
51 void reset() { failed_ = false; } | |
52 | |
53 private: | |
54 bool failed_; | |
55 }; | |
56 | |
57 } // namespace | |
58 | |
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 | |
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 | |
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 | |
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 | |
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 base::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 | |
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 base::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 | |
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 base::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 } | |
OLD | NEW |