• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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