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