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